Создать собственный трясогузку URL
У меня есть сайт, который я структурировал так:
Первая страница: Животные Дополнительная
страница: Кошка Дополнительная
страница: Собака
Первая страница: Кот
Подстраница: Еда
Первая страница: Собака
Подстраница: Еда
Таким образом, пользователь может перейти на сайт / животные или сайт / кошка. Животные - это общая информация, Кот и Собака - более подробные разделы сайта.
Что я хочу сделать:
иногда мне хочется иметь возможность повторно использовать страницы из Animals и использовать их в Cat или Dog. Но я не хочу, чтобы URL-адрес переключался с одного раздела на другой.
Пример: если пользователь находится в разделе "Животное" и щелкает статью о продукте, URL-адрес должен быть таким:animals/cats/food_article
Если они нажмут на ту же статью под кошками, URL-адрес должен выглядеть так:cats/food_article
Что пробовал:
пробовал использоватьRoutableRouteMixin
. Но это работает для подстраниц, а не для страниц того же уровня.
Я попытался перезаписать get_url_parts
метод для моделей изделий. Но затем я получил ошибку 404, потому что страницы на самом деле не существовали по созданному мной URL.
Можно ли этого добиться в трясогузке? Или есть решение Django, которое я могу использовать с Wagtail?
2 ответа
Возможный подход
- Без серьезной настройки вам нужно будет добавить отдельную модель страницы для ваших статей, их можно добавить под корневую страницу, а затем просто не показывать по умолчанию (включая скрытые из любых настроек меню).
- Затем создайте модельный кластер, который будет взаимосвязью между вашими страницами с животными (кошка, собака) и страницами статей.
- Наконец, переопределите страницу Animal
get_children
метод, который есть на каждомPage
и происходит изdjango-treebeard
. Этот метод используется логикой маршрутизации, чтобы определить, является ли страница дочерней по отношению к другой (при чтении URL-адреса). - Как только вы создадите несколько
AnimalPage
s, затем создайтеArticlePage
(например, статья о продуктах питания), затем перейдите на страницу с собаками и кошками и свяжите статью о продуктах питания со страницей. - На этом этапе вы сможете получить доступ к статье о еде по 3 URL-адресам;
http://localhost:8000/food-article/
,http://localhost:8000/animals/cats/food-article/
а такжеhttp://localhost:8000/animals/dogs/food-article/
. - Вы можете сделать так, чтобы исходный URL-адрес статьи о еде не работал несколькими способами, но самый простой способ - поставить все
ArticlePage
s на своей родительской странице, а затем используйте routablemixin, чтобы эта страница не показывала никаких дополнительных URL-адресов.- Примечание. Если вы разместите все статьи на отдельной подстранице (например,
ArticlesPage
, это все равно должно работать, поскольку проверяется только "последний" раздел URL-адреса). Однако я не проверял это.
- Примечание. Если вы разместите все статьи на отдельной подстранице (например,
Пример кода
from django.db import models
from modelcluster.fields import ParentalKey
from modelcluster.models import ClusterableModel
from wagtail.admin.edit_handlers import (
InlinePanel,
PageChooserPanel,
)
from wagtail.core.models import Orderable, Page
class ArticlePage(Page):
# Articles will be available to link to any other AnimalPage and content shared
content_panels = Page.content_panels
subpage_types = [] # no sub-pages allowed
class RelatedArticle(models.Model):
# http://docs.wagtail.io/en/v2.7.1/reference/pages/panels.html#inline-panels
article_page = models.ForeignKey(
'wagtailcore.Page',
null=True,
blank=True,
on_delete=models.SET_NULL,
related_name='+',
)
panels = [
PageChooserPanel('article_page', 'base.ArticlePage')
]
class Meta:
abstract = True
class ArticlePageRelatedPages(Orderable, RelatedArticle):
page = ParentalKey(
'base.AnimalPage',
on_delete=models.CASCADE,
related_name='related_articles'
)
class AnimalsPage(Page):
# This is our root animals page
content_panels = Page.content_panels
subpage_types = ['AnimalPage']
class AnimalPage(Page):
def get_articles(self):
# returns a queryset (not a list) of all Pages that are linked articles
return Page.objects.filter(id__in=[
r.article_page.pk for r in self.related_articles.all()
])
def get_children(self):
# override the method from django-treebeard
# can be used in the template or wherever needed for 'children'
# this method is also used when attempting to find child urls
sub_pages = super().get_children()
articles = self.get_articles()
return articles | sub_pages # merges the two querysets for the 'or' operator
content_panels = Page.content_panels + [
InlinePanel('related_articles', label='Related Articles'),
]
subpage_types = [] # no sub-pages allowed
Вы можете создать IncludePage, как показано ниже, и использовать его для представления того же контента под другим URL-адресом.
from django.db import models
from wagtail.core.models import Page
from wagtail.admin.edit_handlers import PageChooserPanel
from django.http import Http404
class IncludePage(Page):
page = models.ForeignKey('wagtailcore.Page',
null=True, blank=True,
on_delete=models.SET_NULL,
related_name='+')
content_panels = Page.content_panels + [
PageChooserPanel('page'),
]
def get_template(self, *args, **kwargs):
page = self.page.specific
if type(page) == IncludePage:
raise Http404("Avoid recursion")
return page.get_template(*args, **kwargs)
def get_context(self, *args, **kwargs):
page = self.page.specific
if type(page) == IncludePage:
raise Http404("Avoid recursion")
return page.get_context(*args, **kwargs)
Обратите внимание, однако, что это не будет работать для страниц с настраиваемыми функциями обслуживания (например, страниц, использующих RoutableRouteMixin). Для тех, кому вам нужно будет продублировать функциональность обслуживания в IncludePage, чтобы соответствовать. Но в зависимости от вашего сайта это может быть полезно.