diff --git a/main/feeds.py b/main/feeds.py index 48497ff06f6edcc320dae78fa2ec54515d2ccb6b..46d6f2c244f3b96bd9939f7e463a58ef3fce8606 100644 --- a/main/feeds.py +++ b/main/feeds.py @@ -56,7 +56,7 @@ class LatestArticlesFeed(Feed): return "" def item_categories(self, item: MainArticlePage) -> list: - return item.get_tags() if callable(item.get_tags) else item.get_tags + return item.get_tags def item_link(self, item: MainArticlePage) -> str: return item.get_full_url diff --git a/shared/models.py b/shared/models.py index f45f82be2d3e2fe7df8fc85459bf8891e524de5f..391732d48368ab03aa7f80732adee2d1d7199f58 100644 --- a/shared/models.py +++ b/shared/models.py @@ -8,7 +8,7 @@ from django.apps import apps from django.core.paginator import Paginator from django.db import models from django.db.models import Q -from django.db.models.expressions import F, Value +from django.db.models.expressions import F, Subquery, Value from django.utils import timezone from modelcluster.fields import ParentalKey, ParentalManyToManyField from taggit.models import ItemBase, Tag, TagBase @@ -69,16 +69,22 @@ class ArticleMixin(models.Model): null=True, verbose_name="obrázek", ) - shared_type = models.TextField( - null=True, blank=True - ) # hidden field describing the source of shared articles + """ + Hidden field describing the source of shared articles, can be of values "district", "uniweb" or "main", + depending on from which type of site this article was shared from + """ + shared_type = models.TextField(null=True, blank=True) + """ + Hidden field which links to a Page model of ArticlesMixin page to which this article was shared. + Example: DistrictArticlesPage has shared tag "main", which this article shares as well -> shared_from will contain a reference to DistrictArticlesPage + """ shared_from = models.ForeignKey( Page, null=True, blank=True, related_name="+", on_delete=models.PROTECT, - ) # hidden field to indicate the article is from another page + ) search_fields = Page.search_fields + [ index.SearchField("title"), @@ -109,7 +115,7 @@ class ArticleMixin(models.Model): @property def get_no_index(self): """ - Indicates that a link should contain rel="noindex" + Indicates that a link to self should contain rel="noindex" """ return self.shared_from is not None @@ -134,6 +140,9 @@ class ArticleMixin(models.Model): @property def articles_page(self): + """ + Returns articles page on which this article is displayed + """ return ( self.shared_from.get_specific() if self.shared_from @@ -142,6 +151,9 @@ class ArticleMixin(models.Model): @property def root_page(self): + """ + Returns root page of article, or a root page of Articles page to which this article was shared + """ if self.shared_from is None: return self.get_parent().get_ancestors().specific().live().last() @@ -149,9 +161,12 @@ class ArticleMixin(models.Model): @property def get_tags(self): + """ + Returns all tags, including tags of shared articles from another site + """ if self.shared_from is not None: return self.articles_page.search_tags_by_unioned_id_query([self]) - return self.tags.all + return self.tags.all() @classmethod def has_tags(cls): @@ -394,17 +409,27 @@ class ArticlesMixin(models.Model): ).values() ) - def create_base_shared_query(self, query): + def create_base_shared_query(self, query, original_query): """ - Returns a query filtered by shared tags + Returns a query filtered by shared tags, + Filters out page ids that would be duplicates of original query (shared articles dispayed on the same page) """ - return ( + filtered_query = ( query.filter( - shared_tags__slug__in=self.shared_tags.values_list("slug", flat=True) + ~Q(page_ptr_id__in=Subquery(original_query.values("page_ptr_id"))), + shared_tags__slug__in=self.shared_tags.values_list("slug", flat=True), + ) + if isinstance(original_query, models.QuerySet) + else query.filter( + ~Q( + page_ptr_id__in=list( + map(lambda article: article.pk, original_query) + ) + ), + shared_tags__slug__in=self.shared_tags.values_list("slug", flat=True), ) - .live() - .specific() ) + return filtered_query.live().specific() def append_all_shared_articles_query( self, @@ -525,7 +550,7 @@ class ArticlesMixin(models.Model): ) create_query_by_slug = lambda query: apply_additional_filter( - self.create_base_shared_query(query) + self.create_base_shared_query(query, previous_query) ) district_by_slug = create_query_by_slug(district_article_query) @@ -663,14 +688,17 @@ class ArticlesMixin(models.Model): self, query: models.QuerySet, page_size: int, page: int ): """ - Returns a list based on articles query using Paginator internally. + Returns Page object whose property object_list has been materialized, uses Paginator internally """ - return self.materialize_shared_articles_query( - Paginator( - query, - page_size, - ).get_page(page) + paginator = Paginator( + query, + page_size, + ) + paginator_page = paginator.get_page(page) + paginator_page.object_list = self.materialize_shared_articles_query( + paginator_page.object_list ) + return paginator_page def get_article_page_by_slug(self, slug: str): """ @@ -794,13 +822,13 @@ class ArticlesMixin(models.Model): ) current_query = search_factory(previous_query) shared_district_search = search_factory( - self.create_base_shared_query(DistrictArticlePage.objects) + self.create_base_shared_query(DistrictArticlePage.objects, current_query) ) shared_uniweb_search = search_factory( - self.create_base_shared_query(UniwebArticlePage.objects) + self.create_base_shared_query(UniwebArticlePage.objects, current_query) ) shared_main_search = search_factory( - self.create_base_shared_query(MainArticlePage.objects) + self.create_base_shared_query(MainArticlePage.objects, current_query) ) # .search is not lazy either, making this the best optimized query possible AFAIK