diff --git a/akarpov/blog/migrations/0008_tag_seo_tags.py b/akarpov/blog/migrations/0008_tag_seo_tags.py new file mode 100644 index 0000000..e1244b7 --- /dev/null +++ b/akarpov/blog/migrations/0008_tag_seo_tags.py @@ -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), + ), + ] diff --git a/akarpov/blog/models.py b/akarpov/blog/models.py index 221ccf4..2807708 100644 --- a/akarpov/blog/models.py +++ b/akarpov/blog/models.py @@ -10,6 +10,7 @@ from akarpov.tools.shortener.models import ShortLinkModel from akarpov.users.models import User from akarpov.users.services.history import UserHistoryModel +from akarpov.utils.cache import cache_model_property from akarpov.utils.string import cleanhtml @@ -41,7 +42,6 @@ def get_comments(self): return self.comments.all() def h_tags(self): - # TODO: add caching here tags = ( Tag.objects.filter(posts__id=self.id) .annotate(num_posts=Count("posts")) @@ -49,18 +49,20 @@ def h_tags(self): ) return tags - def h_tag(self): + def _h_tag(self): return self.h_tags().first() + def h_tag(self): + return cache_model_property(self, "_h_tag") + @property def text(self): - # TODO: add caching here return cleanhtml(self.body) @property @extend_schema_field(serializers.CharField) def summary(self): - body = self.text + body = cache_model_property(self, "text") return body[:100] + "..." if len(body) > 100 else "" def get_absolute_url(self): @@ -77,6 +79,7 @@ class SlugMeta: class Tag(UserHistoryModel): name = models.CharField(max_length=20, unique=True) color = ColorField(blank=True, default="#FF0000") + seo_tags = models.TextField(blank=True, null=True) def __str__(self): return self.name diff --git a/akarpov/blog/signals.py b/akarpov/blog/signals.py index bc18b39..dbbd888 100644 --- a/akarpov/blog/signals.py +++ b/akarpov/blog/signals.py @@ -1,7 +1,8 @@ from django.db.models.signals import pre_delete, pre_save 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 @@ -38,6 +39,16 @@ def post_rating(sender, instance: PostRating, **kwargs): 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) def post_rating_delete(sender, instance: PostRating, **kwargs): post = instance.post diff --git a/akarpov/blog/views.py b/akarpov/blog/views.py index d2e6a53..bfcebc5 100644 --- a/akarpov/blog/views.py +++ b/akarpov/blog/views.py @@ -2,6 +2,7 @@ from django.core.exceptions import PermissionDenied, ValidationError from django.http import HttpResponseRedirect from django.shortcuts import get_object_or_404 +from django.urls import reverse from django.views.decorators.csrf import csrf_exempt from django.views.generic import CreateView, DetailView, ListView, UpdateView @@ -19,17 +20,19 @@ class PostDetailView(DetailView): def get_context_data(self, **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: - context["rating_bar"] = get_rating_bar(self.request.user, post) + context["rating_bar"] = get_rating_bar(self.request.user, kwargs["object"]) else: context["rating_bar"] = None 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() @@ -127,7 +130,7 @@ def rate_post_up(request, slug): post_r.save() else: 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 @@ -147,7 +150,7 @@ def rate_post_down(request, slug): post_r.save() else: 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): @@ -160,4 +163,6 @@ def comment(request, slug): post=post, author=request.user, body=request.POST["body"] ) - return HttpResponseRedirect(f"/{slug}" + "#comments") + return HttpResponseRedirect( + reverse("blog:post", kwargs={"slug": slug}) + "#comments" + ) diff --git a/akarpov/templates/blog/post.html b/akarpov/templates/blog/post.html index 2655db7..b51cb1b 100644 --- a/akarpov/templates/blog/post.html +++ b/akarpov/templates/blog/post.html @@ -107,6 +107,7 @@ {% endfor %} + {# TODO: add SEO tags here #}