Add tags on articles and list view

This commit is contained in:
Gabriel Augendre 2021-03-04 18:24:22 +01:00
parent 8d5edfd8df
commit 4ddf916364
6 changed files with 60 additions and 7 deletions

View file

@ -0,0 +1,40 @@
# Generated by Django 3.1.5 on 2021-03-04 17:17
from django.db import migrations, models
from django.utils.text import slugify
def forwards(apps, schema_editor):
Tag = apps.get_model("articles", "Tag")
db_alias = schema_editor.connection.alias
tags = Tag.objects.using(db_alias).all()
for tag in tags:
tag.slug = slugify(tag.name)
Tag.objects.bulk_update(tags, ["slug"])
def backwards(apps, schema_editor):
Tag = apps.get_model("articles", "Tag")
db_alias = schema_editor.connection.alias
Tag.objects.using(db_alias).update(slug="")
class Migration(migrations.Migration):
dependencies = [
("articles", "0029_auto_20210303_1711"),
]
operations = [
migrations.AddField(
model_name="tag",
name="slug",
field=models.CharField(blank=True, max_length=255),
),
migrations.RunPython(forwards, backwards),
migrations.AlterField(
model_name="tag",
name="slug",
field=models.CharField(max_length=255, unique=True),
),
]

View file

@ -25,6 +25,7 @@ class User(AbstractUser):
class Tag(models.Model): class Tag(models.Model):
name = models.CharField(max_length=255, unique=True) name = models.CharField(max_length=255, unique=True)
slug = models.CharField(max_length=255, unique=True)
class Meta: class Meta:
ordering = ["name"] ordering = ["name"]

View file

@ -20,7 +20,7 @@ footer > :first-child {
margin-top: 1em; margin-top: 1em;
} }
nav a:not(:first-child):before { nav a:not(:first-child):before, a.tag:not(:first-of-type):before {
content: '\00B7'; content: '\00B7';
margin: 0 5px; margin: 0 5px;
color: var(--nc-tx-1); color: var(--nc-tx-1);

View file

@ -2,4 +2,7 @@
Published on {% include "articles/snippets/datetime.html" %} Published on {% include "articles/snippets/datetime.html" %}
· {{ article.get_read_time }} min read · {{ article.get_read_time }} min read
{% include "articles/snippets/admin_link.html" %} {% include "articles/snippets/admin_link.html" %}
{% if article.tags.all %}
<br>{% for tag in article.tags.all %}<a href="{% url "tag" slug=tag.slug %}" class="tag">{{ tag.name }}</a>{% endfor %}
{% endif %}
</p> </p>

View file

@ -6,6 +6,7 @@ urlpatterns = [
path("", html.ArticlesListView.as_view(), name="articles-list"), path("", html.ArticlesListView.as_view(), name="articles-list"),
path("drafts/", html.DraftsListView.as_view(), name="drafts-list"), path("drafts/", html.DraftsListView.as_view(), name="drafts-list"),
path("search/", html.SearchArticlesListView.as_view(), name="search"), path("search/", html.SearchArticlesListView.as_view(), name="search"),
path("tag/<slug:slug>/", html.TagArticlesListView.as_view(), name="tag"),
path("feed/", feeds.CompleteFeed(), name="complete-feed"), path("feed/", feeds.CompleteFeed(), name="complete-feed"),
path("api/render/<int:article_pk>/", api.render_article, name="api-render-article"), path("api/render/<int:article_pk>/", api.render_article, name="api-render-article"),
path("<slug:slug>/", html.ArticleDetailView.as_view(), name="article-detail"), path("<slug:slug>/", html.ArticleDetailView.as_view(), name="article-detail"),

View file

@ -5,9 +5,10 @@ from typing import Dict
from django.conf import settings from django.conf import settings
from django.contrib.auth.mixins import LoginRequiredMixin from django.contrib.auth.mixins import LoginRequiredMixin
from django.db.models import F, Q from django.db.models import F, Q
from django.shortcuts import get_object_or_404
from django.views import generic from django.views import generic
from articles.models import Article from articles.models import Article, Tag
class BaseArticleListView(generic.ListView): class BaseArticleListView(generic.ListView):
@ -41,9 +42,11 @@ class BaseArticleListView(generic.ListView):
return "&".join(map(lambda item: f"{item[0]}={item[1]}", querystring.items())) return "&".join(map(lambda item: f"{item[0]}={item[1]}", querystring.items()))
class ArticlesListView(BaseArticleListView): class PublicArticleListView(BaseArticleListView):
queryset = Article.objects.filter(status=Article.PUBLISHED) queryset = Article.objects.filter(status=Article.PUBLISHED)
class ArticlesListView(PublicArticleListView):
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs) context = super().get_context_data(**kwargs)
home_article = Article.objects.filter( home_article = Article.objects.filter(
@ -53,8 +56,7 @@ class ArticlesListView(BaseArticleListView):
return context return context
class SearchArticlesListView(BaseArticleListView): class SearchArticlesListView(PublicArticleListView):
queryset = Article.objects.filter(status=Article.PUBLISHED)
template_name = "articles/article_search.html" template_name = "articles/article_search.html"
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
@ -85,6 +87,12 @@ class SearchArticlesListView(BaseArticleListView):
return {} return {}
class TagArticlesListView(PublicArticleListView):
def get_queryset(self):
tag = get_object_or_404(Tag, slug=self.kwargs.get("slug"))
return super().get_queryset().filter(tags=tag)
class DraftsListView(LoginRequiredMixin, BaseArticleListView): class DraftsListView(LoginRequiredMixin, BaseArticleListView):
queryset = Article.objects.filter(status=Article.DRAFT) queryset = Article.objects.filter(status=Article.DRAFT)
@ -103,9 +111,9 @@ class ArticleDetailView(generic.DetailView):
def get_queryset(self): def get_queryset(self):
key = self.request.GET.get("draft_key") key = self.request.GET.get("draft_key")
if key: if key:
return Article.objects.filter(draft_key=key) return Article.objects.filter(draft_key=key).prefetch_related("tags")
queryset = super().get_queryset() queryset = super().get_queryset().prefetch_related("tags")
if not self.request.user.is_authenticated: if not self.request.user.is_authenticated:
queryset = queryset.filter(status=Article.PUBLISHED) queryset = queryset.filter(status=Article.PUBLISHED)
return queryset return queryset