models.py 11 KB

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