123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213 |
- from __future__ import unicode_literals
- from django.contrib import messages
- from django.db import models
- from django.shortcuts import redirect, render
- from modelcluster.contrib.taggit import ClusterTaggableManager
- from modelcluster.fields import ParentalKey
- from taggit.models import Tag, TaggedItemBase
- from wagtail.admin.panels import FieldPanel, MultipleChooserPanel
- from wagtail.contrib.routable_page.models import RoutablePageMixin, route
- from wagtail.fields import StreamField
- from wagtail.models import Orderable, Page
- from wagtail.search import index
- from bakerydemo.base.blocks import BaseStreamBlock
- class BlogPersonRelationship(Orderable, models.Model):
- """
- This defines the relationship between the `Person` within the `base`
- app and the BlogPage below. This allows people to be added to a BlogPage.
- We have created a two way relationship between BlogPage and Person using
- the ParentalKey and ForeignKey
- """
- page = ParentalKey(
- "BlogPage", related_name="blog_person_relationship", on_delete=models.CASCADE
- )
- person = models.ForeignKey(
- "base.Person", related_name="person_blog_relationship", on_delete=models.CASCADE
- )
- panels = [FieldPanel("person")]
- class BlogPageTag(TaggedItemBase):
- """
- This model allows us to create a many-to-many relationship between
- the BlogPage object and tags. There's a longer guide on using it at
- https://docs.wagtail.org/en/stable/reference/pages/model_recipes.html#tagging
- """
- content_object = ParentalKey(
- "BlogPage", related_name="tagged_items", on_delete=models.CASCADE
- )
- class BlogPage(Page):
- """
- A Blog Page
- We access the Person object with an inline panel that references the
- ParentalKey's related_name in BlogPersonRelationship. More docs:
- https://docs.wagtail.org/en/stable/topics/pages.html#inline-models
- """
- introduction = models.TextField(help_text="Text to describe the page", blank=True)
- image = models.ForeignKey(
- "wagtailimages.Image",
- null=True,
- blank=True,
- on_delete=models.SET_NULL,
- related_name="+",
- help_text="Landscape mode only; horizontal width between 1000px and 3000px.",
- )
- body = StreamField(
- BaseStreamBlock(), verbose_name="Page body", blank=True, use_json_field=True
- )
- subtitle = models.CharField(blank=True, max_length=255)
- tags = ClusterTaggableManager(through=BlogPageTag, blank=True)
- date_published = models.DateField("Date article published", blank=True, null=True)
- content_panels = Page.content_panels + [
- FieldPanel("subtitle"),
- FieldPanel("introduction"),
- FieldPanel("image"),
- FieldPanel("body"),
- FieldPanel("date_published"),
- MultipleChooserPanel(
- "blog_person_relationship",
- chooser_field_name="person",
- heading="Authors",
- label="Author",
- panels=None,
- min_num=1,
- ),
- FieldPanel("tags"),
- ]
- search_fields = Page.search_fields + [
- index.SearchField("body"),
- ]
- def authors(self):
- """
- Returns the BlogPage's related people. Again note that we are using
- the ParentalKey's related_name from the BlogPersonRelationship model
- to access these objects. This allows us to access the Person objects
- with a loop on the template. If we tried to access the blog_person_
- relationship directly we'd print `blog.BlogPersonRelationship.None`
- """
- # Only return authors that are not in draft
- return [
- n.person
- for n in self.blog_person_relationship.filter(
- person__live=True
- ).select_related("person")
- ]
- @property
- def get_tags(self):
- """
- Similar to the authors function above we're returning all the tags that
- are related to the blog post into a list we can access on the template.
- We're additionally adding a URL to access BlogPage objects with that tag
- """
- tags = self.tags.all()
- base_url = self.get_parent().url
- for tag in tags:
- tag.url = f"{base_url}tags/{tag.slug}/"
- return tags
- # Specifies parent to BlogPage as being BlogIndexPages
- parent_page_types = ["BlogIndexPage"]
- # Specifies what content types can exist as children of BlogPage.
- # Empty list means that no child content types are allowed.
- subpage_types = []
- class BlogIndexPage(RoutablePageMixin, Page):
- """
- Index page for blogs.
- We need to alter the page model's context to return the child page objects,
- the BlogPage objects, so that it works as an index page
- RoutablePageMixin is used to allow for a custom sub-URL for the tag views
- defined above.
- """
- introduction = models.TextField(help_text="Text to describe the page", blank=True)
- image = models.ForeignKey(
- "wagtailimages.Image",
- null=True,
- blank=True,
- on_delete=models.SET_NULL,
- related_name="+",
- help_text="Landscape mode only; horizontal width between 1000px and 3000px.",
- )
- content_panels = Page.content_panels + [
- FieldPanel("introduction"),
- FieldPanel("image"),
- ]
- # Specifies that only BlogPage objects can live under this index page
- subpage_types = ["BlogPage"]
- # Defines a method to access the children of the page (e.g. BlogPage
- # objects). On the demo site we use this on the HomePage
- def children(self):
- return self.get_children().specific().live()
- # Overrides the context to list all child items, that are live, by the
- # date that they were published
- # https://docs.wagtail.org/en/stable/getting_started/tutorial.html#overriding-context
- def get_context(self, request):
- context = super(BlogIndexPage, self).get_context(request)
- context["posts"] = (
- BlogPage.objects.descendant_of(self).live().order_by("-date_published")
- )
- return context
- # This defines a Custom view that utilizes Tags. This view will return all
- # related BlogPages for a given Tag or redirect back to the BlogIndexPage.
- # More information on RoutablePages is at
- # https://docs.wagtail.org/en/stable/reference/contrib/routablepage.html
- @route(r"^tags/$", name="tag_archive")
- @route(r"^tags/([\w-]+)/$", name="tag_archive")
- def tag_archive(self, request, tag=None):
- try:
- tag = Tag.objects.get(slug=tag)
- except Tag.DoesNotExist:
- if tag:
- msg = 'There are no blog posts tagged with "{}"'.format(tag)
- messages.add_message(request, messages.INFO, msg)
- return redirect(self.url)
- posts = self.get_posts(tag=tag)
- context = {"page": self, "tag": tag, "posts": posts}
- return render(request, "blog/blog_index_page.html", context)
- def serve_preview(self, request, mode_name):
- # Needed for previews to work
- return self.serve(request)
- # Returns the child BlogPage objects for this BlogPageIndex.
- # If a tag is used then it will filter the posts by tag.
- def get_posts(self, tag=None):
- posts = BlogPage.objects.live().descendant_of(self)
- if tag:
- posts = posts.filter(tags=tag)
- return posts
- # Returns the list of Tags for all child posts of this BlogPage.
- def get_child_tags(self):
- tags = []
- for post in self.get_posts():
- # Not tags.append() because we don't want a list of lists
- tags += post.get_tags
- tags = sorted(set(tags))
- return tags
|