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,
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={ options={"ordering": ["sort_order"], "abstract": False},
'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

@ -1,4 +1,4 @@
{% extends "base.html" %} {% extends "base.html" %}
{% load wagtailcore_tags wagtailimages_tags %} {% load wagtailcore_tags wagtailimages_tags %}
@ -6,32 +6,32 @@
{% block content %} {% block content %}
<section class="mw7 center"> <section class="mw7 center">
<h1 class="ph3 ph0-l">{{ page.title }}</h1> <h1 class="ph3 ph0-l">{{ page.title }}</h1>
{% for post in blogpages %} {% for post in blogpages %}
{% with post=post.specific %} {% with post=post.specific %}
<article class="pv4 bt bb b--black-10 ph3 ph0-l"> <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="flex flex-column flex-row-ns">
<div class="w-100 w-60-ns pr3-ns order-2 order-1-ns"> <div class="w-100 w-60-ns pr3-ns order-2 order-1-ns">
<h1 class="f3 mt0 lh-title">{{ post.title }}</h1> <h1 class="f3 mt0 lh-title">{{ post.title }}</h1>
<p class="f5 f4-l lh-copy">{{ post.intro }}</p> <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> </div>
<div class="pl3-ns order-1 order-2-ns mb4 mb0-ns w-100 w-40-ns"> <p class="f6 lh-copy gray mv0">By <span class="ttu">{{ post.owner }}</span></p>
{% with post.main_image as main_image %} <time class="f6 db gray">{{ post.date|date }}</time>
{% if main_image %}{% image main_image fill-320x240 class="db" %}{% endif %} </a>
{% endwith %} </article>
</div> {% endwith %}
</div> {% endfor %}
<p class="f6 lh-copy gray mv0">By <span class="ttu">{{ post.owner }}</span></p>
<time class="f6 db gray">{{ post.date|date }}</time> </section>
</a>
</article>
{% endwith %}
{% endfor %}
</section>
{% endblock %} {% endblock %}

@ -5,17 +5,30 @@
{% block body_class %}helvetica{% endblock %} {% block body_class %}helvetica{% endblock %}
{% block content %} {% block content %}
<h1>{{ page.title }}</h1> <h1>{{ page.title }}</h1>
<p class="meta">{{ page.date }}</p> <p class="meta">{{ page.date }}</p>
<div class="intro">{{ page.intro }}</div> <div class="intro">{{ page.intro }}</div>
{{ page.body|richtext }} {{ page.body|richtext }}
{% for item in page.gallery_images.all %} {% for item in page.gallery_images.all %}
<div class="fl ma1"> <div class="fl ma1">
{% image item.image fill-320x240 %} {% image item.image fill-320x240 %}
<p> {{ item.caption }}</p> <p> {{ item.caption }}</p>
</div> </div>
{% endfor %} {% endfor %}
<p><a href="{{ page.get_parent.url }}">Return to blog</a></p>
{% 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 %} {% 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 %}

@ -3,7 +3,7 @@
{% block body_class %}template-404{% endblock %} {% block body_class %}template-404{% endblock %}
{% block content %} {% block content %}
<h1>Page not found</h1> <h1>Page not found</h1>
<h2>Sorry, this page could not be found.</h2> <h2>Sorry, this page could not be found.</h2>
{% endblock %} {% endblock %}

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

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

Loading…
Cancel
Save