diff --git a/app/cms/migrations/0004_alter_section_options_alter_sitemap_options_and_more.py b/app/cms/migrations/0004_alter_section_options_alter_sitemap_options_and_more.py new file mode 100644 index 0000000..7a02de7 --- /dev/null +++ b/app/cms/migrations/0004_alter_section_options_alter_sitemap_options_and_more.py @@ -0,0 +1,335 @@ +# Generated by Django 5.2 on 2025-05-18 06:54 + +import django.db.models.deletion +import django.utils.timezone +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("cms", "0003_alter_historicalsitemap_order_alter_sitemap_order"), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.AlterModelOptions(name="section", options={"ordering": ["order"]}), + migrations.AlterModelOptions(name="sitemap", options={"ordering": ["order"]}), + migrations.RemoveField(model_name="historicalpage", name="is_active"), + migrations.RemoveField(model_name="page", name="is_active"), + migrations.AddField( + model_name="historicalpage", + name="created_at", + field=models.DateTimeField(blank=True, default=django.utils.timezone.now, editable=False), + preserve_default=False, + ), + migrations.AddField( + model_name="historicalpage", + name="created_by", + field=models.ForeignKey( + blank=True, + db_constraint=False, + null=True, + on_delete=django.db.models.deletion.DO_NOTHING, + related_name="+", + to=settings.AUTH_USER_MODEL, + ), + ), + migrations.AddField( + model_name="historicalpage", + name="deleted_at", + field=models.DateTimeField(blank=True, null=True), + ), + migrations.AddField( + model_name="historicalpage", + name="deleted_by", + field=models.ForeignKey( + blank=True, + db_constraint=False, + null=True, + on_delete=django.db.models.deletion.DO_NOTHING, + related_name="+", + to=settings.AUTH_USER_MODEL, + ), + ), + migrations.AddField( + model_name="historicalpage", + name="updated_at", + field=models.DateTimeField(blank=True, default=django.utils.timezone.now, editable=False), + preserve_default=False, + ), + migrations.AddField( + model_name="historicalpage", + name="updated_by", + field=models.ForeignKey( + blank=True, + db_constraint=False, + null=True, + on_delete=django.db.models.deletion.DO_NOTHING, + related_name="+", + to=settings.AUTH_USER_MODEL, + ), + ), + migrations.AddField( + model_name="historicalsection", + name="created_at", + field=models.DateTimeField(blank=True, default=django.utils.timezone.now, editable=False), + preserve_default=False, + ), + migrations.AddField( + model_name="historicalsection", + name="created_by", + field=models.ForeignKey( + blank=True, + db_constraint=False, + null=True, + on_delete=django.db.models.deletion.DO_NOTHING, + related_name="+", + to=settings.AUTH_USER_MODEL, + ), + ), + migrations.AddField( + model_name="historicalsection", + name="deleted_at", + field=models.DateTimeField(blank=True, null=True), + ), + migrations.AddField( + model_name="historicalsection", + name="deleted_by", + field=models.ForeignKey( + blank=True, + db_constraint=False, + null=True, + on_delete=django.db.models.deletion.DO_NOTHING, + related_name="+", + to=settings.AUTH_USER_MODEL, + ), + ), + migrations.AddField( + model_name="historicalsection", + name="updated_at", + field=models.DateTimeField(blank=True, default=django.utils.timezone.now, editable=False), + preserve_default=False, + ), + migrations.AddField( + model_name="historicalsection", + name="updated_by", + field=models.ForeignKey( + blank=True, + db_constraint=False, + null=True, + on_delete=django.db.models.deletion.DO_NOTHING, + related_name="+", + to=settings.AUTH_USER_MODEL, + ), + ), + migrations.AddField( + model_name="historicalsitemap", + name="created_at", + field=models.DateTimeField(blank=True, default=django.utils.timezone.now, editable=False), + preserve_default=False, + ), + migrations.AddField( + model_name="historicalsitemap", + name="created_by", + field=models.ForeignKey( + blank=True, + db_constraint=False, + null=True, + on_delete=django.db.models.deletion.DO_NOTHING, + related_name="+", + to=settings.AUTH_USER_MODEL, + ), + ), + migrations.AddField( + model_name="historicalsitemap", + name="deleted_at", + field=models.DateTimeField(blank=True, null=True), + ), + migrations.AddField( + model_name="historicalsitemap", + name="deleted_by", + field=models.ForeignKey( + blank=True, + db_constraint=False, + null=True, + on_delete=django.db.models.deletion.DO_NOTHING, + related_name="+", + to=settings.AUTH_USER_MODEL, + ), + ), + migrations.AddField( + model_name="historicalsitemap", + name="updated_at", + field=models.DateTimeField(blank=True, default=django.utils.timezone.now, editable=False), + preserve_default=False, + ), + migrations.AddField( + model_name="historicalsitemap", + name="updated_by", + field=models.ForeignKey( + blank=True, + db_constraint=False, + null=True, + on_delete=django.db.models.deletion.DO_NOTHING, + related_name="+", + to=settings.AUTH_USER_MODEL, + ), + ), + migrations.AddField( + model_name="page", + name="created_at", + field=models.DateTimeField(auto_now_add=True, default=django.utils.timezone.now), + preserve_default=False, + ), + migrations.AddField( + model_name="page", + name="created_by", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.PROTECT, + related_name="%(class)s_created_by", + to=settings.AUTH_USER_MODEL, + ), + ), + migrations.AddField( + model_name="page", + name="deleted_at", + field=models.DateTimeField(blank=True, null=True), + ), + migrations.AddField( + model_name="page", + name="deleted_by", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.PROTECT, + related_name="%(class)s_deleted_by", + to=settings.AUTH_USER_MODEL, + ), + ), + migrations.AddField( + model_name="page", + name="updated_at", + field=models.DateTimeField(auto_now=True), + ), + migrations.AddField( + model_name="page", + name="updated_by", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.PROTECT, + related_name="%(class)s_updated_by", + to=settings.AUTH_USER_MODEL, + ), + ), + migrations.AddField( + model_name="section", + name="created_at", + field=models.DateTimeField(auto_now_add=True, default=django.utils.timezone.now), + preserve_default=False, + ), + migrations.AddField( + model_name="section", + name="created_by", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.PROTECT, + related_name="%(class)s_created_by", + to=settings.AUTH_USER_MODEL, + ), + ), + migrations.AddField( + model_name="section", + name="deleted_at", + field=models.DateTimeField(blank=True, null=True), + ), + migrations.AddField( + model_name="section", + name="deleted_by", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.PROTECT, + related_name="%(class)s_deleted_by", + to=settings.AUTH_USER_MODEL, + ), + ), + migrations.AddField( + model_name="section", + name="updated_at", + field=models.DateTimeField(auto_now=True), + ), + migrations.AddField( + model_name="section", + name="updated_by", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.PROTECT, + related_name="%(class)s_updated_by", + to=settings.AUTH_USER_MODEL, + ), + ), + migrations.AddField( + model_name="sitemap", + name="created_at", + field=models.DateTimeField(auto_now_add=True, default=django.utils.timezone.now), + preserve_default=False, + ), + migrations.AddField( + model_name="sitemap", + name="created_by", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.PROTECT, + related_name="%(class)s_created_by", + to=settings.AUTH_USER_MODEL, + ), + ), + migrations.AddField( + model_name="sitemap", + name="deleted_at", + field=models.DateTimeField(blank=True, null=True), + ), + migrations.AddField( + model_name="sitemap", + name="deleted_by", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.PROTECT, + related_name="%(class)s_deleted_by", + to=settings.AUTH_USER_MODEL, + ), + ), + migrations.AddField( + model_name="sitemap", + name="updated_at", + field=models.DateTimeField(auto_now=True), + ), + migrations.AddField( + model_name="sitemap", + name="updated_by", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.PROTECT, + related_name="%(class)s_updated_by", + to=settings.AUTH_USER_MODEL, + ), + ), + migrations.AlterField( + model_name="section", + name="page", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, related_name="sections", to="cms.page" + ), + ), + migrations.AlterField( + model_name="sitemap", + name="parent_sitemap", + field=models.ForeignKey( + default=None, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="children", + to="cms.sitemap", + ), + ), + ] diff --git a/app/cms/models.py b/app/cms/models.py index 431368c..b7ce393 100644 --- a/app/cms/models.py +++ b/app/cms/models.py @@ -1,44 +1,59 @@ -import uuid +import datetime +import typing +from core.models import BaseAbstractModel, BaseAbstractModelQuerySet from django.core.validators import MinValueValidator from django.db import models -class Page(models.Model): - id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) - is_active = models.BooleanField(default=False) +class Page(BaseAbstractModel): css = models.TextField(null=True, blank=True, default=None) title = models.CharField(max_length=256) subtitle = models.CharField(max_length=512) - # history = HistoricalRecords() def __str__(self): return str(self.title) -class Sitemap(models.Model): - id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) +class SitemapQuerySet(BaseAbstractModelQuerySet): + def filter_by_today(self) -> typing.Self: + now = datetime.datetime.now() + return self.filter( + models.Q(display_start_at__isnull=True) | models.Q(display_start_at__lte=now), + models.Q(display_end_at__isnull=True) | models.Q(display_end_at__gte=now), + ) + + +class Sitemap(BaseAbstractModel): parent_sitemap = models.ForeignKey( - "self", null=True, blank=True, default=None, on_delete=models.SET_NULL, related_name="children" + "self", null=True, default=None, on_delete=models.SET_NULL, related_name="children" ) + name = models.CharField(max_length=256) order = models.IntegerField(default=0, validators=[MinValueValidator(0)]) page = models.ForeignKey(Page, on_delete=models.PROTECT) + display_start_at = models.DateTimeField(null=True, blank=True) display_end_at = models.DateTimeField(null=True, blank=True) - # history = HistoricalRecords() + + objects: SitemapQuerySet = SitemapQuerySet.as_manager() + + class Meta: + ordering = ["order"] def __str__(self): return str(self.name) -class Section(models.Model): - id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) - page = models.ForeignKey(Page, on_delete=models.CASCADE) +class Section(BaseAbstractModel): + page = models.ForeignKey(Page, on_delete=models.CASCADE, related_name="sections") order = models.IntegerField(default=0) + css = models.TextField(null=True, blank=True, default=None) body = models.TextField(help_text="Content of the page, Written in markdown format") - # history = HistoricalRecords() + + class Meta: + ordering = ["order"] def __str__(self): return f"Section {self.order} of {self.page}" diff --git a/app/cms/urls.py b/app/cms/urls.py index 08930ac..878e6ca 100644 --- a/app/cms/urls.py +++ b/app/cms/urls.py @@ -20,7 +20,7 @@ from rest_framework import routers cms_router = routers.SimpleRouter() -cms_router.register("sitemap", views.SitemapListRetrieveViewSet, basename="cms-sitemap") -cms_router.register("page", views.PageListRetrieveViewSet, basename="cms-page") +cms_router.register("sitemap", views.SitemapViewSet, basename="cms-sitemap") +cms_router.register("page", views.PageViewSet, basename="cms-page") urlpatterns = [path("", include(cms_router.urls))] diff --git a/app/cms/views.py b/app/cms/views.py index c48956a..f7cd7a6 100644 --- a/app/cms/views.py +++ b/app/cms/views.py @@ -1,13 +1,13 @@ from cms.models import Page, Sitemap from cms.serializers import PageSerializer, SitemapSerializer -from rest_framework.viewsets import ReadOnlyModelViewSet +from rest_framework import mixins, viewsets -class SitemapListRetrieveViewSet(ReadOnlyModelViewSet): +class SitemapViewSet(mixins.ListModelMixin, viewsets.GenericViewSet): serializer_class = SitemapSerializer - queryset = Sitemap.objects.all() + queryset = Sitemap.objects.filter_active().filter_by_today() -class PageListRetrieveViewSet(ReadOnlyModelViewSet): +class PageViewSet(mixins.RetrieveModelMixin, viewsets.GenericViewSet): serializer_class = PageSerializer - queryset = Page.objects.all() + queryset = Page.objects.filter_active().prefetch_related("sections") diff --git a/app/core/models.py b/app/core/models.py index 7676c5e..cb834a1 100644 --- a/app/core/models.py +++ b/app/core/models.py @@ -1,5 +1,4 @@ import collections.abc -import datetime import typing import uuid @@ -25,11 +24,11 @@ def filter_active(self) -> typing.Self: class BaseAbstractModel(models.Model): - id = models.UUIDField[uuid.UUID, uuid.UUID](primary_key=True, default=uuid.uuid4, editable=False) + id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) - created_at = models.DateTimeField[datetime.datetime, datetime.datetime](auto_now_add=True) - updated_at = models.DateTimeField[datetime.datetime, datetime.datetime](auto_now=True) - deleted_at = models.DateTimeField[datetime.datetime, datetime.datetime](null=True, blank=True) + created_at = models.DateTimeField(auto_now_add=True) + updated_at = models.DateTimeField(auto_now=True) + deleted_at = models.DateTimeField(null=True, blank=True) created_by = models.ForeignKey["UserExt", "UserExt"]( User, on_delete=models.PROTECT, null=True, related_name="%(class)s_created_by"