minor blog updates and fixes, added better cache

This commit is contained in:
Alexander Karpov 2023-08-04 17:51:50 +03:00
parent 13638466a2
commit f0dd4e2b27
6 changed files with 91 additions and 14 deletions

View 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),
),
]

View File

@ -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

View File

@ -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

View File

@ -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"
)

View File

@ -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
View 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