models.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351
  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.wagtailadmin.edit_handlers import (
  6. FieldPanel, FieldRowPanel, InlinePanel, MultiFieldPanel, PageChooserPanel, StreamFieldPanel,
  7. )
  8. from wagtail.wagtailcore.fields import RichTextField, StreamField
  9. from wagtail.wagtailcore.models import Collection, Page
  10. from wagtail.wagtailforms.models import AbstractEmailForm, AbstractFormField
  11. from wagtail.wagtailimages.edit_handlers import ImageChooserPanel
  12. from wagtail.wagtailsearch import index
  13. from wagtail.wagtailsnippets.models import register_snippet
  14. from .blocks import BaseStreamBlock
  15. class BasePageFieldsMixin(models.Model):
  16. """
  17. An abstract base class for common fields
  18. """
  19. introduction = models.TextField(
  20. help_text='Text to describe the page',
  21. blank=True)
  22. image = models.ForeignKey(
  23. 'wagtailimages.Image',
  24. null=True,
  25. blank=True,
  26. on_delete=models.SET_NULL,
  27. related_name='+',
  28. help_text='Landscape mode only; horizontal width between 1000px and 3000px.'
  29. )
  30. # The StreamField is defined within base/blocks.py
  31. body = StreamField(
  32. BaseStreamBlock(), verbose_name="Page body", blank=True
  33. )
  34. # The content_panels makes the fields accessible to the editor within the
  35. # admin area. If you don't include a field here it won't be displayed
  36. content_panels = Page.content_panels + [
  37. FieldPanel('introduction', classname="full"),
  38. ImageChooserPanel('image'),
  39. StreamFieldPanel('body'),
  40. ]
  41. class Meta:
  42. abstract = True
  43. @register_snippet
  44. class People(ClusterableModel):
  45. """
  46. A Django model to store People objects.
  47. It uses the `@register_snippet` decorator to allow it to be accessible
  48. as via the Snippets UI (e.g. /admin/snippets/base/people/)
  49. `People` uses the `ClusterableModel`, which allows the relationship to another
  50. model (e.g. a PageModel) to be stored independently of the database. This
  51. allows us to preview foreignKey relationships without first saving the parent
  52. https://github.com/wagtail/django-modelcluster
  53. """
  54. first_name = models.CharField("First name", max_length=254)
  55. last_name = models.CharField("Last name", max_length=254)
  56. job_title = models.CharField("Job title", max_length=254)
  57. image = models.ForeignKey(
  58. 'wagtailimages.Image',
  59. null=True,
  60. blank=True,
  61. on_delete=models.SET_NULL,
  62. related_name='+'
  63. )
  64. panels = [
  65. FieldPanel('first_name', classname="col6"),
  66. FieldPanel('last_name', classname="col6"),
  67. FieldPanel('job_title'),
  68. ImageChooserPanel('image')
  69. ]
  70. search_fields = Page.search_fields + [
  71. index.SearchField('first_name'),
  72. index.SearchField('last_name'),
  73. ]
  74. @property
  75. def thumb_image(self):
  76. # Return an empty string if there is no profile pic or the rendition
  77. # file can't be found.
  78. try:
  79. return self.image.get_rendition('fill-50x50').img_tag()
  80. except:
  81. return ''
  82. def __str__(self):
  83. return '{} {}'.format(self.first_name, self.last_name)
  84. class Meta:
  85. verbose_name = 'Person'
  86. verbose_name_plural = 'People'
  87. @register_snippet
  88. class FooterText(models.Model):
  89. """
  90. This provides editable text for the site footer. Again it uses the decorator
  91. `register_snippet` to allow it to be accessible via the admin. It is made
  92. accessible on the template via a template tag defined in base/templatetags/
  93. navigation_tags.py
  94. """
  95. body = RichTextField()
  96. panels = [
  97. FieldPanel('body'),
  98. ]
  99. def __str__(self):
  100. return "Footer text"
  101. class Meta:
  102. verbose_name_plural = 'Footer Text'
  103. class StandardPage(Page, BasePageFieldsMixin):
  104. """
  105. A generic content page. On this demo site we use it for an about page but
  106. could be used for any type of page content.
  107. """
  108. # Inherit the content panels from BasePageFieldsMixin
  109. content_panels = BasePageFieldsMixin.content_panels + []
  110. class HomePage(Page):
  111. """
  112. The Home Page. This looks slightly more complicated than it is. You can
  113. see if you visit your site and edit the homepage that it is split between
  114. a:
  115. - Hero area
  116. - Body area
  117. - A promotional area
  118. - Moveable featured site sections
  119. """
  120. # Hero section of HomePage
  121. image = models.ForeignKey(
  122. 'wagtailimages.Image',
  123. null=True,
  124. blank=True,
  125. on_delete=models.SET_NULL,
  126. related_name='+',
  127. help_text='Homepage image'
  128. )
  129. hero_text = models.CharField(
  130. max_length=255,
  131. help_text='Write an introduction for the bakery'
  132. )
  133. hero_cta = models.CharField(
  134. verbose_name='Hero CTA',
  135. max_length=255,
  136. help_text='Text to display on Call to Action'
  137. )
  138. hero_cta_link = models.ForeignKey(
  139. 'wagtailcore.Page',
  140. null=True,
  141. blank=True,
  142. on_delete=models.SET_NULL,
  143. related_name='+',
  144. verbose_name='Hero CTA link',
  145. help_text='Choose a page to link to for the Call to Action'
  146. )
  147. # Body section of the HomePage
  148. body = StreamField(
  149. BaseStreamBlock(), verbose_name="Home content block", blank=True
  150. )
  151. # Promo section of the HomePage
  152. promo_image = models.ForeignKey(
  153. 'wagtailimages.Image',
  154. null=True,
  155. blank=True,
  156. on_delete=models.SET_NULL,
  157. related_name='+',
  158. help_text='Promo image'
  159. )
  160. promo_title = models.CharField(
  161. null=True,
  162. blank=True,
  163. max_length=255,
  164. help_text='Title to display above the promo copy'
  165. )
  166. promo_text = RichTextField(
  167. null=True,
  168. blank=True,
  169. help_text='Write some promotional copy'
  170. )
  171. # Featured sections on the HomePage
  172. # You will see on templates/base/home_page.html that these are treated
  173. # in different ways, and displayed in different areas of the page.
  174. # Each list their children items that we access via the children function
  175. # that we define on the individual Page models e.g. BlogIndexPage
  176. featured_section_1_title = models.CharField(
  177. null=True,
  178. blank=True,
  179. max_length=255,
  180. help_text='Title to display above the promo copy'
  181. )
  182. featured_section_1 = models.ForeignKey(
  183. 'wagtailcore.Page',
  184. null=True,
  185. blank=True,
  186. on_delete=models.SET_NULL,
  187. related_name='+',
  188. help_text='First featured section for the homepage. Will display up to three child items.',
  189. verbose_name='Featured section 1'
  190. )
  191. featured_section_2_title = models.CharField(
  192. null=True,
  193. blank=True,
  194. max_length=255,
  195. help_text='Title to display above the promo copy'
  196. )
  197. featured_section_2 = models.ForeignKey(
  198. 'wagtailcore.Page',
  199. null=True,
  200. blank=True,
  201. on_delete=models.SET_NULL,
  202. related_name='+',
  203. help_text='Second featured section for the homepage. Will display up to three child items.',
  204. verbose_name='Featured section 2'
  205. )
  206. featured_section_3_title = models.CharField(
  207. null=True,
  208. blank=True,
  209. max_length=255,
  210. help_text='Title to display above the promo copy'
  211. )
  212. featured_section_3 = models.ForeignKey(
  213. 'wagtailcore.Page',
  214. null=True,
  215. blank=True,
  216. on_delete=models.SET_NULL,
  217. related_name='+',
  218. help_text='Third featured section for the homepage. Will display up to six child items.',
  219. verbose_name='Featured section 3'
  220. )
  221. content_panels = Page.content_panels + [
  222. MultiFieldPanel([
  223. ImageChooserPanel('image'),
  224. FieldPanel('hero_text', classname="full"),
  225. MultiFieldPanel([
  226. FieldPanel('hero_cta'),
  227. PageChooserPanel('hero_cta_link'),
  228. ])
  229. ], heading="Hero section"),
  230. MultiFieldPanel([
  231. ImageChooserPanel('promo_image'),
  232. FieldPanel('promo_title'),
  233. FieldPanel('promo_text'),
  234. ], heading="Promo section"),
  235. StreamFieldPanel('body'),
  236. MultiFieldPanel([
  237. MultiFieldPanel([
  238. FieldPanel('featured_section_1_title'),
  239. PageChooserPanel('featured_section_1'),
  240. ]),
  241. MultiFieldPanel([
  242. FieldPanel('featured_section_2_title'),
  243. PageChooserPanel('featured_section_2'),
  244. ]),
  245. MultiFieldPanel([
  246. FieldPanel('featured_section_3_title'),
  247. PageChooserPanel('featured_section_3'),
  248. ])
  249. ], heading="Featured homepage sections", classname="collapsible")
  250. ]
  251. def __str__(self):
  252. return self.title
  253. class GalleryPage(BasePageFieldsMixin, Page):
  254. """
  255. This is a page to list locations from the selected Collection. We use a Q
  256. object to list any Collection created (/admin/collections/) even if they
  257. contain no items. It's unlikely to be useful for many production settings
  258. but is intended to show the extensibility of aspect of Wagtail
  259. """
  260. collection = models.ForeignKey(
  261. Collection,
  262. limit_choices_to=~models.Q(name__in=['Root']),
  263. null=True,
  264. blank=True,
  265. on_delete=models.SET_NULL,
  266. help_text='Select the image collection for this gallery.'
  267. )
  268. content_panels = BasePageFieldsMixin.content_panels + [
  269. FieldPanel('collection'),
  270. ]
  271. # Defining what content type can sit under the parent. Since it's a blank
  272. # array no subpage can be added
  273. subpage_types = []
  274. class FormField(AbstractFormField):
  275. """
  276. Wagtailforms is a module to introduce simple forms on a Wagtail site. It
  277. isn't intended as a replacement to Django's form support but as a quick way
  278. to generate general purpose data-collection form without having to write
  279. code. We use it on the site for a contact form. You can read more about
  280. Wagtail forms at:
  281. http://docs.wagtail.io/en/v1.9/reference/contrib/forms/index.html
  282. """
  283. page = ParentalKey('FormPage', related_name='form_fields')
  284. class FormPage(AbstractEmailForm):
  285. image = models.ForeignKey(
  286. 'wagtailimages.Image',
  287. null=True,
  288. blank=True,
  289. on_delete=models.SET_NULL,
  290. related_name='+'
  291. )
  292. body = StreamField(BaseStreamBlock())
  293. thank_you_text = RichTextField(blank=True)
  294. # Note how we include the FormField object via an InlinePanel using the
  295. # related_name value
  296. content_panels = AbstractEmailForm.content_panels + [
  297. ImageChooserPanel('image'),
  298. StreamFieldPanel('body'),
  299. InlinePanel('form_fields', label="Form fields"),
  300. FieldPanel('thank_you_text', classname="full"),
  301. MultiFieldPanel([
  302. FieldRowPanel([
  303. FieldPanel('from_address', classname="col6"),
  304. FieldPanel('to_address', classname="col6"),
  305. ]),
  306. FieldPanel('subject'),
  307. ], "Email"),
  308. ]