models.py 11 KB

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