Added basic tagging to blog posts

master
androiddrew 6 years ago
parent 28a32d2f12
commit 5832137a34

@ -8,23 +8,45 @@ import modelcluster.fields
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [
('wagtailimages', '0021_image_file_hash'), ("wagtailimages", "0021_image_file_hash"),
('blog', '0002_blogpage'), ("blog", "0002_blogpage"),
] ]
operations = [ operations = [
migrations.CreateModel( migrations.CreateModel(
name='BlogPageGalleryImage', name="BlogPageGalleryImage",
fields=[ 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)), "id",
('caption', models.CharField(blank=True, max_length=250)), models.AutoField(
('image', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='+', to='wagtailimages.Image')), auto_created=True,
('page', modelcluster.fields.ParentalKey(on_delete=django.db.models.deletion.CASCADE, related_name='gallery_images', to='blog.BlogPage')), primary_key=True,
], serialize=False,
options={ verbose_name="ID",
'ordering': ['sort_order'], ),
'abstract': False, ),
}, (
"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},
)
] ]

@ -0,0 +1,33 @@
# Generated by Django 2.1.2 on 2018-11-19 19:50
from django.db import migrations, models
import django.db.models.deletion
import modelcluster.contrib.taggit
import modelcluster.fields
class Migration(migrations.Migration):
dependencies = [
('taggit', '0002_auto_20150616_2121'),
('blog', '0003_blogpagegalleryimage'),
]
operations = [
migrations.CreateModel(
name='BlogPageTag',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('content_object', modelcluster.fields.ParentalKey(on_delete=django.db.models.deletion.CASCADE, related_name='tagged_items', to='blog.BlogPage')),
('tag', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='blog_blogpagetag_items', to='taggit.Tag')),
],
options={
'abstract': False,
},
),
migrations.AddField(
model_name='blogpage',
name='tags',
field=modelcluster.contrib.taggit.ClusterTaggableManager(blank=True, help_text='A comma-separated list of tags.', through='blog.BlogPageTag', to='taggit.Tag', verbose_name='Tags'),
),
]

@ -0,0 +1,25 @@
# Generated by Django 2.1.2 on 2018-11-19 20:04
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('wagtailcore', '0040_page_draft_title'),
('blog', '0004_auto_20181119_1950'),
]
operations = [
migrations.CreateModel(
name='BlogTagIndexPage',
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')),
],
options={
'abstract': False,
},
bases=('wagtailcore.page',),
),
]

@ -1,10 +1,12 @@
from django.db import models from django.db import models
from modelcluster.fields import ParentalKey from modelcluster.fields import ParentalKey
from modelcluster.contrib.taggit import ClusterTaggableManager
from taggit.models import TaggedItemBase
from wagtail.core.models import Page, Orderable from wagtail.core.models import Page, Orderable
from wagtail.core.fields import RichTextField from wagtail.core.fields import RichTextField
from wagtail.admin.edit_handlers import FieldPanel, InlinePanel from wagtail.admin.edit_handlers import FieldPanel, InlinePanel, MultiFieldPanel
from wagtail.images.edit_handlers import ImageChooserPanel from wagtail.images.edit_handlers import ImageChooserPanel
from wagtail.search import index from wagtail.search import index
@ -27,10 +29,21 @@ class BlogIndexPage(Page):
return context return context
class BlogPageTag(TaggedItemBase):
"""Provides a simple tagging feature to Blog Pages"""
content_object = ParentalKey(
"BlogPage", related_name="tagged_items", on_delete=models.CASCADE
)
class BlogPage(Page): class BlogPage(Page):
"""Models a blog page entry."""
date = models.DateField("Post date") date = models.DateField("Post date")
intro = models.CharField(max_length=250) intro = models.CharField(max_length=250)
body = RichTextField(blank=True) body = RichTextField(blank=True)
tags = ClusterTaggableManager(through=BlogPageTag, blank=True)
def main_image(self): def main_image(self):
"""Returns the first image associated with a `BlogPage` from `BlogPageGalleryImage`""" """Returns the first image associated with a `BlogPage` from `BlogPageGalleryImage`"""
@ -46,7 +59,10 @@ class BlogPage(Page):
] ]
content_panels = Page.content_panels + [ content_panels = Page.content_panels + [
FieldPanel("date"), # Grouping Date and tags together for readability in the Admin panel
MultiFieldPanel(
[FieldPanel("date"), FieldPanel("tags")], heading="Blog information"
),
FieldPanel("intro"), FieldPanel("intro"),
FieldPanel("body", classname="full"), FieldPanel("body", classname="full"),
InlinePanel("gallery_images", label="Gallery images"), InlinePanel("gallery_images", label="Gallery images"),
@ -67,13 +83,35 @@ class BlogPageGalleryImage(Orderable):
This allows for the same image to exist in multiple galleries - effectively creating a many-to-many relationship This allows for the same image to exist in multiple galleries - effectively creating a many-to-many relationship
between pages and images. between pages and images.
""" """
page = ParentalKey( page = ParentalKey(
BlogPage, on_delete=models.CASCADE, related_name="gallery_images" BlogPage, on_delete=models.CASCADE, related_name="gallery_images"
) )
image = models.ForeignKey( image = models.ForeignKey(
# on_delete here means that is an image is deleted from the system, the gallery entry is deleted as well. # 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="+" "wagtailimages.Image",
on_delete=models.CASCADE,
related_name="+",
) )
caption = models.CharField(blank=True, max_length=250) caption = models.CharField(blank=True, max_length=250)
panels = [ImageChooserPanel("image"), FieldPanel("caption")] panels = [ImageChooserPanel("image"), FieldPanel("caption")]
class BlogTagIndexPage(Page):
"""
Models the Index page of blogs by tag.
Even though this model does not define any fields of its own, it is a subclass of Page and is added to the Wagtail
ecosystem. This means that it can be given a title and a URL in the admin
"""
def get_context(self, request, *args, **kwargs):
"""Filters by tage passed in request query string"""
tag = request.GET.get('tag')
blogpages = BlogPage.objects.filter(tags__name=tag)
context = super().get_context(request)
context['blogpages'] = blogpages
return context

@ -17,5 +17,18 @@
<p> {{ item.caption }}</p> <p> {{ item.caption }}</p>
</div> </div>
{% endfor %} {% endfor %}
{% if page.tags.all.count %}
<div class="tags">
<h3>Tags</h3>
{% for tag in page.tags.all %}
<a href="{% slugurl 'tags' %}?tag={{ tag }}">
<button type="button">{{ tag }}</button>
</a>
{% endfor %}
</div>
{% endif %}
<p><a href="{{ page.get_parent.url }}">Return to blog</a></p> <p><a href="{{ page.get_parent.url }}">Return to blog</a></p>
{% endblock %} {% endblock %}

@ -0,0 +1,38 @@
{% extends "base.html" %}
{% load wagtailcore_tags wagtailimages_tags %}
{% block body_class %}helvetica{% endblock %}
{% block content %}
<section class="mw7 center">
{% if request.GET.tag|length %}
<h1 class="ph3 ph0-l">Showing pages tagged "{{ request.GET.tag }}"</h1>
{% endif %}
{% for post in blogpages %}
{% with post=post.specific %}
<article class="pv4 bt bb b--black-10 ph3 ph0-l">
<a class="db pv4 ph3 ph0-l no-underline black dim" href="{% pageurl post %}">
<div class="flex flex-column flex-row-ns">
<div class="w-100 w-60-ns pr3-ns order-2 order-1-ns">
<h1 class="f3 mt0 lh-title">{{ post.title }}</h1>
<p class="f5 f4-l lh-copy">{{ post.intro }}</p>
</div>
<div class="pl3-ns order-1 order-2-ns mb4 mb0-ns w-100 w-40-ns">
{% with post.main_image as main_image %}
{% if main_image %}{% image main_image fill-320x240 class="db" %}{% endif %}
{% endwith %}
</div>
</div>
<p class="f6 lh-copy gray mv0">By <span class="ttu">{{ post.owner }}</span></p>
<time class="f6 db gray">{{ post.date|date }}</time>
</a>
</article>
{% endwith %}
{% empty %}
No pages found with that tag.
{% endfor %}
</section>
{% endblock %}
Loading…
Cancel
Save