From 90ea2d55763ab584316c5af3c2ed9cbbe5921d9b Mon Sep 17 00:00:00 2001 From: Jordan Robinson Date: Sat, 20 Sep 2025 22:31:22 +0100 Subject: [PATCH] add tags to posts --- main/feeds.py | 6 +++ main/forms.py | 1 + main/migrations/0121_post_tags.py | 18 ++++++++ main/models.py | 7 +++ main/templates/main/api_docs.html | 2 + main/templates/main/blog_posts.html | 16 +++++++ main/templates/main/post_detail.html | 9 ++++ main/templates/main/post_form.html | 11 +++++ main/templates/main/post_list.html | 16 +++++++ main/urls.py | 1 + main/views/general.py | 65 +++++++++++++++++++++++++++- 11 files changed, 150 insertions(+), 2 deletions(-) create mode 100644 main/migrations/0121_post_tags.py diff --git a/main/feeds.py b/main/feeds.py index 39b6dc6a82614581ab18968e1b203c33a3dfe69f..3cf87c6bfd6d77c63f63d85590f105d15f04f2e7 100644 --- a/main/feeds.py +++ b/main/feeds.py @@ -107,6 +107,12 @@ class RSSBlogFeed(Feed): def item_pubdate(self, item): # set time to 00:00 because we don't store time for published_at field return datetime.combine(item.published_at, datetime.min.time()) + + def item_categories(self, item): + """ + Return a list of tags for this item, to render elements. + """ + return item.tag_list # uses your Post.tag_list property def item_extra_kwargs(self, item): """ diff --git a/main/forms.py b/main/forms.py index 8cd5fbf06a20b360e32434cbd7be85a1992b7c37..7c3e8015cfc3692847cdd9cf11bfeefbb96590bb 100644 --- a/main/forms.py +++ b/main/forms.py @@ -73,3 +73,4 @@ class APIPost(forms.Form): slug = forms.SlugField(max_length=300, required=False) body = forms.CharField(widget=forms.Textarea, required=False) published_at = forms.DateField(required=False) + tags = forms.CharField(max_length=300, required=False) diff --git a/main/migrations/0121_post_tags.py b/main/migrations/0121_post_tags.py new file mode 100644 index 0000000000000000000000000000000000000000..b4570d69ec4c41f751db66a4afb47441530aa2e4 --- /dev/null +++ b/main/migrations/0121_post_tags.py @@ -0,0 +1,18 @@ +# Generated by Django 5.2.5 on 2025-09-20 20:52 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('main', '0120_user_number_of_posts_feed'), + ] + + operations = [ + migrations.AddField( + model_name='post', + name='tags', + field=models.CharField(blank=True, default=None, max_length=300, null=True), + ), + ] diff --git a/main/models.py b/main/models.py index b5051aa228b77d2b9c263c1223cabc4bccaa9203..a4743e288a33f09f78bc57bff05cef5c81097bc8 100644 --- a/main/models.py +++ b/main/models.py @@ -264,6 +264,7 @@ class Post(models.Model): help_text="Leave blank to keep as draft/unpublished. Use a future date for auto-posting.", ) broadcasted_at = models.DateTimeField(blank=True, null=True, default=None) + tags = models.CharField(max_length=300, blank=True, null=True, default=None) class Meta: ordering = ["-published_at", "-created_at"] @@ -277,6 +278,12 @@ class Post(models.Model): def body_as_text(self): as_html = util.md_to_html(self.body) return bleach.clean(as_html, strip=True, tags=[]) + + @property + def tag_list(self): + if self.tags: + return [t.strip() for t in self.tags.split(",") if t.strip()] + return [] @property def is_draft(self): diff --git a/main/templates/main/api_docs.html b/main/templates/main/api_docs.html index ffdefb3b9b85cb5f6309855b0e04304c96673c6e..732e5977cd035b5bc6e5014ada2aeefb80d8a792 100644 --- a/main/templates/main/api_docs.html +++ b/main/templates/main/api_docs.html @@ -54,6 +54,7 @@
  • title: string [required]
  • body: string [optional]
  • published_at: string (ISO date eg. 2006-01-31) [optional]
  • +
  • tags: string [optional]
  • Request @@ -61,6 +62,7 @@ "title": "New blog", "body": "## Why?\n\nEveryone wants a blog, right?", "published_at": "2020-09-21" + "tags": "life, tech, coding", } Response diff --git a/main/templates/main/blog_posts.html b/main/templates/main/blog_posts.html index 69a14092af54e830d5a131d5a5dfa3a7f0cdbd8d..ff1cf18afcb8ed4009cb22ceb46fe5b1f4a41221 100644 --- a/main/templates/main/blog_posts.html +++ b/main/templates/main/blog_posts.html @@ -43,6 +43,12 @@

    {% if blog_user.posts_page_title %}{{ blog_user.posts_page_title }}{% else %}Posts{% endif %}

    + {% if filter_tag %} +

    + Active tag filter: {{ filter_tag }} (clear filter) +

    + {% endif %} + {% if request.user.is_authenticated and request.subdomain == request.user.username and drafts %}
    @@ -63,8 +69,18 @@ {% if p.published_at %}
  • {{ p.title }} + {% if p.tag_list %} + + — + Tags: + {% for tag in p.tag_list %} + {{ tag }}{% if not forloop.last %}, {% endif %} + {% endfor %} + + {% endif %} — + Published on: diff --git a/main/templates/main/post_detail.html b/main/templates/main/post_detail.html index c656a02118c59b8ed3acb601acef191d383af5be..6e5a6b187efacd9dbef760b01e307150d55a316d 100644 --- a/main/templates/main/post_detail.html +++ b/main/templates/main/post_detail.html @@ -63,6 +63,15 @@
    {{ post.body_as_html|safe }}
    + + {% if post.tag_list %} +
    +

    Tags: + {% for tag in post.tag_list %} + {{ tag }}{% if not forloop.last %}, {% endif %} + {% endfor %} +

    + {% endif %} diff --git a/main/templates/main/post_form.html b/main/templates/main/post_form.html index 79cbf1c9deb18db1108870dbf0c84bbb5abef446..993f1c852655d46b4f15ef1b23d77cf56bd7656e 100644 --- a/main/templates/main/post_form.html +++ b/main/templates/main/post_form.html @@ -61,6 +61,17 @@ {{ form.published_at.help_text }}

    +

    + + {% if form.tags.errors %} + {% for error in form.tags.errors %} + {{ error|escape }}
    + {% endfor %} + {% endif %} + + {{ form.tags.help_text }} +

    +

    {% if form.body.errors %} diff --git a/main/templates/main/post_list.html b/main/templates/main/post_list.html index cd3c43f6dd16529c3a7973f37ddb1ece21d97e69..d19b84d9c768581351cbead4ac64a0a8b2b9a8b1 100644 --- a/main/templates/main/post_list.html +++ b/main/templates/main/post_list.html @@ -9,6 +9,11 @@ Create a new post »

    {% if post_list %} + {% if filter_tag %} +

    + Active tag filter: {{ filter_tag }} (clear filter) +

    + {% endif %}

    List of posts:

    @@ -18,7 +23,18 @@ {{ post.title }} + {% if p.tag_list %} + + — + Tags: + {% for tag in p.tag_list %} + {{ tag }}{% if not forloop.last %}, {% endif %} + {% endfor %} + + {% endif %} + — + Published on: {% if post.is_published %}