models.py 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394
  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("Standard page introduction", default="Text to describe the page"),
  92. blank=True,
  93. )
  94. image = models.ForeignKey(
  95. "wagtailimages.Image",
  96. null=True,
  97. blank=True,
  98. on_delete=models.SET_NULL,
  99. related_name="+",
  100. help_text=HelpText("Hero image", default="Landscape mode only; horizontal width between 1000px and 3000px."),
  101. )
  102. body = StreamField(
  103. BaseStreamBlock(), verbose_name="Page body", blank=True, use_json_field=True
  104. )
  105. content_panels = Page.content_panels + [
  106. FieldPanel("introduction", classname="full"),
  107. FieldPanel("body"),
  108. FieldPanel("image"),
  109. ]
  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=HelpText("Hero image", default="Landscape mode only; horizontal width between 1000px and 3000px."),
  128. )
  129. hero_text = models.CharField(
  130. max_length=255,
  131. help_text=HelpText("Home page hero text", default="Write an introduction for the bakery"),
  132. )
  133. hero_cta = models.CharField(
  134. verbose_name="Hero CTA",
  135. max_length=255,
  136. help_text=HelpText("Home page hero CTA", default="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=HelpText("Home page CTA link", default="Choose a page to link to for the Call to Action"),
  146. )
  147. # Body section of the HomePage
  148. body = StreamField(
  149. BaseStreamBlock(),
  150. verbose_name="Home content block",
  151. blank=True,
  152. use_json_field=True,
  153. )
  154. # Promo section of the HomePage
  155. promo_image = models.ForeignKey(
  156. "wagtailimages.Image",
  157. null=True,
  158. blank=True,
  159. on_delete=models.SET_NULL,
  160. related_name="+",
  161. help_text=HelpText("Home page promo image", default="Promo image"),
  162. )
  163. promo_title = models.CharField(
  164. blank=True, max_length=255,
  165. help_text=HelpText("Home page promo title", default="Title to display above the promo copy"),
  166. )
  167. promo_text = RichTextField(
  168. null=True, blank=True,
  169. help_text=HelpText("Home page promo text", default="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, max_length=255,
  178. help_text=HelpText("Home page featured section title 1", default="Title to display above the promo copy"),
  179. )
  180. featured_section_1 = models.ForeignKey(
  181. "wagtailcore.Page",
  182. null=True,
  183. blank=True,
  184. on_delete=models.SET_NULL,
  185. related_name="+",
  186. help_text=HelpText(
  187. "Home page featured section 1",
  188. default="First featured section for the homepage. Will display up to three child items."
  189. ),
  190. verbose_name="Featured section 1",
  191. )
  192. featured_section_2_title = models.CharField(
  193. blank=True, max_length=255,
  194. help_text=HelpText("Home page featured section title 2", default="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=HelpText(
  203. "Home page featured section 2",
  204. default="Second featured section for the homepage. Will display up to three child items."
  205. ),
  206. verbose_name="Featured section 2",
  207. )
  208. featured_section_3_title = models.CharField(
  209. blank=True, max_length=255,
  210. help_text=HelpText("Home page featured section title 3", default="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=HelpText(
  219. "Home page featured section 3",
  220. default="Third featured section for the homepage. Will display up to six child items."
  221. ),
  222. verbose_name="Featured section 3",
  223. )
  224. content_panels = Page.content_panels + [
  225. MultiFieldPanel(
  226. [
  227. FieldPanel("image"),
  228. FieldPanel("hero_text", classname="full"),
  229. MultiFieldPanel(
  230. [
  231. FieldPanel("hero_cta"),
  232. FieldPanel("hero_cta_link"),
  233. ]
  234. ),
  235. ],
  236. heading="Hero section",
  237. ),
  238. MultiFieldPanel(
  239. [
  240. FieldPanel("promo_image"),
  241. FieldPanel("promo_title"),
  242. FieldPanel("promo_text"),
  243. ],
  244. heading="Promo section",
  245. ),
  246. FieldPanel("body"),
  247. MultiFieldPanel(
  248. [
  249. MultiFieldPanel(
  250. [
  251. FieldPanel("featured_section_1_title"),
  252. FieldPanel("featured_section_1"),
  253. ]
  254. ),
  255. MultiFieldPanel(
  256. [
  257. FieldPanel("featured_section_2_title"),
  258. FieldPanel("featured_section_2"),
  259. ]
  260. ),
  261. MultiFieldPanel(
  262. [
  263. FieldPanel("featured_section_3_title"),
  264. FieldPanel("featured_section_3"),
  265. ]
  266. ),
  267. ],
  268. heading="Featured homepage sections",
  269. classname="collapsible",
  270. ),
  271. ]
  272. def __str__(self):
  273. return self.title
  274. class GalleryPage(Page):
  275. """
  276. This is a page to list locations from the selected Collection. We use a Q
  277. object to list any Collection created (/admin/collections/) even if they
  278. contain no items. In this demo we use it for a GalleryPage,
  279. and is intended to show the extensibility of this aspect of Wagtail
  280. """
  281. introduction = models.TextField(
  282. help_text=HelpText("Gallery page introduction", default="Text to describe the page"),
  283. blank=True)
  284. image = models.ForeignKey(
  285. "wagtailimages.Image",
  286. null=True,
  287. blank=True,
  288. on_delete=models.SET_NULL,
  289. related_name="+",
  290. help_text=HelpText("Hero image", default="Landscape mode only; horizontal width between 1000px and 3000px."),
  291. )
  292. body = StreamField(
  293. BaseStreamBlock(), verbose_name="Page body", blank=True, use_json_field=True
  294. )
  295. collection = models.ForeignKey(
  296. Collection,
  297. limit_choices_to=~models.Q(name__in=["Root"]),
  298. null=True,
  299. blank=True,
  300. on_delete=models.SET_NULL,
  301. help_text=HelpText("Gallery page collection", default="Select the image collection for this gallery."),
  302. )
  303. content_panels = Page.content_panels + [
  304. FieldPanel("introduction", classname="full"),
  305. FieldPanel("body"),
  306. FieldPanel("image"),
  307. FieldPanel("collection"),
  308. ]
  309. # Defining what content type can sit under the parent. Since it's a blank
  310. # array no subpage can be added
  311. subpage_types = []
  312. class FormField(AbstractFormField):
  313. """
  314. Wagtailforms is a module to introduce simple forms on a Wagtail site. It
  315. isn't intended as a replacement to Django's form support but as a quick way
  316. to generate a general purpose data-collection form or contact form
  317. without having to write code. We use it on the site for a contact form. You
  318. can read more about Wagtail forms at:
  319. https://docs.wagtail.org/en/stable/reference/contrib/forms/index.html
  320. """
  321. page = ParentalKey("FormPage", related_name="form_fields", on_delete=models.CASCADE)
  322. class FormPage(AbstractEmailForm):
  323. image = models.ForeignKey(
  324. "wagtailimages.Image",
  325. null=True,
  326. blank=True,
  327. on_delete=models.SET_NULL,
  328. related_name="+",
  329. )
  330. body = StreamField(BaseStreamBlock(), use_json_field=True)
  331. thank_you_text = RichTextField(blank=True)
  332. # Note how we include the FormField object via an InlinePanel using the
  333. # related_name value
  334. content_panels = AbstractEmailForm.content_panels + [
  335. FieldPanel("image"),
  336. FieldPanel("body"),
  337. InlinePanel("form_fields", label="Form fields"),
  338. FieldPanel("thank_you_text", classname="full"),
  339. MultiFieldPanel(
  340. [
  341. FieldRowPanel(
  342. [
  343. FieldPanel("from_address", classname="col6"),
  344. FieldPanel("to_address", classname="col6"),
  345. ]
  346. ),
  347. FieldPanel("subject"),
  348. ],
  349. "Email",
  350. ),
  351. ]