regeneration for blogposts
This commit is contained in:
parent
08c26ce090
commit
91667a7da9
|
@ -1,4 +1,5 @@
|
|||
from django.contrib import admin
|
||||
from django.utils.translation import gettext as _
|
||||
from .models import *
|
||||
|
||||
@admin.register(Category)
|
||||
|
@ -7,6 +8,11 @@ class CategoryAdmin(admin.ModelAdmin):
|
|||
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)
|
||||
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"]
|
||||
date_hierarchy = "date"
|
||||
ordering = ['title_de', 'title_en']
|
||||
actions = [regenerate]
|
||||
|
|
|
@ -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'),
|
||||
),
|
||||
]
|
|
@ -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),
|
||||
),
|
||||
]
|
|
@ -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',
|
||||
),
|
||||
]
|
|
@ -1,14 +1,27 @@
|
|||
from django.db import models
|
||||
|
||||
from django.utils.translation import gettext as _
|
||||
from start.models import Searchable
|
||||
|
||||
import logging
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
class Category(models.Model):
|
||||
"""
|
||||
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()
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("Category")
|
||||
verbose_name_plural = _("Categories")
|
||||
|
||||
def __str__(self):
|
||||
return f"{{<{self.__class__.__name__}>\"{self.name}\"}}"
|
||||
|
||||
class BlogPost(Searchable):
|
||||
"""
|
||||
Should contain a blogpost
|
||||
|
@ -16,5 +29,21 @@ class BlogPost(Searchable):
|
|||
body = models.TextField()
|
||||
category = models.ForeignKey(Category, on_delete=models.SET_NULL, null=True)
|
||||
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()
|
||||
|
||||
|
||||
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")
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
<div class="container-lg mt-5">
|
||||
<h4 class="">{% trans "Featured" %}</h4>
|
||||
<div class="row my-4">
|
||||
{% for post in featured_posts %}
|
||||
<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">
|
||||
|
@ -18,119 +19,6 @@
|
|||
<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 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>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -6,7 +6,6 @@ from .models import BlogPost, Category
|
|||
from start.views import SearchableView
|
||||
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
class Index(TemplateView, SearchableView):
|
||||
|
@ -19,6 +18,12 @@ class Index(TemplateView, SearchableView):
|
|||
|
||||
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):
|
||||
"""
|
||||
Main page of a blog post
|
||||
|
@ -28,6 +33,12 @@ class Post(DetailView):
|
|||
template_name = "blog/blogpost.html"
|
||||
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):
|
||||
obj = get_object_or_404(
|
||||
BlogPost,
|
||||
|
@ -61,6 +72,12 @@ class CategoryList(ListView):
|
|||
template_name = "blog/categories.html"
|
||||
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):
|
||||
"""
|
||||
Scroll through a list of blog posts
|
||||
|
@ -72,3 +89,9 @@ class ArticleList(ListView):
|
|||
model=BlogPost
|
||||
template_name = "blog/posts.html"
|
||||
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
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
#!/bin/bash
|
||||
python manage.py makemigrations && python manage.py migrate
|
|
@ -1,8 +1,15 @@
|
|||
from django.contrib import admin
|
||||
from django.db.models import CASCADE, AutoField, OneToOneField
|
||||
from django.views.generic import View
|
||||
from django.utils.translation import gettext as _
|
||||
from .models import *
|
||||
|
||||
|
||||
@admin.action(description=_("Regenerate searchable traits"))
|
||||
def regenerate(modeladmin, request, queryset):
|
||||
for obj in queryset:
|
||||
obj.regenerate()
|
||||
|
||||
@admin.register(Keyword)
|
||||
class KeywordAdmin(admin.ModelAdmin):
|
||||
"""
|
||||
|
@ -16,3 +23,5 @@ class StaticSiteAdmin(admin.ModelAdmin):
|
|||
Admin Interface for StaticSite
|
||||
"""
|
||||
list_display = ["title_en", "subtitle_en", "title_de", "subtitle_de", "suburl"]
|
||||
ordering = ['title_de', 'title_en']
|
||||
actions = [regenerate]
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class StartConfig(AppConfig):
|
||||
default_auto_field = 'django.db.models.BigAutoField'
|
||||
name = 'start'
|
||||
|
|
|
@ -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),
|
||||
),
|
||||
]
|
|
@ -1,8 +1,8 @@
|
|||
from django.db import models
|
||||
from django.db.models.options import override
|
||||
|
||||
#from .views import SearchableView
|
||||
# ^^^ raises Circular Import
|
||||
import logging
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
class Keyword(models.Model):
|
||||
"""
|
||||
|
@ -11,6 +11,9 @@ class Keyword(models.Model):
|
|||
text_de = 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):
|
||||
"""
|
||||
Abstract class for any model that should be searchable.
|
||||
|
@ -28,13 +31,25 @@ class Searchable(models.Model):
|
|||
date = models.DateField(blank=True, null=True)
|
||||
keywords = models.ManyToManyField(Keyword)
|
||||
suburl = models.CharField(max_length=200, blank=True, null=True)
|
||||
public = models.BooleanField(default=True)
|
||||
|
||||
@classmethod
|
||||
def regenerate_all_entries(cls):
|
||||
"""
|
||||
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):
|
||||
"""
|
||||
|
@ -54,4 +69,4 @@ class StaticSite(Searchable):
|
|||
# TODO automate searching for these
|
||||
#all_views: list[SearchableView] = []
|
||||
|
||||
raise NotImplementedError
|
||||
raise NotImplementedError("This model does not implement regenerate_all_entries")
|
||||
|
|
|
@ -85,6 +85,7 @@ class MainSearch(ListView):
|
|||
Q(subtitle_de__icontains=search) | Q(subtitle_en__icontains=search) |
|
||||
Q(desc_de__icontains=search) | Q(desc_en__icontains=search)
|
||||
)
|
||||
object_list = object_list.filter(public=True)
|
||||
print(object_list)
|
||||
return object_list
|
||||
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 33 KiB |
Loading…
Reference in New Issue