From 28a32d2f122bbb4db34859a28b87572e1b456a7d Mon Sep 17 00:00:00 2001 From: androiddrew Date: Mon, 19 Nov 2018 14:41:02 -0500 Subject: [PATCH] Added support for Images to be linked to Blogs --- services/cms/blog/apps.py | 2 +- services/cms/blog/migrations/0001_initial.py | 28 ++++++---- services/cms/blog/migrations/0002_blogpage.py | 33 ++++++----- .../migrations/0003_blogpagegalleryimage.py | 30 ++++++++++ services/cms/blog/models.py | 56 ++++++++++++++++--- .../blog/templates/blog/blog_index_page.html | 7 ++- .../cms/blog/templates/blog/blog_page.html | 9 ++- 7 files changed, 126 insertions(+), 39 deletions(-) create mode 100644 services/cms/blog/migrations/0003_blogpagegalleryimage.py diff --git a/services/cms/blog/apps.py b/services/cms/blog/apps.py index 7930587..1003897 100644 --- a/services/cms/blog/apps.py +++ b/services/cms/blog/apps.py @@ -2,4 +2,4 @@ from django.apps import AppConfig class BlogConfig(AppConfig): - name = 'blog' + name = "blog" diff --git a/services/cms/blog/migrations/0001_initial.py b/services/cms/blog/migrations/0001_initial.py index 7c7e6e2..cec22d9 100644 --- a/services/cms/blog/migrations/0001_initial.py +++ b/services/cms/blog/migrations/0001_initial.py @@ -9,20 +9,26 @@ class Migration(migrations.Migration): initial = True - dependencies = [ - ('wagtailcore', '0040_page_draft_title'), - ] + dependencies = [("wagtailcore", "0040_page_draft_title")] operations = [ migrations.CreateModel( - name='BlogIndexPage', + name="BlogIndexPage", fields=[ - ('page_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='wagtailcore.Page')), - ('intro', wagtail.core.fields.RichTextField(blank=True)), + ( + "page_ptr", + models.OneToOneField( + auto_created=True, + on_delete=django.db.models.deletion.CASCADE, + parent_link=True, + primary_key=True, + serialize=False, + to="wagtailcore.Page", + ), + ), + ("intro", wagtail.core.fields.RichTextField(blank=True)), ], - options={ - 'abstract': False, - }, - bases=('wagtailcore.page',), - ), + options={"abstract": False}, + bases=("wagtailcore.page",), + ) ] diff --git a/services/cms/blog/migrations/0002_blogpage.py b/services/cms/blog/migrations/0002_blogpage.py index 3b517eb..0e04e49 100644 --- a/services/cms/blog/migrations/0002_blogpage.py +++ b/services/cms/blog/migrations/0002_blogpage.py @@ -7,23 +7,28 @@ import wagtail.core.fields class Migration(migrations.Migration): - dependencies = [ - ('wagtailcore', '0040_page_draft_title'), - ('blog', '0001_initial'), - ] + dependencies = [("wagtailcore", "0040_page_draft_title"), ("blog", "0001_initial")] operations = [ migrations.CreateModel( - name='BlogPage', + name="BlogPage", fields=[ - ('page_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='wagtailcore.Page')), - ('date', models.DateField(verbose_name='Post date')), - ('intro', models.CharField(max_length=250)), - ('body', wagtail.core.fields.RichTextField(blank=True)), + ( + "page_ptr", + models.OneToOneField( + auto_created=True, + on_delete=django.db.models.deletion.CASCADE, + parent_link=True, + primary_key=True, + serialize=False, + to="wagtailcore.Page", + ), + ), + ("date", models.DateField(verbose_name="Post date")), + ("intro", models.CharField(max_length=250)), + ("body", wagtail.core.fields.RichTextField(blank=True)), ], - options={ - 'abstract': False, - }, - bases=('wagtailcore.page',), - ), + options={"abstract": False}, + bases=("wagtailcore.page",), + ) ] diff --git a/services/cms/blog/migrations/0003_blogpagegalleryimage.py b/services/cms/blog/migrations/0003_blogpagegalleryimage.py new file mode 100644 index 0000000..896a436 --- /dev/null +++ b/services/cms/blog/migrations/0003_blogpagegalleryimage.py @@ -0,0 +1,30 @@ +# Generated by Django 2.1.2 on 2018-11-16 18:45 + +from django.db import migrations, models +import django.db.models.deletion +import modelcluster.fields + + +class Migration(migrations.Migration): + + dependencies = [ + ('wagtailimages', '0021_image_file_hash'), + ('blog', '0002_blogpage'), + ] + + operations = [ + migrations.CreateModel( + name='BlogPageGalleryImage', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('sort_order', models.IntegerField(blank=True, editable=False, null=True)), + ('caption', models.CharField(blank=True, max_length=250)), + ('image', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='+', to='wagtailimages.Image')), + ('page', modelcluster.fields.ParentalKey(on_delete=django.db.models.deletion.CASCADE, related_name='gallery_images', to='blog.BlogPage')), + ], + options={ + 'ordering': ['sort_order'], + 'abstract': False, + }, + ), + ] diff --git a/services/cms/blog/models.py b/services/cms/blog/models.py index f5d40b3..2486cda 100644 --- a/services/cms/blog/models.py +++ b/services/cms/blog/models.py @@ -1,17 +1,20 @@ from django.db import models -from wagtail.core.models import Page +from modelcluster.fields import ParentalKey + +from wagtail.core.models import Page, Orderable from wagtail.core.fields import RichTextField -from wagtail.admin.edit_handlers import FieldPanel +from wagtail.admin.edit_handlers import FieldPanel, InlinePanel +from wagtail.images.edit_handlers import ImageChooserPanel from wagtail.search import index class BlogIndexPage(Page): + """Entry point of the blog. Displays its children `BlogPages`""" + intro = RichTextField(blank=True) - content_panels = Page.content_panels + [ - FieldPanel('intro', classname="full") - ] + content_panels = Page.content_panels + [FieldPanel("intro", classname="full")] def get_context(self, request, *args, **kwargs): """ @@ -29,13 +32,48 @@ class BlogPage(Page): intro = models.CharField(max_length=250) body = RichTextField(blank=True) + def main_image(self): + """Returns the first image associated with a `BlogPage` from `BlogPageGalleryImage`""" + gallery_item = self.gallery_images.first() + if gallery_item: + return gallery_item.image + else: + return None + search_fields = Page.search_fields + [ index.SearchField("intro"), - index.SearchField("body") + index.SearchField("body"), ] content_panels = Page.content_panels + [ - FieldPanel('date'), - FieldPanel('intro'), - FieldPanel('body', classname="full"), + FieldPanel("date"), + FieldPanel("intro"), + FieldPanel("body", classname="full"), + InlinePanel("gallery_images", label="Gallery images"), ] + + +class BlogPageGalleryImage(Orderable): + """ + An image gallery descendant to a `BlogPage` + + Inherits a `sort_order` field from the `Orderable` object which keeps track of image ordering. + The `ParentalKey` works similarly to a `ForeignKey` but also defines this class as a child of `BlogPage` model. + This means that the image gallery associated with a particular `BlogPage` instance is treated as a part of the page + in operations like submitting for moderation, and tracking version history. + + The `BlogPageGalleryImage.image` is a `ForeignKey to the Wagtail built-in `Image` model where images themselves are + stored. The `ImageChooserPanel` provides a popup interface for choosing an existing image or uploading a new one. + This allows for the same image to exist in multiple galleries - effectively creating a many-to-many relationship + between pages and images. + """ + page = ParentalKey( + BlogPage, on_delete=models.CASCADE, related_name="gallery_images" + ) + image = models.ForeignKey( + # on_delete here means that is an image is deleted from the system, the gallery entry is deleted as well. + "wagtailimages.Image", on_delete=models.CASCADE, related_name="+" + ) + caption = models.CharField(blank=True, max_length=250) + + panels = [ImageChooserPanel("image"), FieldPanel("caption")] diff --git a/services/cms/blog/templates/blog/blog_index_page.html b/services/cms/blog/templates/blog/blog_index_page.html index af2b890..45454f8 100644 --- a/services/cms/blog/templates/blog/blog_index_page.html +++ b/services/cms/blog/templates/blog/blog_index_page.html @@ -1,6 +1,6 @@ {% extends "base.html" %} -{% load wagtailcore_tags %} +{% load wagtailcore_tags wagtailimages_tags %} {% block body_class %}helvetica{% endblock %} @@ -19,8 +19,9 @@

{{ post.intro }}

- Photo of a dimly lit room with a computer interface terminal. + {% with post.main_image as main_image %} + {% if main_image %}{% image main_image fill-320x240 class="db" %}{% endif %} + {% endwith %}

By {{ post.owner }}

diff --git a/services/cms/blog/templates/blog/blog_page.html b/services/cms/blog/templates/blog/blog_page.html index 2b61e7d..5469e85 100644 --- a/services/cms/blog/templates/blog/blog_page.html +++ b/services/cms/blog/templates/blog/blog_page.html @@ -1,6 +1,6 @@ {% extends "base.html" %} -{% load wagtailcore_tags %} +{% load wagtailcore_tags wagtailimages_tags %} {% block body_class %}helvetica{% endblock %} @@ -10,5 +10,12 @@
{{ page.intro }}
{{ page.body|richtext }} + + {% for item in page.gallery_images.all %} +
+ {% image item.image fill-320x240 %} +

{{ item.caption }}

+
+ {% endfor %}

Return to blog

{% endblock %} \ No newline at end of file