models.py 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446
  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.panels import FieldPanel, FieldRowPanel, InlinePanel, MultiFieldPanel
  6. from wagtail.contrib.forms.models import AbstractEmailForm, AbstractFormField
  7. from wagtail.fields import RichTextField, StreamField
  8. from wagtail.models import Collection, Page
  9. from wagtail.search import index
  10. from wagtail.snippets.models import register_snippet
  11. from wagtail_editable_help.models import HelpText
  12. from .blocks import BaseStreamBlock
  13. @register_snippet
  14. class People(index.Indexed, ClusterableModel):
  15. """
  16. A Django model to store People objects.
  17. It uses the `@register_snippet` decorator to allow it to be accessible
  18. via the Snippets UI (e.g. /admin/snippets/base/people/)
  19. `People` uses the `ClusterableModel`, which allows the relationship with
  20. another model to be stored locally to the 'parent' model (e.g. a PageModel)
  21. until the parent is explicitly saved. This allows the editor to use the
  22. 'Preview' button, to preview the content, without saving the relationships
  23. to the database.
  24. https://github.com/wagtail/django-modelcluster
  25. """
  26. first_name = models.CharField("First name", max_length=254)
  27. last_name = models.CharField("Last name", max_length=254)
  28. job_title = models.CharField("Job title", max_length=254)
  29. image = models.ForeignKey(
  30. "wagtailimages.Image",
  31. null=True,
  32. blank=True,
  33. on_delete=models.SET_NULL,
  34. related_name="+",
  35. )
  36. panels = [
  37. MultiFieldPanel(
  38. [
  39. FieldRowPanel(
  40. [
  41. FieldPanel("first_name", classname="col6"),
  42. FieldPanel("last_name", classname="col6"),
  43. ]
  44. )
  45. ],
  46. "Name",
  47. ),
  48. FieldPanel("job_title"),
  49. FieldPanel("image"),
  50. ]
  51. search_fields = [
  52. index.SearchField("first_name"),
  53. index.SearchField("last_name"),
  54. ]
  55. @property
  56. def thumb_image(self):
  57. # Returns an empty string if there is no profile pic or the rendition
  58. # file can't be found.
  59. try:
  60. return self.image.get_rendition("fill-50x50").img_tag()
  61. except: # noqa: E722 FIXME: remove bare 'except:'
  62. return ""
  63. def __str__(self):
  64. return "{} {}".format(self.first_name, self.last_name)
  65. class Meta:
  66. verbose_name = "Person"
  67. verbose_name_plural = "People"
  68. @register_snippet
  69. class FooterText(models.Model):
  70. """
  71. This provides editable text for the site footer. Again it uses the decorator
  72. `register_snippet` to allow it to be accessible via the admin. It is made
  73. accessible on the template via a template tag defined in base/templatetags/
  74. navigation_tags.py
  75. """
  76. body = RichTextField()
  77. panels = [
  78. FieldPanel("body"),
  79. ]
  80. def __str__(self):
  81. return "Footer text"
  82. class Meta:
  83. verbose_name_plural = "Footer Text"
  84. class StandardPage(Page):
  85. """
  86. A generic content page. On this demo site we use it for an about page but
  87. it could be used for any type of page content that only needs a title,
  88. image, introduction and body field
  89. """
  90. introduction = models.TextField(
  91. help_text=HelpText(
  92. "Standard page", "introduction", default="Text to describe the page"
  93. ),
  94. blank=True,
  95. )
  96. image = models.ForeignKey(
  97. "wagtailimages.Image",
  98. null=True,
  99. blank=True,
  100. on_delete=models.SET_NULL,
  101. related_name="+",
  102. help_text=HelpText(
  103. "Common",
  104. "hero image",
  105. default="Landscape mode only; horizontal width between 1000px and 3000px.",
  106. ),
  107. )
  108. body = StreamField(
  109. BaseStreamBlock(), verbose_name="Page body", blank=True, use_json_field=True
  110. )
  111. content_panels = Page.content_panels + [
  112. FieldPanel("introduction", classname="full"),
  113. FieldPanel("body"),
  114. FieldPanel("image"),
  115. ]
  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=HelpText(
  134. "Common",
  135. "hero image",
  136. default="Landscape mode only; horizontal width between 1000px and 3000px.",
  137. ),
  138. )
  139. hero_text = models.CharField(
  140. max_length=255,
  141. help_text=HelpText(
  142. "Home page", "hero text", default="Write an introduction for the bakery"
  143. ),
  144. )
  145. hero_cta = models.CharField(
  146. verbose_name="Hero CTA",
  147. max_length=255,
  148. help_text=HelpText(
  149. "Home page", "hero CTA", default="Text to display on Call to Action"
  150. ),
  151. )
  152. hero_cta_link = models.ForeignKey(
  153. "wagtailcore.Page",
  154. null=True,
  155. blank=True,
  156. on_delete=models.SET_NULL,
  157. related_name="+",
  158. verbose_name="Hero CTA link",
  159. help_text=HelpText(
  160. "Home page",
  161. "CTA link",
  162. default="Choose a page to link to for the Call to Action",
  163. ),
  164. )
  165. # Body section of the HomePage
  166. body = StreamField(
  167. BaseStreamBlock(),
  168. verbose_name="Home content block",
  169. blank=True,
  170. use_json_field=True,
  171. )
  172. # Promo section of the HomePage
  173. promo_image = models.ForeignKey(
  174. "wagtailimages.Image",
  175. null=True,
  176. blank=True,
  177. on_delete=models.SET_NULL,
  178. related_name="+",
  179. help_text=HelpText("Home page", "promo image", default="Promo image"),
  180. )
  181. promo_title = models.CharField(
  182. blank=True,
  183. max_length=255,
  184. help_text=HelpText(
  185. "Home page", "promo title", default="Title to display above the promo copy"
  186. ),
  187. )
  188. promo_text = RichTextField(
  189. null=True,
  190. blank=True,
  191. help_text=HelpText(
  192. "Home page", "promo text", default="Write some promotional copy"
  193. ),
  194. )
  195. # Featured sections on the HomePage
  196. # You will see on templates/base/home_page.html that these are treated
  197. # in different ways, and displayed in different areas of the page.
  198. # Each list their children items that we access via the children function
  199. # that we define on the individual Page models e.g. BlogIndexPage
  200. featured_section_1_title = models.CharField(
  201. blank=True,
  202. max_length=255,
  203. help_text=HelpText(
  204. "Home page",
  205. "featured section title 1",
  206. default="Title to display above the promo copy",
  207. ),
  208. )
  209. featured_section_1 = models.ForeignKey(
  210. "wagtailcore.Page",
  211. null=True,
  212. blank=True,
  213. on_delete=models.SET_NULL,
  214. related_name="+",
  215. help_text=HelpText(
  216. "Home page",
  217. "featured section 1",
  218. default="First featured section for the homepage. Will display up to three child items.",
  219. ),
  220. verbose_name="Featured section 1",
  221. )
  222. featured_section_2_title = models.CharField(
  223. blank=True,
  224. max_length=255,
  225. help_text=HelpText(
  226. "Home page",
  227. "featured section title 2",
  228. default="Title to display above the promo copy",
  229. ),
  230. )
  231. featured_section_2 = models.ForeignKey(
  232. "wagtailcore.Page",
  233. null=True,
  234. blank=True,
  235. on_delete=models.SET_NULL,
  236. related_name="+",
  237. help_text=HelpText(
  238. "Home page",
  239. "featured section 2",
  240. default="Second featured section for the homepage. Will display up to three child items.",
  241. ),
  242. verbose_name="Featured section 2",
  243. )
  244. featured_section_3_title = models.CharField(
  245. blank=True,
  246. max_length=255,
  247. help_text=HelpText(
  248. "Home page",
  249. "featured section title 3",
  250. default="Title to display above the promo copy",
  251. ),
  252. )
  253. featured_section_3 = models.ForeignKey(
  254. "wagtailcore.Page",
  255. null=True,
  256. blank=True,
  257. on_delete=models.SET_NULL,
  258. related_name="+",
  259. help_text=HelpText(
  260. "Home page",
  261. "featured section 3",
  262. default="Third featured section for the homepage. Will display up to six child items.",
  263. ),
  264. verbose_name="Featured section 3",
  265. )
  266. content_panels = Page.content_panels + [
  267. MultiFieldPanel(
  268. [
  269. FieldPanel("image"),
  270. FieldPanel("hero_text", classname="full"),
  271. MultiFieldPanel(
  272. [
  273. FieldPanel("hero_cta"),
  274. FieldPanel("hero_cta_link"),
  275. ]
  276. ),
  277. ],
  278. heading="Hero section",
  279. ),
  280. MultiFieldPanel(
  281. [
  282. FieldPanel("promo_image"),
  283. FieldPanel("promo_title"),
  284. FieldPanel("promo_text"),
  285. ],
  286. heading="Promo section",
  287. ),
  288. FieldPanel("body"),
  289. MultiFieldPanel(
  290. [
  291. MultiFieldPanel(
  292. [
  293. FieldPanel("featured_section_1_title"),
  294. FieldPanel("featured_section_1"),
  295. ]
  296. ),
  297. MultiFieldPanel(
  298. [
  299. FieldPanel("featured_section_2_title"),
  300. FieldPanel("featured_section_2"),
  301. ]
  302. ),
  303. MultiFieldPanel(
  304. [
  305. FieldPanel("featured_section_3_title"),
  306. FieldPanel("featured_section_3"),
  307. ]
  308. ),
  309. ],
  310. heading="Featured homepage sections",
  311. classname="collapsible",
  312. ),
  313. ]
  314. def __str__(self):
  315. return self.title
  316. class GalleryPage(Page):
  317. """
  318. This is a page to list locations from the selected Collection. We use a Q
  319. object to list any Collection created (/admin/collections/) even if they
  320. contain no items. In this demo we use it for a GalleryPage,
  321. and is intended to show the extensibility of this aspect of Wagtail
  322. """
  323. introduction = models.TextField(
  324. help_text=HelpText(
  325. "Gallery page", "introduction", default="Text to describe the page"
  326. ),
  327. blank=True,
  328. )
  329. image = models.ForeignKey(
  330. "wagtailimages.Image",
  331. null=True,
  332. blank=True,
  333. on_delete=models.SET_NULL,
  334. related_name="+",
  335. help_text=HelpText(
  336. "Common",
  337. "hero image",
  338. default="Landscape mode only; horizontal width between 1000px and 3000px.",
  339. ),
  340. )
  341. body = StreamField(
  342. BaseStreamBlock(), verbose_name="Page body", blank=True, use_json_field=True
  343. )
  344. collection = models.ForeignKey(
  345. Collection,
  346. limit_choices_to=~models.Q(name__in=["Root"]),
  347. null=True,
  348. blank=True,
  349. on_delete=models.SET_NULL,
  350. help_text=HelpText(
  351. "Gallery page",
  352. "collection",
  353. default="Select the image collection for this gallery.",
  354. ),
  355. )
  356. content_panels = Page.content_panels + [
  357. FieldPanel("introduction", classname="full"),
  358. FieldPanel("body"),
  359. FieldPanel("image"),
  360. FieldPanel("collection"),
  361. ]
  362. # Defining what content type can sit under the parent. Since it's a blank
  363. # array no subpage can be added
  364. subpage_types = []
  365. class FormField(AbstractFormField):
  366. """
  367. Wagtailforms is a module to introduce simple forms on a Wagtail site. It
  368. isn't intended as a replacement to Django's form support but as a quick way
  369. to generate a general purpose data-collection form or contact form
  370. without having to write code. We use it on the site for a contact form. You
  371. can read more about Wagtail forms at:
  372. https://docs.wagtail.org/en/stable/reference/contrib/forms/index.html
  373. """
  374. page = ParentalKey("FormPage", related_name="form_fields", on_delete=models.CASCADE)
  375. class FormPage(AbstractEmailForm):
  376. image = models.ForeignKey(
  377. "wagtailimages.Image",
  378. null=True,
  379. blank=True,
  380. on_delete=models.SET_NULL,
  381. related_name="+",
  382. )
  383. body = StreamField(BaseStreamBlock(), use_json_field=True)
  384. thank_you_text = RichTextField(blank=True)
  385. # Note how we include the FormField object via an InlinePanel using the
  386. # related_name value
  387. content_panels = AbstractEmailForm.content_panels + [
  388. FieldPanel("image"),
  389. FieldPanel("body"),
  390. InlinePanel("form_fields", label="Form fields"),
  391. FieldPanel("thank_you_text", classname="full"),
  392. MultiFieldPanel(
  393. [
  394. FieldRowPanel(
  395. [
  396. FieldPanel("from_address", classname="col6"),
  397. FieldPanel("to_address", classname="col6"),
  398. ]
  399. ),
  400. FieldPanel("subject"),
  401. ],
  402. "Email",
  403. ),
  404. ]