from __future__ import unicode_literals from django.db import models from django.shortcuts import get_list_or_404, get_object_or_404, render from modelcluster.fields import ParentalKey from modelcluster.contrib.taggit import ClusterTaggableManager from taggit.models import Tag, TaggedItemBase from wagtail.contrib.wagtailroutablepage.models import RoutablePageMixin, route from wagtail.wagtailadmin.edit_handlers import ( FieldPanel, InlinePanel, StreamFieldPanel, ) from wagtail.wagtailimages.edit_handlers import ImageChooserPanel from wagtail.wagtailcore.fields import StreamField from wagtail.wagtailcore.models import Page, Orderable from wagtail.wagtailsnippets.edit_handlers import SnippetChooserPanel from bakerydemo.base.blocks import BaseStreamBlock class BlogPeopleRelationship(Orderable, models.Model): ''' This defines the relationship between the `Peopole` within the `base` app and the BlogPage below allowing us to add people to a BlogPage. ''' page = ParentalKey( 'BlogPage', related_name='blog_person_relationship' ) people = models.ForeignKey( 'base.People', related_name='person_blog_relationship' ) panels = [ SnippetChooserPanel('people') ] class BlogPageTag(TaggedItemBase): content_object = ParentalKey('BlogPage', related_name='tagged_items') class BlogPage(Page): ''' A Blog Page (Post) ''' image = models.ForeignKey( 'wagtailimages.Image', null=True, blank=True, on_delete=models.SET_NULL, related_name='+', help_text='Location image' ) tags = ClusterTaggableManager(through=BlogPageTag, blank=True) date_published = models.DateField("Date article published", blank=True, null=True) body = StreamField( BaseStreamBlock(), verbose_name="Blog post", blank=True ) content_panels = Page.content_panels + [ ImageChooserPanel('image'), StreamFieldPanel('body'), FieldPanel('date_published'), InlinePanel( 'blog_person_relationship', label="Author(s)", panels=None, min_num=1), FieldPanel('tags'), ] def authors(self): ''' Returns the BlogPage's related People ''' authors = [ n.people for n in self.blog_person_relationship.all() ] return authors @property def get_tags(self): ''' Returns the BlogPage's related list of Tags. Each Tag is modified to include a url attribute ''' tags = self.tags.all() for tag in tags: tag.url = '/'+'/'.join(s.strip('/') for s in [ self.get_parent().url, 'tags', tag.slug ]) return tags @property def blog_index(self): return self.get_ancestors().type(BlogIndexPage).last() def get_absolute_url(self): return self.full_url parent_page_types = ['BlogIndexPage'] # Defining what content type can sit under the parent # The empty array means that no children can be placed under the # LocationPage page model subpage_types = [] # api_fields = ['image', 'body'] 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 - so that it works as an index page The RoutablePageMixin is used to allow for a custom sub-URL ''' image = models.ForeignKey( 'wagtailimages.Image', null=True, blank=True, on_delete=models.SET_NULL, related_name='+', help_text='Location listing image' ) introduction = models.TextField( help_text='Text to describe the index page', blank=True) content_panels = Page.content_panels + [ ImageChooserPanel('image'), FieldPanel('introduction') ] # parent_page_types = [ # 'home.HomePage' # ] # Defining what content type can sit under the parent. Since it's a blank # array no subpage can be added subpage_types = ['BlogPage'] def get_context(self, request): context = super(BlogIndexPage, self).get_context(request) context['blogs'] = BlogPage.objects.descendant_of( self).live().order_by( '-first_published_at') return context @route('^tags/(\w+)/$', name='tag_archive') def tag_archive(self, request, tag=None): ''' A Custom view that utilizes Tags. This view will return all related BlogPages for a given Tag. ''' tag = get_object_or_404(Tag, slug=tag) blogs = get_list_or_404( BlogPage.objects.filter(tags=tag).live().descendant_of(self) ) context = { 'title': 'Posts tagged with: {}'.format(tag.name), 'blogs': blogs } return render(request, 'blog/blog_index_page.html', context) # api_fields = ['introduction']