models.py 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268
  1. from __future__ import unicode_literals
  2. from django.db import models
  3. from modelcluster.fields import ParentalKey
  4. from modelcluster.models import ClusterableModel
  5. from wagtail.contrib.modeladmin.options import (
  6. ModelAdmin, ModelAdminGroup, modeladmin_register)
  7. from wagtail.wagtailadmin.edit_handlers import (
  8. FieldPanel, FieldRowPanel, InlinePanel, MultiFieldPanel,
  9. PageChooserPanel, StreamFieldPanel,
  10. )
  11. from wagtail.wagtailcore.fields import RichTextField, StreamField
  12. from wagtail.wagtailcore.models import Collection, Orderable, Page
  13. from wagtail.wagtailforms.models import AbstractEmailForm, AbstractFormField
  14. from wagtail.wagtailimages.edit_handlers import ImageChooserPanel
  15. from wagtail.wagtailsearch import index
  16. from wagtail.wagtailsnippets.models import register_snippet
  17. from .blocks import BaseStreamBlock
  18. class BasePageFieldsMixin(models.Model):
  19. """
  20. An abstract base class for common fields
  21. """
  22. introduction = models.TextField(
  23. help_text='Text to describe the page',
  24. blank=True)
  25. image = models.ForeignKey(
  26. 'wagtailimages.Image',
  27. null=True,
  28. blank=True,
  29. on_delete=models.SET_NULL,
  30. related_name='+',
  31. help_text='Landscape mode only; horizontal width between 1000px and 3000px.'
  32. )
  33. content_panels = Page.content_panels + [
  34. FieldPanel('introduction', classname="full"),
  35. ImageChooserPanel('image'),
  36. ]
  37. class Meta:
  38. abstract = True
  39. @register_snippet
  40. class People(ClusterableModel):
  41. """
  42. `People` snippets are secondary content objects that do not require their
  43. own full webpage to render.
  44. """
  45. first_name = models.CharField("First name", max_length=254)
  46. last_name = models.CharField("Last name", max_length=254)
  47. job_title = models.CharField("Job title", max_length=254)
  48. image = models.ForeignKey(
  49. 'wagtailimages.Image',
  50. null=True,
  51. blank=True,
  52. on_delete=models.SET_NULL,
  53. related_name='+'
  54. )
  55. panels = [
  56. FieldPanel('first_name', classname="col6"),
  57. FieldPanel('last_name', classname="col6"),
  58. FieldPanel('job_title'),
  59. ImageChooserPanel('image')
  60. ]
  61. search_fields = Page.search_fields + [
  62. index.SearchField('first_name'),
  63. index.SearchField('last_name'),
  64. ]
  65. @property
  66. def thumb_image(self):
  67. # fail silently if there is no profile pic or the rendition file can't
  68. # be found. Note @richbrennan worked out how to do this...
  69. try:
  70. return self.image.get_rendition('fill-50x50').img_tag()
  71. except:
  72. return ''
  73. def __str__(self):
  74. return '{} {}'.format(self.first_name, self.last_name)
  75. class Meta:
  76. verbose_name = 'Person'
  77. verbose_name_plural = 'People'
  78. @register_snippet
  79. class FooterText(models.Model):
  80. """
  81. This provides editable text for the site footer
  82. """
  83. body = RichTextField()
  84. panels = [
  85. FieldPanel('body'),
  86. ]
  87. def __str__(self):
  88. return "Footer text"
  89. class Meta:
  90. verbose_name_plural = 'Footer Text'
  91. class AboutLocationRelationship(Orderable, models.Model):
  92. """
  93. This defines the relationship between the `LocationPage` within the `locations`
  94. app and the About page below allowing us to add locations to the about
  95. section.
  96. """
  97. page = ParentalKey(
  98. 'AboutPage', related_name='location_about_relationship'
  99. )
  100. locations = models.ForeignKey(
  101. 'locations.LocationPage', related_name='about_location_relationship'
  102. )
  103. panels = [
  104. PageChooserPanel('locations')
  105. ]
  106. class AboutPage(Page):
  107. """
  108. The About Page
  109. """
  110. image = models.ForeignKey(
  111. 'wagtailimages.Image',
  112. null=True,
  113. blank=True,
  114. on_delete=models.SET_NULL,
  115. related_name='+',
  116. help_text='About image'
  117. )
  118. body = StreamField(
  119. BaseStreamBlock(), verbose_name="About page detail", blank=True
  120. )
  121. # We've defined the StreamBlock() within blocks.py that we've imported on
  122. # line 12. Defining it in a different file gives us consistency across the
  123. # site, though StreamFields _can_ be created on a per model basis if you
  124. # have a use case for it
  125. content_panels = Page.content_panels + [
  126. ImageChooserPanel('image'),
  127. StreamFieldPanel('body'),
  128. InlinePanel(
  129. 'location_about_relationship',
  130. label='Locations',
  131. min_num=None
  132. ),
  133. ]
  134. # parent_page_types = [
  135. # 'home.HomePage'
  136. # ]
  137. # Defining what content type can sit under the parent
  138. # The empty array means that no children can be placed under the
  139. # LocationPage page model
  140. subpage_types = []
  141. # api_fields = ['image', 'body']
  142. class HomePage(Page):
  143. """
  144. The Home Page
  145. """
  146. image = models.ForeignKey(
  147. 'wagtailimages.Image',
  148. null=True,
  149. blank=True,
  150. on_delete=models.SET_NULL,
  151. related_name='+',
  152. help_text='Homepage image'
  153. )
  154. body = StreamField(
  155. BaseStreamBlock(), verbose_name="Home page detail", blank=True
  156. )
  157. content_panels = Page.content_panels + [
  158. ImageChooserPanel('image'),
  159. StreamFieldPanel('body'),
  160. ]
  161. def __str__(self):
  162. return self.title
  163. class GalleryPage(BasePageFieldsMixin, Page):
  164. """
  165. This is a page to list locations from the selected Collection
  166. """
  167. collection = models.ForeignKey(
  168. Collection,
  169. limit_choices_to=~models.Q(name__in=['Root']),
  170. null=True,
  171. blank=True,
  172. on_delete=models.SET_NULL,
  173. help_text='Select the image collection for this gallery.'
  174. )
  175. content_panels = BasePageFieldsMixin.content_panels + [
  176. FieldPanel('collection'),
  177. ]
  178. # Defining what content type can sit under the parent. Since it's a blank
  179. # array no subpage can be added
  180. subpage_types = [
  181. ]
  182. class FormField(AbstractFormField):
  183. page = ParentalKey('FormPage', related_name='form_fields')
  184. class FormPage(AbstractEmailForm):
  185. header_image = models.ForeignKey(
  186. 'wagtailimages.Image',
  187. null=True,
  188. blank=True,
  189. on_delete=models.SET_NULL,
  190. related_name='+'
  191. )
  192. body = StreamField(BaseStreamBlock())
  193. thank_you_text = RichTextField(blank=True)
  194. content_panels = AbstractEmailForm.content_panels + [
  195. ImageChooserPanel('header_image'),
  196. StreamFieldPanel('body'),
  197. InlinePanel('form_fields', label="Form fields"),
  198. FieldPanel('thank_you_text', classname="full"),
  199. MultiFieldPanel([
  200. FieldRowPanel([
  201. FieldPanel('from_address', classname="col6"),
  202. FieldPanel('to_address', classname="col6"),
  203. ]),
  204. FieldPanel('subject'),
  205. ], "Email"),
  206. ]
  207. class PeopleModelAdmin(ModelAdmin):
  208. model = People
  209. menu_label = 'People' # ditch this to use verbose_name_plural from model
  210. menu_icon = 'fa-people' # change as required
  211. list_display = ('first_name', 'last_name', 'job_title', 'thumb_image')
  212. class MyModelAdminGroup(ModelAdminGroup):
  213. menu_label = 'WagtailBakery'
  214. menu_icon = 'folder-open-inverse' # change as required
  215. menu_order = 200 # will put in 3rd place (000 being 1st, 100 2nd)
  216. items = (PeopleModelAdmin,)
  217. # When using a ModelAdminGroup class to group several ModelAdmin classes together,
  218. # you only need to register the ModelAdminGroup class with Wagtail:
  219. modeladmin_register(MyModelAdminGroup)