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):
dependencies = [
('wagtailimages', '0021_image_file_hash'),
('blog', '0002_blogpage'),
("wagtailimages", "0021_image_file_hash"),
("blog", "0002_blogpage"),
]
operations = [
migrations.CreateModel(
name='BlogPageGalleryImage',
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,
},
(
"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},
)
]

@ -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 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.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.search import index
@ -27,10 +29,21 @@ class BlogIndexPage(Page):
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):
"""Models a blog page entry."""
date = models.DateField("Post date")
intro = models.CharField(max_length=250)
body = RichTextField(blank=True)
tags = ClusterTaggableManager(through=BlogPageTag, blank=True)
def main_image(self):
"""Returns the first image associated with a `BlogPage` from `BlogPageGalleryImage`"""
@ -46,7 +59,10 @@ class BlogPage(Page):
]
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("body", classname="full"),
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
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="+"
"wagtailimages.Image",
on_delete=models.CASCADE,
related_name="+",
)
caption = models.CharField(blank=True, max_length=250)
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

@ -6,13 +6,13 @@
{% block content %}
<section class="mw7 center">
<section class="mw7 center">
<h1 class="ph3 ph0-l">{{ page.title }}</h1>
{% 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 %}" >
<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>
@ -31,7 +31,7 @@
{% endwith %}
{% endfor %}
</section>
</section>
{% endblock %}

@ -17,5 +17,18 @@
<p> {{ item.caption }}</p>
</div>
{% 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>
{% 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 %}

@ -1,13 +1,13 @@
<!DOCTYPE html>
<html class="no-js">
<head>
<meta charset="utf-8" />
<head>
<meta charset="utf-8"/>
<title>Internal server error</title>
<meta name="viewport" content="width=device-width, initial-scale=1" />
</head>
<body>
<h1>Internal server error</h1>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
</head>
<body>
<h1>Internal server error</h1>
<h2>Sorry, there seems to be an error. Please try again soon.</h2>
</body>
<h2>Sorry, there seems to be an error. Please try again soon.</h2>
</body>
</html>

@ -2,8 +2,8 @@
<!DOCTYPE html>
<html class="no-js">
<head>
<meta charset="utf-8" />
<head>
<meta charset="utf-8"/>
<title>
{% block title %}
{% if self.seo_title %}{{ self.seo_title }}{% else %}{{ self.title }}{% endif %}
@ -14,8 +14,8 @@
{% endwith %}
{% endblock %}
</title>
<meta name="description" content="" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="description" content=""/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
{# Global stylesheets #}
<link rel="stylesheet" type="text/css" href="{% static 'css/tachyons.min.css' %}">
@ -24,18 +24,18 @@
{% block extra_css %}
{# Override this in templates to add extra stylesheets #}
{% endblock %}
</head>
</head>
<body class="{% block body_class %}{% endblock %}">
{% wagtailuserbar %}
<body class="{% block body_class %}{% endblock %}">
{% wagtailuserbar %}
{% block content %}{% endblock %}
{% block content %}{% endblock %}
{# Global javascript #}
<script type="text/javascript" src="{% static 'js/cms.js' %}"></script>
{# Global javascript #}
<script type="text/javascript" src="{% static 'js/cms.js' %}"></script>
{% block extra_js %}
{% block extra_js %}
{# Override this in templates to add extra javascript #}
{% endblock %}
</body>
{% endblock %}
</body>
</html>

Loading…
Cancel
Save