regeneration for blogposts

This commit is contained in:
Christoph J. Scherr 2023-06-04 01:53:21 +02:00
parent 08c26ce090
commit 91667a7da9
Signed by: PlexSheep
GPG Key ID: 25B4ACF7D88186CC
14 changed files with 173 additions and 123 deletions

View File

@ -1,4 +1,5 @@
from django.contrib import admin from django.contrib import admin
from django.utils.translation import gettext as _
from .models import * from .models import *
@admin.register(Category) @admin.register(Category)
@ -7,6 +8,11 @@ class CategoryAdmin(admin.ModelAdmin):
The admin model for Category The admin model for Category
""" """
@admin.action(description=_("Regenerate searchable traits"))
def regenerate(modeladmin, request, queryset):
for obj in queryset:
obj.regenerate()
@admin.register(BlogPost) @admin.register(BlogPost)
class BlogPostAdmin(admin.ModelAdmin): class BlogPostAdmin(admin.ModelAdmin):
""" """
@ -14,3 +20,5 @@ class BlogPostAdmin(admin.ModelAdmin):
""" """
list_display = ["title_en", "subtitle_en", "title_de", "subtitle_de", "date", "category", "slug", "suburl", "public"] list_display = ["title_en", "subtitle_en", "title_de", "subtitle_de", "date", "category", "slug", "suburl", "public"]
date_hierarchy = "date" date_hierarchy = "date"
ordering = ['title_de', 'title_en']
actions = [regenerate]

View File

@ -0,0 +1,23 @@
# Generated by Django 3.2.19 on 2023-06-03 22:50
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('blog', '0004_blogpost_slug'),
]
operations = [
migrations.AddField(
model_name='blogpost',
name='featured',
field=models.BooleanField(default=False),
),
migrations.AlterField(
model_name='blogpost',
name='thumbnail',
field=models.ImageField(blank=True, upload_to='img/thumbnails'),
),
]

View File

@ -0,0 +1,18 @@
# Generated by Django 3.2.19 on 2023-06-03 22:55
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('blog', '0005_auto_20230604_0050'),
]
operations = [
migrations.AddField(
model_name='blogpost',
name='markdown',
field=models.BooleanField(default=False),
),
]

View File

@ -0,0 +1,17 @@
# Generated by Django 3.2.19 on 2023-06-03 23:13
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('blog', '0006_blogpost_markdown'),
]
operations = [
migrations.RemoveField(
model_name='blogpost',
name='public',
),
]

View File

@ -1,14 +1,27 @@
from django.db import models from django.db import models
from django.utils.translation import gettext as _
from start.models import Searchable from start.models import Searchable
import logging
logger = logging.getLogger(__name__)
class Category(models.Model): class Category(models.Model):
""" """
A category of blog posts A category of blog posts
Name not translated because it would make i18n in urls and Searchables specifically a pain.
Maybe some day it would be cool if these were Searchable
""" """
name= models.CharField(max_length=50) name= models.CharField(max_length=50)
slug = models.SlugField() slug = models.SlugField()
class Meta:
verbose_name = _("Category")
verbose_name_plural = _("Categories")
def __str__(self):
return f"{{<{self.__class__.__name__}>\"{self.name}\"}}"
class BlogPost(Searchable): class BlogPost(Searchable):
""" """
Should contain a blogpost Should contain a blogpost
@ -16,5 +29,21 @@ class BlogPost(Searchable):
body = models.TextField() body = models.TextField()
category = models.ForeignKey(Category, on_delete=models.SET_NULL, null=True) category = models.ForeignKey(Category, on_delete=models.SET_NULL, null=True)
thumbnail = models.ImageField(blank=True, upload_to="img/thumbnails") thumbnail = models.ImageField(blank=True, upload_to="img/thumbnails")
public = models.BooleanField(default=True) featured = models.BooleanField(default=False)
markdown = models.BooleanField(default=False)
slug = models.SlugField() slug = models.SlugField()
def regenerate(self):
"""
regenerate a object
Implements the abstract method of Searchable
"""
logger.info(f"regenerating {self.__class__.__name__} object: {self}")
self.suburl = f"/blog/{self.category.name}/{self.slug}"
self.save()
class Meta:
verbose_name = _("blog post")
verbose_name_plural = _("blog posts")

View File

@ -2,6 +2,7 @@
<div class="container-lg mt-5"> <div class="container-lg mt-5">
<h4 class="">{% trans "Featured" %}</h4> <h4 class="">{% trans "Featured" %}</h4>
<div class="row my-4"> <div class="row my-4">
{% for post in featured_posts %}
<div class="card mx-3" style="width: 18rem;"> <div class="card mx-3" style="width: 18rem;">
<img src="https://static.cscherr.de/images/profile/profile-margin-downscaled.png" class="card-img-top" alt="..."> <img src="https://static.cscherr.de/images/profile/profile-margin-downscaled.png" class="card-img-top" alt="...">
<div class="card-body"> <div class="card-body">
@ -18,119 +19,6 @@
<a href="#" class="card-link">Another link</a> <a href="#" class="card-link">Another link</a>
</div> </div>
</div> </div>
<div class="card mx-3" style="width: 18rem;"> {% endfor %}
<img src="https://static.cscherr.de/images/profile/profile-margin-downscaled.png" class="card-img-top" alt="...">
<div class="card-body">
<h5 class="card-title">Card title</h5>
<p class="card-text">Some quick example text to build on the card title and make up the bulk of the card's content.</p>
</div>
<ul class="list-group list-group-flush">
<li class="list-group-item">An item</li>
<li class="list-group-item">A second item</li>
<li class="list-group-item">A third item</li>
</ul>
<div class="card-body">
<a href="#" class="card-link">Card link</a>
<a href="#" class="card-link">Another link</a>
</div>
</div>
<div class="card mx-3" style="width: 18rem;">
<img src="https://static.cscherr.de/images/profile/profile-margin-downscaled.png" class="card-img-top" alt="...">
<div class="card-body">
<h5 class="card-title">Card title</h5>
<p class="card-text">Some quick example text to build on the card title and make up the bulk of the card's content.</p>
</div>
<ul class="list-group list-group-flush">
<li class="list-group-item">An item</li>
<li class="list-group-item">A second item</li>
<li class="list-group-item">A third item</li>
</ul>
<div class="card-body">
<a href="#" class="card-link">Card link</a>
<a href="#" class="card-link">Another link</a>
</div>
</div>
<div class="card mx-3" style="width: 18rem;">
<img src="https://static.cscherr.de/images/profile/profile-margin-downscaled.png" class="card-img-top" alt="...">
<div class="card-body">
<h5 class="card-title">Card title</h5>
<p class="card-text">Some quick example text to build on the card title and make up the bulk of the card's content.</p>
</div>
<ul class="list-group list-group-flush">
<li class="list-group-item">An item</li>
<li class="list-group-item">A second item</li>
<li class="list-group-item">A third item</li>
</ul>
<div class="card-body">
<a href="#" class="card-link">Card link</a>
<a href="#" class="card-link">Another link</a>
</div>
</div>
</div>
<div class="row my-4">
<div class="card mx-3" style="width: 18rem;">
<img src="https://static.cscherr.de/images/profile/profile-margin-downscaled.png" class="card-img-top" alt="...">
<div class="card-body">
<h5 class="card-title">Card title</h5>
<p class="card-text">Some quick example text to build on the card title and make up the bulk of the card's content.</p>
</div>
<ul class="list-group list-group-flush">
<li class="list-group-item">An item</li>
<li class="list-group-item">A second item</li>
<li class="list-group-item">A third item</li>
</ul>
<div class="card-body">
<a href="#" class="card-link">Card link</a>
<a href="#" class="card-link">Another link</a>
</div>
</div>
<div class="card mx-3" style="width: 18rem;">
<img src="https://static.cscherr.de/images/profile/profile-margin-downscaled.png" class="card-img-top" alt="...">
<div class="card-body">
<h5 class="card-title">Card title</h5>
<p class="card-text">Some quick example text to build on the card title and make up the bulk of the card's content.</p>
</div>
<ul class="list-group list-group-flush">
<li class="list-group-item">An item</li>
<li class="list-group-item">A second item</li>
<li class="list-group-item">A third item</li>
</ul>
<div class="card-body">
<a href="#" class="card-link">Card link</a>
<a href="#" class="card-link">Another link</a>
</div>
</div>
<div class="card mx-3" style="width: 18rem;">
<img src="https://static.cscherr.de/images/profile/profile-margin-downscaled.png" class="card-img-top" alt="...">
<div class="card-body">
<h5 class="card-title">Card title</h5>
<p class="card-text">Some quick example text to build on the card title and make up the bulk of the card's content.</p>
</div>
<ul class="list-group list-group-flush">
<li class="list-group-item">An item</li>
<li class="list-group-item">A second item</li>
<li class="list-group-item">A third item</li>
</ul>
<div class="card-body">
<a href="#" class="card-link">Card link</a>
<a href="#" class="card-link">Another link</a>
</div>
</div>
<div class="card mx-3" style="width: 18rem;">
<img src="https://static.cscherr.de/images/profile/profile-margin-downscaled.png" class="card-img-top" alt="...">
<div class="card-body">
<h5 class="card-title">Card title</h5>
<p class="card-text">Some quick example text to build on the card title and make up the bulk of the card's content.</p>
</div>
<ul class="list-group list-group-flush">
<li class="list-group-item">An item</li>
<li class="list-group-item">A second item</li>
<li class="list-group-item">A third item</li>
</ul>
<div class="card-body">
<a href="#" class="card-link">Card link</a>
<a href="#" class="card-link">Another link</a>
</div>
</div>
</div> </div>
</div> </div>

View File

@ -6,7 +6,6 @@ from .models import BlogPost, Category
from start.views import SearchableView from start.views import SearchableView
import logging import logging
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
class Index(TemplateView, SearchableView): class Index(TemplateView, SearchableView):
@ -19,6 +18,12 @@ class Index(TemplateView, SearchableView):
template_name: str = "blog/index.html" template_name: str = "blog/index.html"
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['featured_posts'] = BlogPost.objects.filter(featured=True, public=True)
logger.debug(f"loaded featured posts: {context['featured_posts']}")
return context
class Post(DetailView): class Post(DetailView):
""" """
Main page of a blog post Main page of a blog post
@ -28,6 +33,12 @@ class Post(DetailView):
template_name = "blog/blogpost.html" template_name = "blog/blogpost.html"
context_object_name = "post" context_object_name = "post"
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['featured_posts'] = BlogPost.objects.filter(featured=True)
logger.debug(f"loaded featured posts: {context['featured_posts']}")
return context
def get_object(self, queryset=None): def get_object(self, queryset=None):
obj = get_object_or_404( obj = get_object_or_404(
BlogPost, BlogPost,
@ -61,6 +72,12 @@ class CategoryList(ListView):
template_name = "blog/categories.html" template_name = "blog/categories.html"
context_object_name = "categories" context_object_name = "categories"
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['featured_posts'] = BlogPost.objects.filter(featured=True)
logger.debug(f"loaded featured posts: {context['featured_posts']}")
return context
class ArticleList(ListView): class ArticleList(ListView):
""" """
Scroll through a list of blog posts Scroll through a list of blog posts
@ -72,3 +89,9 @@ class ArticleList(ListView):
model=BlogPost model=BlogPost
template_name = "blog/posts.html" template_name = "blog/posts.html"
context_object_name = "posts" context_object_name = "posts"
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['featured_posts'] = BlogPost.objects.filter(featured=True)
logger.debug(f"loaded featured posts: {context['featured_posts']}")
return context

2
gawa/migrate.sh Executable file
View File

@ -0,0 +1,2 @@
#!/bin/bash
python manage.py makemigrations && python manage.py migrate

View File

@ -1,8 +1,15 @@
from django.contrib import admin from django.contrib import admin
from django.db.models import CASCADE, AutoField, OneToOneField from django.db.models import CASCADE, AutoField, OneToOneField
from django.views.generic import View from django.views.generic import View
from django.utils.translation import gettext as _
from .models import * from .models import *
@admin.action(description=_("Regenerate searchable traits"))
def regenerate(modeladmin, request, queryset):
for obj in queryset:
obj.regenerate()
@admin.register(Keyword) @admin.register(Keyword)
class KeywordAdmin(admin.ModelAdmin): class KeywordAdmin(admin.ModelAdmin):
""" """
@ -16,3 +23,5 @@ class StaticSiteAdmin(admin.ModelAdmin):
Admin Interface for StaticSite Admin Interface for StaticSite
""" """
list_display = ["title_en", "subtitle_en", "title_de", "subtitle_de", "suburl"] list_display = ["title_en", "subtitle_en", "title_de", "subtitle_de", "suburl"]
ordering = ['title_de', 'title_en']
actions = [regenerate]

View File

@ -1,6 +1,5 @@
from django.apps import AppConfig from django.apps import AppConfig
class StartConfig(AppConfig): class StartConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField' default_auto_field = 'django.db.models.BigAutoField'
name = 'start' name = 'start'

View File

@ -0,0 +1,18 @@
# Generated by Django 3.2.19 on 2023-06-03 23:13
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('start', '0002_keyword_searchable_staticsite'),
]
operations = [
migrations.AddField(
model_name='searchable',
name='public',
field=models.BooleanField(default=True),
),
]

View File

@ -1,8 +1,8 @@
from django.db import models from django.db import models
from django.db.models.options import override from django.db.models.options import override
#from .views import SearchableView import logging
# ^^^ raises Circular Import logger = logging.getLogger(__name__)
class Keyword(models.Model): class Keyword(models.Model):
""" """
@ -11,6 +11,9 @@ class Keyword(models.Model):
text_de = models.CharField(max_length=40) text_de = models.CharField(max_length=40)
text_en = models.CharField(max_length=40) text_en = models.CharField(max_length=40)
def __str__(self):
return f"{{<{self.__class__.__name__}>\"{self.text_en}\"}}"
class Searchable(models.Model): class Searchable(models.Model):
""" """
Abstract class for any model that should be searchable. Abstract class for any model that should be searchable.
@ -28,13 +31,25 @@ class Searchable(models.Model):
date = models.DateField(blank=True, null=True) date = models.DateField(blank=True, null=True)
keywords = models.ManyToManyField(Keyword) keywords = models.ManyToManyField(Keyword)
suburl = models.CharField(max_length=200, blank=True, null=True) suburl = models.CharField(max_length=200, blank=True, null=True)
public = models.BooleanField(default=True)
@classmethod @classmethod
def regenerate_all_entries(cls): def regenerate_all_entries(cls):
""" """
regenerate all searchable items regenerate all searchable items
""" """
raise NotImplementedError logger.info(f"regenerating all {Searchable.__name__} entries")
for obj in cls.objects.all():
obj.regenerate()
def __str__(self):
return f"{{<{self.__class__.__name__}>\"{self.title_en}\"}}"
def regenerate(self):
"""
regenerate a object
"""
raise NotImplementedError("This model does not implement regenerate")
class StaticSite(Searchable): class StaticSite(Searchable):
""" """
@ -54,4 +69,4 @@ class StaticSite(Searchable):
# TODO automate searching for these # TODO automate searching for these
#all_views: list[SearchableView] = [] #all_views: list[SearchableView] = []
raise NotImplementedError raise NotImplementedError("This model does not implement regenerate_all_entries")

View File

@ -85,6 +85,7 @@ class MainSearch(ListView):
Q(subtitle_de__icontains=search) | Q(subtitle_en__icontains=search) | Q(subtitle_de__icontains=search) | Q(subtitle_en__icontains=search) |
Q(desc_de__icontains=search) | Q(desc_en__icontains=search) Q(desc_de__icontains=search) | Q(desc_en__icontains=search)
) )
object_list = object_list.filter(public=True)
print(object_list) print(object_list)
return object_list return object_list

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB