mirror of
https://github.com/Alexander-D-Karpov/akarpov
synced 2024-11-22 07:26:33 +03:00
minor blog updates and fixes, added better cache
This commit is contained in:
parent
13638466a2
commit
f0dd4e2b27
18
akarpov/blog/migrations/0008_tag_seo_tags.py
Normal file
18
akarpov/blog/migrations/0008_tag_seo_tags.py
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
# Generated by Django 4.2.3 on 2023-08-03 12:10
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
("blog", "0007_alter_comment_options_alter_commentrating_options_and_more"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="tag",
|
||||||
|
name="seo_tags",
|
||||||
|
field=models.TextField(blank=True, null=True),
|
||||||
|
),
|
||||||
|
]
|
|
@ -10,6 +10,7 @@
|
||||||
from akarpov.tools.shortener.models import ShortLinkModel
|
from akarpov.tools.shortener.models import ShortLinkModel
|
||||||
from akarpov.users.models import User
|
from akarpov.users.models import User
|
||||||
from akarpov.users.services.history import UserHistoryModel
|
from akarpov.users.services.history import UserHistoryModel
|
||||||
|
from akarpov.utils.cache import cache_model_property
|
||||||
from akarpov.utils.string import cleanhtml
|
from akarpov.utils.string import cleanhtml
|
||||||
|
|
||||||
|
|
||||||
|
@ -41,7 +42,6 @@ def get_comments(self):
|
||||||
return self.comments.all()
|
return self.comments.all()
|
||||||
|
|
||||||
def h_tags(self):
|
def h_tags(self):
|
||||||
# TODO: add caching here
|
|
||||||
tags = (
|
tags = (
|
||||||
Tag.objects.filter(posts__id=self.id)
|
Tag.objects.filter(posts__id=self.id)
|
||||||
.annotate(num_posts=Count("posts"))
|
.annotate(num_posts=Count("posts"))
|
||||||
|
@ -49,18 +49,20 @@ def h_tags(self):
|
||||||
)
|
)
|
||||||
return tags
|
return tags
|
||||||
|
|
||||||
def h_tag(self):
|
def _h_tag(self):
|
||||||
return self.h_tags().first()
|
return self.h_tags().first()
|
||||||
|
|
||||||
|
def h_tag(self):
|
||||||
|
return cache_model_property(self, "_h_tag")
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def text(self):
|
def text(self):
|
||||||
# TODO: add caching here
|
|
||||||
return cleanhtml(self.body)
|
return cleanhtml(self.body)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@extend_schema_field(serializers.CharField)
|
@extend_schema_field(serializers.CharField)
|
||||||
def summary(self):
|
def summary(self):
|
||||||
body = self.text
|
body = cache_model_property(self, "text")
|
||||||
return body[:100] + "..." if len(body) > 100 else ""
|
return body[:100] + "..." if len(body) > 100 else ""
|
||||||
|
|
||||||
def get_absolute_url(self):
|
def get_absolute_url(self):
|
||||||
|
@ -77,6 +79,7 @@ class SlugMeta:
|
||||||
class Tag(UserHistoryModel):
|
class Tag(UserHistoryModel):
|
||||||
name = models.CharField(max_length=20, unique=True)
|
name = models.CharField(max_length=20, unique=True)
|
||||||
color = ColorField(blank=True, default="#FF0000")
|
color = ColorField(blank=True, default="#FF0000")
|
||||||
|
seo_tags = models.TextField(blank=True, null=True)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.name
|
return self.name
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
from django.db.models.signals import pre_delete, pre_save
|
from django.db.models.signals import pre_delete, pre_save
|
||||||
from django.dispatch import receiver
|
from django.dispatch import receiver
|
||||||
|
|
||||||
from akarpov.blog.models import PostRating, Tag
|
from akarpov.blog.models import Post, PostRating, Tag
|
||||||
|
from akarpov.utils.cache import clear_model_cache
|
||||||
from akarpov.utils.generators import generate_hex_color
|
from akarpov.utils.generators import generate_hex_color
|
||||||
|
|
||||||
|
|
||||||
|
@ -38,6 +39,16 @@ def post_rating(sender, instance: PostRating, **kwargs):
|
||||||
post.save()
|
post.save()
|
||||||
|
|
||||||
|
|
||||||
|
@receiver(pre_save, sender=Post)
|
||||||
|
def post_update(sender, instance: Post, **kwargs):
|
||||||
|
if instance.id:
|
||||||
|
if "update_fields" in kwargs:
|
||||||
|
for field in kwargs["update_fields"]:
|
||||||
|
clear_model_cache(instance, field)
|
||||||
|
else:
|
||||||
|
clear_model_cache(instance)
|
||||||
|
|
||||||
|
|
||||||
@receiver(pre_delete, sender=PostRating)
|
@receiver(pre_delete, sender=PostRating)
|
||||||
def post_rating_delete(sender, instance: PostRating, **kwargs):
|
def post_rating_delete(sender, instance: PostRating, **kwargs):
|
||||||
post = instance.post
|
post = instance.post
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
from django.core.exceptions import PermissionDenied, ValidationError
|
from django.core.exceptions import PermissionDenied, ValidationError
|
||||||
from django.http import HttpResponseRedirect
|
from django.http import HttpResponseRedirect
|
||||||
from django.shortcuts import get_object_or_404
|
from django.shortcuts import get_object_or_404
|
||||||
|
from django.urls import reverse
|
||||||
from django.views.decorators.csrf import csrf_exempt
|
from django.views.decorators.csrf import csrf_exempt
|
||||||
from django.views.generic import CreateView, DetailView, ListView, UpdateView
|
from django.views.generic import CreateView, DetailView, ListView, UpdateView
|
||||||
|
|
||||||
|
@ -19,17 +20,19 @@ class PostDetailView(DetailView):
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
context = super().get_context_data(**kwargs)
|
context = super().get_context_data(**kwargs)
|
||||||
|
|
||||||
# that's kind of trash code, but CBS forced me to do so
|
|
||||||
post = self.get_object()
|
|
||||||
post.post_views += 1
|
|
||||||
post.save(update_fields=["post_views"])
|
|
||||||
|
|
||||||
if self.request.user.is_authenticated:
|
if self.request.user.is_authenticated:
|
||||||
context["rating_bar"] = get_rating_bar(self.request.user, post)
|
context["rating_bar"] = get_rating_bar(self.request.user, kwargs["object"])
|
||||||
else:
|
else:
|
||||||
context["rating_bar"] = None
|
context["rating_bar"] = None
|
||||||
return context
|
return context
|
||||||
|
|
||||||
|
def get(self, request, *args, **kwargs):
|
||||||
|
self.object = self.get_object()
|
||||||
|
self.object.post_views += 1
|
||||||
|
self.object.save(update_fields=["post_views"])
|
||||||
|
context = self.get_context_data(object=self.object)
|
||||||
|
return self.render_to_response(context)
|
||||||
|
|
||||||
|
|
||||||
post_detail_view = PostDetailView.as_view()
|
post_detail_view = PostDetailView.as_view()
|
||||||
|
|
||||||
|
@ -127,7 +130,7 @@ def rate_post_up(request, slug):
|
||||||
post_r.save()
|
post_r.save()
|
||||||
else:
|
else:
|
||||||
PostRating.objects.create(user=request.user, post=post, vote_up=True)
|
PostRating.objects.create(user=request.user, post=post, vote_up=True)
|
||||||
return HttpResponseRedirect(f"/{slug}" + "#rating")
|
return HttpResponseRedirect(reverse("blog:post", kwargs={"slug": slug}) + "#rating")
|
||||||
|
|
||||||
|
|
||||||
@csrf_exempt
|
@csrf_exempt
|
||||||
|
@ -147,7 +150,7 @@ def rate_post_down(request, slug):
|
||||||
post_r.save()
|
post_r.save()
|
||||||
else:
|
else:
|
||||||
PostRating.objects.create(user=request.user, post=post, vote_up=False)
|
PostRating.objects.create(user=request.user, post=post, vote_up=False)
|
||||||
return HttpResponseRedirect(f"/{slug}" + "#rating")
|
return HttpResponseRedirect(reverse("blog:post", kwargs={"slug": slug}) + "#rating")
|
||||||
|
|
||||||
|
|
||||||
def comment(request, slug):
|
def comment(request, slug):
|
||||||
|
@ -160,4 +163,6 @@ def comment(request, slug):
|
||||||
post=post, author=request.user, body=request.POST["body"]
|
post=post, author=request.user, body=request.POST["body"]
|
||||||
)
|
)
|
||||||
|
|
||||||
return HttpResponseRedirect(f"/{slug}" + "#comments")
|
return HttpResponseRedirect(
|
||||||
|
reverse("blog:post", kwargs={"slug": slug}) + "#comments"
|
||||||
|
)
|
||||||
|
|
|
@ -107,6 +107,7 @@
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
{# TODO: add SEO tags here #}
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
function addComment(){
|
function addComment(){
|
||||||
|
|
39
akarpov/utils/cache.py
Normal file
39
akarpov/utils/cache.py
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
from django.db.models import Model
|
||||||
|
|
||||||
|
stack = {}
|
||||||
|
|
||||||
|
|
||||||
|
def cache_model_property(model: Model, name: str):
|
||||||
|
# function to store non-hashable value for model instances
|
||||||
|
# TODO: add TTL here and update with celery
|
||||||
|
|
||||||
|
app_name = model._meta.app_label + model._meta.model_name
|
||||||
|
if app_name not in stack:
|
||||||
|
stack[app_name] = {}
|
||||||
|
|
||||||
|
if model.pk not in stack[app_name]:
|
||||||
|
stack[app_name][model.pk] = {}
|
||||||
|
|
||||||
|
if name not in stack[app_name][model.pk]:
|
||||||
|
val = getattr(model, name)
|
||||||
|
if callable(val):
|
||||||
|
val = val()
|
||||||
|
stack[app_name][model.pk][name] = val
|
||||||
|
return val
|
||||||
|
return stack[app_name][model.pk][name]
|
||||||
|
|
||||||
|
|
||||||
|
def clear_model_cache(model: Model, name=None):
|
||||||
|
app_name = model._meta.app_label + model._meta.model_name
|
||||||
|
if app_name not in stack:
|
||||||
|
return
|
||||||
|
|
||||||
|
if model.pk not in stack[app_name]:
|
||||||
|
return
|
||||||
|
|
||||||
|
if name:
|
||||||
|
if name in stack[app_name][model.pk]:
|
||||||
|
del stack[app_name][model.pk][name]
|
||||||
|
else:
|
||||||
|
del stack[app_name][model.pk]
|
||||||
|
return
|
Loading…
Reference in New Issue
Block a user