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