Replace keywords by a Tag model

This commit is contained in:
Gabriel Augendre 2021-03-03 17:30:38 +01:00
parent 5722903301
commit 26cc694008
6 changed files with 123 additions and 20 deletions

View file

@ -1,11 +1,9 @@
import copy
from django.contrib import admin, messages from django.contrib import admin, messages
from django.contrib.admin import register from django.contrib.admin import register
from django.contrib.auth.admin import UserAdmin from django.contrib.auth.admin import UserAdmin
from django.shortcuts import redirect from django.shortcuts import redirect
from .models import Article, User from .models import Article, Tag, User
admin.site.register(User, UserAdmin) admin.site.register(User, UserAdmin)
@ -40,7 +38,7 @@ class ArticleAdmin(admin.ModelAdmin):
{ {
"fields": [ "fields": [
("title", "slug"), ("title", "slug"),
("author", "keywords"), ("author", "tags"),
("status", "published_at"), ("status", "published_at"),
("created_at", "updated_at"), ("created_at", "updated_at"),
("views_count", "read_time"), ("views_count", "read_time"),
@ -71,7 +69,8 @@ class ArticleAdmin(admin.ModelAdmin):
] ]
prepopulated_fields = {"slug": ("title",)} prepopulated_fields = {"slug": ("title",)}
change_form_template = "articles/article_change_form.html" change_form_template = "articles/article_change_form.html"
search_fields = ["title", "content"] search_fields = ["title", "content", "tags__name"]
autocomplete_fields = ["tags"]
def publish(self, request, queryset): def publish(self, request, queryset):
if not request.user.has_perm("articles.change_article"): if not request.user.has_perm("articles.change_article"):
@ -133,3 +132,9 @@ class ArticleAdmin(admin.ModelAdmin):
return bool(instance.custom_css) return bool(instance.custom_css)
has_custom_css.boolean = True has_custom_css.boolean = True
@register(Tag)
class TagAdmin(admin.ModelAdmin):
list_display = ["name"]
search_fields = ["name"]

View file

@ -0,0 +1,62 @@
# Generated by Django 3.1.5 on 2021-03-03 15:33
from django.db import migrations, models
def forwards(apps, schema_editor):
Tag = apps.get_model("articles", "Tag")
Article = apps.get_model("articles", "Article")
db_alias = schema_editor.connection.alias
articles = Article.objects.using(db_alias).all()
for article in articles:
tags = []
for keyword in list(
filter(None, map(lambda k: k.strip(), article.keywords.split(",")))
):
tag = Tag.objects.using(db_alias).filter(name__iexact=keyword).first()
if tag is None:
tag = Tag.objects.create(name=keyword)
tags.append(tag)
article.tags.set(tags)
article.keywords = ""
Article.objects.bulk_update(articles, ["keywords"])
def backwards(apps, schema_editor):
Article = apps.get_model("articles", "Article")
db_alias = schema_editor.connection.alias
articles = Article.objects.using(db_alias).all()
for article in articles:
article.keywords = ",".join(map(lambda tag: tag.name, article.tags.all()))
Article.objects.bulk_update(articles, ["keywords"])
class Migration(migrations.Migration):
dependencies = [
("articles", "0026_article_draft_key"),
]
operations = [
migrations.CreateModel(
name="Tag",
fields=[
(
"id",
models.AutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("name", models.CharField(max_length=255, unique=True)),
],
),
migrations.AddField(
model_name="article",
name="tags",
field=models.ManyToManyField(related_name="articles", to="articles.Tag"),
),
migrations.RunPython(forwards, backwards),
]

View file

@ -0,0 +1,17 @@
# Generated by Django 3.1.5 on 2021-03-03 15:52
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
("articles", "0027_auto_20210303_1633"),
]
operations = [
migrations.RemoveField(
model_name="article",
name="keywords",
),
]

View file

@ -0,0 +1,17 @@
# Generated by Django 3.1.5 on 2021-03-03 16:11
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
("articles", "0028_remove_article_keywords"),
]
operations = [
migrations.AlterModelOptions(
name="tag",
options={"ordering": ["name"]},
),
]

View file

@ -23,6 +23,16 @@ class User(AbstractUser):
pass pass
class Tag(models.Model):
name = models.CharField(max_length=255, unique=True)
class Meta:
ordering = ["name"]
def __str__(self):
return self.name
class Article(models.Model): class Article(models.Model):
DRAFT = "draft" DRAFT = "draft"
PUBLISHED = "published" PUBLISHED = "published"
@ -39,11 +49,11 @@ class Article(models.Model):
author = models.ForeignKey(User, on_delete=models.PROTECT, default=1) author = models.ForeignKey(User, on_delete=models.PROTECT, default=1)
views_count = models.IntegerField(default=0) views_count = models.IntegerField(default=0)
slug = models.SlugField(unique=True, max_length=255) slug = models.SlugField(unique=True, max_length=255)
keywords = models.CharField(max_length=255, blank=True)
has_code = models.BooleanField(default=False, blank=True) has_code = models.BooleanField(default=False, blank=True)
is_home = models.BooleanField(default=False, blank=True) is_home = models.BooleanField(default=False, blank=True)
custom_css = models.TextField(blank=True) custom_css = models.TextField(blank=True)
draft_key = models.UUIDField(default=uuid.uuid4) draft_key = models.UUIDField(default=uuid.uuid4)
tags = models.ManyToManyField(to=Tag, related_name="articles")
class Meta: class Meta:
ordering = ["-published_at"] ordering = ["-published_at"]
@ -105,22 +115,14 @@ class Article(models.Model):
@cached_property @cached_property
def get_related_articles(self): def get_related_articles(self):
related_articles = set() related_articles = set()
for keyword in self.get_formatted_keywords: for tag in self.tags.all():
potential_articles = Article.objects.filter( related_articles.update(tag.articles.all())
keywords__icontains=keyword,
status=Article.PUBLISHED,
).exclude(pk=self.pk)
for article in potential_articles:
if keyword in article.get_formatted_keywords:
related_articles.add(article)
sample_size = min([len(related_articles), 3]) sample_size = min([len(related_articles), 3])
return random.sample(related_articles, sample_size) return random.sample(related_articles, sample_size)
@cached_property @cached_property
def get_formatted_keywords(self): def keywords(self):
return list( return ", ".join(map(lambda tag: tag.name, self.tags.all()))
filter(None, map(lambda k: k.strip().lower(), self.keywords.split(",")))
)
@cached_property @cached_property
def get_minified_custom_css(self): def get_minified_custom_css(self):

View file

@ -74,9 +74,9 @@ class SearchArticlesListView(BaseArticleListView):
operator.and_, (Q(content__icontains=term) for term in search_terms) operator.and_, (Q(content__icontains=term) for term in search_terms)
) )
| reduce( | reduce(
operator.and_, (Q(keywords__icontains=term) for term in search_terms) operator.and_, (Q(tags__name__icontains=term) for term in search_terms)
) )
) ).distinct()
def get_additional_querystring_params(self) -> Dict[str, str]: def get_additional_querystring_params(self) -> Dict[str, str]:
search_expression = self.request.GET.get("s") search_expression = self.request.GET.get("s")