models.py 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  1. from __future__ import unicode_literals
  2. from django.db import models
  3. from django.shortcuts import get_list_or_404, get_object_or_404, render
  4. from modelcluster.fields import ParentalKey
  5. from modelcluster.contrib.taggit import ClusterTaggableManager
  6. from taggit.models import Tag, TaggedItemBase
  7. from wagtail.contrib.wagtailroutablepage.models import RoutablePageMixin, route
  8. from wagtail.wagtailadmin.edit_handlers import (
  9. FieldPanel,
  10. InlinePanel,
  11. StreamFieldPanel,
  12. )
  13. from wagtail.wagtailimages.edit_handlers import ImageChooserPanel
  14. from wagtail.wagtailcore.fields import StreamField
  15. from wagtail.wagtailcore.models import Page, Orderable
  16. from wagtail.wagtailsnippets.edit_handlers import SnippetChooserPanel
  17. from bakerydemo.base.blocks import BaseStreamBlock
  18. class BlogPeopleRelationship(Orderable, models.Model):
  19. '''
  20. This defines the relationship between the `Peopole` within the `base`
  21. app and the BlogPage below allowing us to add people to a BlogPage.
  22. '''
  23. page = ParentalKey(
  24. 'BlogPage', related_name='blog_person_relationship'
  25. )
  26. people = models.ForeignKey(
  27. 'base.People', related_name='person_blog_relationship'
  28. )
  29. panels = [
  30. SnippetChooserPanel('people')
  31. ]
  32. class BlogPageTag(TaggedItemBase):
  33. content_object = ParentalKey('BlogPage', related_name='tagged_items')
  34. class BlogPage(Page):
  35. '''
  36. A Blog Page (Post)
  37. '''
  38. image = models.ForeignKey(
  39. 'wagtailimages.Image',
  40. null=True,
  41. blank=True,
  42. on_delete=models.SET_NULL,
  43. related_name='+',
  44. help_text='Location image'
  45. )
  46. tags = ClusterTaggableManager(through=BlogPageTag, blank=True)
  47. date_published = models.DateField("Date article published", blank=True, null=True)
  48. body = StreamField(
  49. BaseStreamBlock(), verbose_name="Blog post", blank=True
  50. )
  51. content_panels = Page.content_panels + [
  52. ImageChooserPanel('image'),
  53. StreamFieldPanel('body'),
  54. FieldPanel('date_published'),
  55. InlinePanel(
  56. 'blog_person_relationship', label="Author(s)",
  57. panels=None, min_num=1),
  58. FieldPanel('tags'),
  59. ]
  60. def authors(self):
  61. '''
  62. Returns the BlogPage's related People
  63. '''
  64. authors = [
  65. n.people for n in self.blog_person_relationship.all()
  66. ]
  67. return authors
  68. @property
  69. def get_tags(self):
  70. '''
  71. Returns the BlogPage's related list of Tags.
  72. Each Tag is modified to include a url attribute
  73. '''
  74. tags = self.tags.all()
  75. for tag in tags:
  76. tag.url = '/'+'/'.join(s.strip('/') for s in [
  77. self.get_parent().url,
  78. 'tags',
  79. tag.slug
  80. ])
  81. return tags
  82. @property
  83. def blog_index(self):
  84. return self.get_ancestors().type(BlogIndexPage).last()
  85. def get_absolute_url(self):
  86. return self.full_url
  87. parent_page_types = ['BlogIndexPage']
  88. # Defining what content type can sit under the parent
  89. # The empty array means that no children can be placed under the
  90. # LocationPage page model
  91. subpage_types = []
  92. # api_fields = ['image', 'body']
  93. class BlogIndexPage(RoutablePageMixin, Page):
  94. '''
  95. Index page for blogs.
  96. We need to alter the page model's context to return the child page objects - the
  97. BlogPage - so that it works as an index page
  98. The RoutablePageMixin is used to allow for a custom sub-URL
  99. '''
  100. image = models.ForeignKey(
  101. 'wagtailimages.Image',
  102. null=True,
  103. blank=True,
  104. on_delete=models.SET_NULL,
  105. related_name='+',
  106. help_text='Location listing image'
  107. )
  108. introduction = models.TextField(
  109. help_text='Text to describe the index page',
  110. blank=True)
  111. content_panels = Page.content_panels + [
  112. ImageChooserPanel('image'),
  113. FieldPanel('introduction')
  114. ]
  115. # parent_page_types = [
  116. # 'home.HomePage'
  117. # ]
  118. # Defining what content type can sit under the parent. Since it's a blank
  119. # array no subpage can be added
  120. subpage_types = ['BlogPage']
  121. def get_context(self, request):
  122. context = super(BlogIndexPage, self).get_context(request)
  123. context['blogs'] = BlogPage.objects.descendant_of(
  124. self).live().order_by(
  125. '-first_published_at')
  126. return context
  127. @route('^tags/(\w+)/$', name='tag_archive')
  128. def tag_archive(self, request, tag=None):
  129. '''
  130. A Custom view that utilizes Tags. This view will
  131. return all related BlogPages for a given Tag.
  132. '''
  133. tag = get_object_or_404(Tag, slug=tag)
  134. blogs = get_list_or_404(
  135. BlogPage.objects.filter(tags=tag).live().descendant_of(self)
  136. )
  137. context = {
  138. 'title': 'Posts tagged with: {}'.format(tag.name),
  139. 'blogs': blogs
  140. }
  141. return render(request, 'blog/blog_index_page.html', context)
  142. # api_fields = ['introduction']