From fe6e0392a99cb7e7402c50ee46d7b5129f2dc2a1 Mon Sep 17 00:00:00 2001 From: "jindra12.underdark" <jindra12.underdark@gmail.com> Date: Sat, 15 Jul 2023 02:48:43 +0200 Subject: [PATCH] Commit experimental changes for the queries #210 --- district/models.py | 13 ++- instagram_utils/models.py | 8 +- main/models.py | 21 +++-- shared/models.py | 183 ++++++++++++++++++++++++++++++-------- uniweb/models.py | 9 +- 5 files changed, 179 insertions(+), 55 deletions(-) diff --git a/district/models.py b/district/models.py index 5a68c0fb..dc836526 100644 --- a/district/models.py +++ b/district/models.py @@ -53,6 +53,7 @@ from shared.models import ( ExtendedMetadataPageMixin, FooterMixin, MenuMixin, + SharedArticleTypes, SharedTaggedDistrictArticle, SubpageMixin, ) @@ -406,7 +407,9 @@ class DistrictArticlePage( is_black = models.BooleanField("Má tmavĂ© pozadĂ?", default=False) tags = ClusterTaggableManager(through=DistrictArticleTag, blank=True) shared_tags = ClusterTaggableManager( - through=SharedTaggedDistrictArticle, blank=True + verbose_name="Tagy pro sdĂlenĂ mezi weby", + through=SharedTaggedDistrictArticle, + blank=True, ) thumb_image = models.ForeignKey( "wagtailimages.Image", @@ -535,7 +538,9 @@ class DistrictArticlesPage( def get_context(self, request): context = super().get_context(request) context["articles"] = Paginator( - self.append_all_shared_articles(self.get_children().live().specific()), + self.append_all_shared_articles( + SharedArticleTypes.DISTRICT, DistrictArticlePage.objects.child_of(self) + ), self.max_items, ).get_page(request.GET.get("page")) return context @@ -559,7 +564,7 @@ class DistrictArticlesPage( context = super().get_context(request) site_article_ids = self.append_all_shared_articles( - self.get_children().live().specific() + SharedArticleTypes.DISTRICT, DistrictArticlePage.objects.child_of(self) ).values_list("id", flat=True) # NaplnĂm "tag" a "article_page_list" parametry @@ -577,7 +582,7 @@ class DistrictArticlesPage( separátnÄ›, ale pak by se musel rozpadnout ten try/except na vĂce blokĹŻ. """ article_page_qs = self.append_all_shared_articles( - DistrictArticlePage.objects + SharedArticleTypes.DISTRICT, DistrictArticlePage.objects ).filter(id__in=site_article_ids) try: diff --git a/instagram_utils/models.py b/instagram_utils/models.py index 579f9d56..d0b80901 100644 --- a/instagram_utils/models.py +++ b/instagram_utils/models.py @@ -1,4 +1,5 @@ import datetime +import logging import mimetypes import uuid @@ -10,6 +11,9 @@ def get_current_datetime() -> datetime.datetime: return datetime.datetime.now(tz=datetime.timezone.utc) +logger = logging.getLogger(__name__) + + def get_instagram_image_path(instance, filename) -> str: mimetypes_instance = mimetypes.MimeTypes() guessed_type = mimetypes_instance.guess_type(filename, strict=False)[0] @@ -92,9 +96,7 @@ class InstagramMixin(models.Model): ) service.perform_update() except Exception: - logger.error( - "Instagram post update failed", exc_info=True - ) + logger.error("Instagram post update failed", exc_info=True) super().save(*args, **kwargs) diff --git a/main/models.py b/main/models.py index 2afb57ff..6d4d882b 100644 --- a/main/models.py +++ b/main/models.py @@ -1,13 +1,10 @@ -from datetime import date, timedelta from functools import cached_property from dateutil.relativedelta import relativedelta from django.conf import settings from django.contrib import messages from django.core.paginator import Paginator -from django.core.validators import RegexValidator from django.db import models -from django.forms import ValidationError from django.http import HttpResponseRedirect, JsonResponse from django.shortcuts import render from django.utils import timezone @@ -22,7 +19,7 @@ from wagtail.admin.panels import ( PageChooserPanel, TabbedInterface, ) -from wagtail.blocks import CharBlock, RichTextBlock +from wagtail.blocks import RichTextBlock from wagtail.contrib.routable_page.models import RoutablePageMixin, route from wagtail.fields import RichTextField, StreamField from wagtail.models import Page @@ -38,6 +35,7 @@ from shared.models import ( # MenuMixin, ArticlesMixin, ExtendedMetadataHomePageMixin, ExtendedMetadataPageMixin, + SharedArticleTypes, SharedTaggedMainArticle, SubpageMixin, ) @@ -515,9 +513,9 @@ class MainArticlesPage( def get_all_articles_search_response(self, request): article_paginator = Paginator( - self.append_all_shared_articles(MainArticlePage.objects.live()).search( - request.GET["q"] - ), + self.append_all_shared_articles( + SharedArticleTypes.MAIN, MainArticlePage.objects + ).search(request.GET["q"]), 10, ) article_page = article_paginator.get_page(request.GET.get("page", 1)) @@ -545,7 +543,8 @@ class MainArticlesPage( query = request.GET["q"] article_results = self.append_all_shared_articles( - MainArticlePage.objects.live() + SharedArticleTypes.MAIN, + MainArticlePage.objects, ).search(query)[:11] return render( @@ -629,7 +628,11 @@ class MainArticlePage( help_text="Kraj, ke kterĂ©mu se ÄŤlánek vztahuje", ) tags = ClusterTaggableManager(through=MainArticleTag, blank=True) - shared_tags = ClusterTaggableManager(through=SharedTaggedMainArticle, blank=True) + shared_tags = ClusterTaggableManager( + verbose_name="Tagy pro sdĂlenĂ mezi weby", + through=SharedTaggedMainArticle, + blank=True, + ) search_fields = Page.search_fields + [ index.SearchField("title"), diff --git a/shared/models.py b/shared/models.py index 8082e2cc..43cc1577 100644 --- a/shared/models.py +++ b/shared/models.py @@ -1,8 +1,9 @@ import logging -import sys +from enum import Enum +from django.apps import apps from django.db import models -from django.db.models.functions import Coalesce +from django.db.models.expressions import F, Value from django.utils import timezone from modelcluster.fields import ParentalKey, ParentalManyToManyField from taggit.models import ItemBase, TagBase @@ -257,61 +258,169 @@ class SharedTaggedMainArticle(ItemBase): ) +class SharedArticleTypes(Enum): + DISTRICT = "district" + UNIWEB = "uniweb" + MAIN = "main" + + class ArticlesMixin(models.Model): shared_tags = ParentalManyToManyField( - "shared.SharedTag", related_name="SdĂlenĂ© tagy", blank=True + "shared.SharedTag", + verbose_name="VĂ˝bÄ›r tagĹŻ pro ÄŤlánky sdĂlenĂ© mezi sĂtÄ›mi", + help_text="Pro vĂ˝bÄ›r jednoho tagu kliknÄ›te na tag a uloĹľte nebo publikujte stránku. Pro vĂ˝bÄ›r vĂce tagĹŻ vyuĹľijte podrĹľte Ctrl a vyberte pĹ™ĂslušnĂ© tagy.", + blank=True, ) content_panels = Page.content_panels + [FieldPanel("shared_tags")] - def append_all_shared_articles(self, previous_query): - districtArticleQuery: models.QuerySet = getattr( - sys.modules["district"], "DistrictArticlePage" - ).objects - uniwebArticlePageQuery: models.QuerySet = getattr( - sys.modules["uniweb"], "UniwebArticlePage" - ).objects - mainArticlePageQuery: models.QuerySet = getattr( - sys.modules["main"], "MainArticlePage" - ).objects + def append_all_shared_articles( + self, source: SharedArticleTypes | None, previous_query: models.QuerySet | None + ): + """ + To prevent circular deps, we get class models during runtime + """ + DistrictArticlePage = apps.get_model(app_label="district.DistrictArticlePage") + UniwebArticlePage = apps.get_model(app_label="uniweb.UniwebArticlePage") + MainArticlePage = apps.get_model(app_label="main.MainArticlePage") + + """ + In order to balance union() requirements for tables with same-fields only, we are adding null fields using values(). + These values must be in correct order + """ + + main_fields = { + "page_ptr_id": F("page_ptr_id"), + "perex": F("perex"), + "date": F("date"), + "author": F("author"), + "image_id": F("image_id"), + "search_image_id": F("search_image_id"), + "content": F("content"), + "author_page_id": F("author_page_id"), + "region": F("region"), + "article_type": F("article_type"), + "is_black": F("is_black"), + "thumb_image_id": F("search_image_id"), + } + + district_fields = { + "page_ptr_id": F("page_ptr_id"), + "perex": F("perex"), + "date": F("date"), + "author": F("author"), + "image_id": F("image_id"), + "search_image_id": F("search_image_id"), + "content": F("content"), + "author_page_id": F("author_page_id"), + "region": Value("", models.CharField()), + "article_type": Value(0, models.PositiveSmallIntegerField()), + "is_black": F("is_black"), + "thumb_image_id": F("thumb_image_id"), + } + + uniweb_fields = { + "page_ptr_id": F("page_ptr_id"), + "perex": F("perex"), + "date": F("date"), + "author": F("author"), + "image_id": F("image_id"), + "search_image_id": F("search_image_id"), + "content": F("content"), + "author_page_id": Value( + None, + output_field=models.ForeignKey( + DistrictArticlePage, blank=True, on_delete=models.SET_NULL + ), + ), + "region": Value("", models.CharField()), + "article_type": Value(0, models.PositiveSmallIntegerField()), + "is_black": Value(False, models.BooleanField()), + "thumb_image_id": F("search_image_id"), + } + + districtArticleQuery: models.QuerySet = DistrictArticlePage.objects + uniwebArticlePageQuery: models.QuerySet = UniwebArticlePage.objects + mainArticlePageQuery: models.QuerySet = MainArticlePage.objects district_by_slug = ( - districtArticleQuery.live() - .specific() - .filter( - shared_tags__slug__in=self.shared_tags.values_list("slug", flat=True) + ( + districtArticleQuery.filter( + shared_tags__slug__in=self.shared_tags.values_list( + "slug", flat=True + ) + ) ) + .live() + .specific() ) uniweb_by_slug = ( - uniwebArticlePageQuery.live() - .specific() - .filter( - shared_tags__slug__in=self.shared_tags.values_list("slug", flat=True) + ( + uniwebArticlePageQuery.filter( + shared_tags__slug__in=self.shared_tags.values_list( + "slug", flat=True + ) + ) ) + .live() + .specific() ) main_by_slug = ( - mainArticlePageQuery.live() - .specific() - .filter( - shared_tags__slug__in=self.shared_tags.values_list("slug", flat=True) + ( + mainArticlePageQuery.filter( + shared_tags__slug__in=self.shared_tags.values_list( + "slug", flat=True + ) + ) ) + .live() + .specific() ) - ordered_articles = ( - district_by_slug.union(uniweb_by_slug) - .union(main_by_slug) - .union(previous_query) - .distinct() - .annotate( - article_date=Coalesce( - "-districtarticlepage__date", "-uniwebarticlepage__date", "-date" - ), - shared=True, + results = ( + main_by_slug.values( + **main_fields, shared=Value(True, output_field=models.BooleanField()) + ) + .union( + uniweb_by_slug.values( + **uniweb_fields, + shared=Value(True, output_field=models.BooleanField()), + ) + ) + .union( + district_by_slug.values( + **district_fields, + shared=Value(True, output_field=models.BooleanField()), + ) ) - .order_by("aritcle_date") ) - return ordered_articles + if previous_query is not None: + prepared_query = previous_query.live().specific() + + if source is SharedArticleTypes.DISTRICT: + prepared_query = prepared_query.values( + **district_fields, + shared=Value(False, output_field=models.BooleanField()), + ) + elif source is SharedArticleTypes.UNIWEB: + prepared_query = prepared_query.values( + **uniweb_fields, + shared=Value(False, output_field=models.BooleanField()), + ) + elif source is SharedArticleTypes.MAIN: + prepared_query = prepared_query.values( + **main_fields, + shared=Value(False, output_field=models.BooleanField()), + ) + + results = results.union(prepared_query) + + results = results.order_by("date") + + print(results.query) + + return results def get_article_page_by_slug(self, slug: str): articles = self.append_all_shared_articles() diff --git a/uniweb/models.py b/uniweb/models.py index 520635a1..72bc8b9e 100644 --- a/uniweb/models.py +++ b/uniweb/models.py @@ -35,6 +35,7 @@ from shared.models import ( ExtendedMetadataHomePageMixin, ExtendedMetadataPageMixin, FooterMixin, + SharedArticleTypes, SharedTaggedUniwebArticle, SubpageMixin, ) @@ -549,7 +550,7 @@ class UniwebArticlesIndexPage( tag = request.GET.get("tag") articles = self.append_all_shared_articles( - self.get_children().live().specific() + SharedArticleTypes.UNIWEB, UniwebArticlePage.objects.child_of(self) ) if tag is not None: @@ -569,7 +570,11 @@ class UniwebArticlePage( ### FIELDS tags = ClusterTaggableManager(through=UniwebArticleTag, blank=True) - shared_tags = ClusterTaggableManager(through=SharedTaggedUniwebArticle, blank=True) + shared_tags = ClusterTaggableManager( + verbose_name="Tagy pro sdĂlenĂ mezi weby", + through=SharedTaggedUniwebArticle, + blank=True, + ) ### PANELS -- GitLab