diff --git a/Makefile b/Makefile index ddf74c0..f216cad 100644 --- a/Makefile +++ b/Makefile @@ -56,7 +56,11 @@ local-collectstatic: local-shell: @ENV_PATH=envfile/.env.local uv run python app/manage.py shell -# Run django migrations +# Run django makemigrations +local-makemigrations: + @ENV_PATH=envfile/.env.local uv run python app/manage.py makemigrations + +# Run django migrate local-migrate: @ENV_PATH=envfile/.env.local uv run python app/manage.py migrate diff --git a/app/cms/__init__.py b/app/cms/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/app/cms/apps.py b/app/cms/apps.py new file mode 100644 index 0000000..6e90a36 --- /dev/null +++ b/app/cms/apps.py @@ -0,0 +1,5 @@ +from django.apps import AppConfig + + +class CmsConfig(AppConfig): + name = "cms" diff --git a/app/cms/page/__init__.py b/app/cms/page/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/app/cms/page/apps.py b/app/cms/page/apps.py new file mode 100644 index 0000000..376f8b6 --- /dev/null +++ b/app/cms/page/apps.py @@ -0,0 +1,5 @@ +from django.apps import AppConfig + + +class PageConfig(AppConfig): + name = "cms.page" diff --git a/app/cms/page/migrations/0001_initial.py b/app/cms/page/migrations/0001_initial.py new file mode 100644 index 0000000..ad8d7cd --- /dev/null +++ b/app/cms/page/migrations/0001_initial.py @@ -0,0 +1,56 @@ +# Generated by Django 5.2 on 2025-04-19 06:44 + +import django.db.models.deletion +import uuid +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ] + + operations = [ + migrations.CreateModel( + name='Page', + fields=[ + ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), + ('is_active', models.BooleanField(default=False)), + ('css', models.TextField(blank=True, default=None, null=True)), + ('title', models.CharField(max_length=256)), + ('title_ko', models.CharField(max_length=256, null=True)), + ('title_en', models.CharField(max_length=256, null=True)), + ('subtitle', models.CharField(max_length=512)), + ('subtitle_ko', models.CharField(max_length=512, null=True)), + ('subtitle_en', models.CharField(max_length=512, null=True)), + ], + ), + migrations.CreateModel( + name='Section', + fields=[ + ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), + ('order', models.IntegerField(default=0)), + ('css', models.TextField(blank=True, default=None, null=True)), + ('body', models.TextField(help_text='Content of the page, Written in markdown format')), + ('body_ko', models.TextField(help_text='Content of the page, Written in markdown format', null=True)), + ('body_en', models.TextField(help_text='Content of the page, Written in markdown format', null=True)), + ('page', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='page.page')), + ], + ), + migrations.CreateModel( + name='Sitemap', + fields=[ + ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), + ('name', models.CharField(max_length=256)), + ('name_ko', models.CharField(max_length=256, null=True)), + ('name_en', models.CharField(max_length=256, null=True)), + ('order', models.IntegerField(default=0)), + ('display_start_at', models.DateTimeField(blank=True, null=True)), + ('display_end_at', models.DateTimeField(blank=True, null=True)), + ('page', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='page.page')), + ('parent_sitemap', models.ForeignKey(blank=True, default=None, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='children', to='page.sitemap')), + ], + ), + ] diff --git a/app/cms/page/migrations/__init__.py b/app/cms/page/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/app/cms/page/models.py b/app/cms/page/models.py new file mode 100644 index 0000000..7ef5cfe --- /dev/null +++ b/app/cms/page/models.py @@ -0,0 +1,34 @@ +from django.db import models +import uuid + +class Page(models.Model): + id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) + is_active = models.BooleanField(default=False) + css = models.TextField(null=True, blank=True, default=None) + title = models.CharField(max_length=256) + subtitle = models.CharField(max_length=512) + + def __str__(self): + return f"{self.title}" + +class Sitemap(models.Model): + id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) + parent_sitemap = models.ForeignKey('self', null=True, blank=True, default=None, on_delete=models.SET_NULL, related_name='children') + name = models.CharField(max_length=256) + order = models.IntegerField(default=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) + + def __str__(self): + return f"{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) + 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') + + def __str__(self): + return f"Section {self.order} of {self.page}" \ No newline at end of file diff --git a/app/cms/page/translation.py b/app/cms/page/translation.py new file mode 100644 index 0000000..c260aa2 --- /dev/null +++ b/app/cms/page/translation.py @@ -0,0 +1,14 @@ +from modeltranslation.translator import register, TranslationOptions +from cms.page.models import Page, Sitemap, Section + +@register(Page) +class PageTranslationOptions(TranslationOptions): + fields = ('title', 'subtitle') + +@register(Sitemap) +class SitemapTranslationOptions(TranslationOptions): + fields = ('name',) + +@register(Section) +class SectionTranslationOptions(TranslationOptions): + fields = ('body',) diff --git a/app/cms/sitemap/__init__.py b/app/cms/sitemap/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/app/cms/sitemap/apps.py b/app/cms/sitemap/apps.py new file mode 100644 index 0000000..76212b9 --- /dev/null +++ b/app/cms/sitemap/apps.py @@ -0,0 +1,5 @@ +from django.apps import AppConfig + + +class SitemapConfig(AppConfig): + name = "cms.sitemap" diff --git a/app/cms/sitemap/migrations/__init__.py b/app/cms/sitemap/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/app/core/settings.py b/app/core/settings.py index c362b7d..6f7752f 100644 --- a/app/core/settings.py +++ b/app/core/settings.py @@ -147,6 +147,9 @@ # Application definition INSTALLED_APPS = [ + # django model translation: https://django-modeltranslation.readthedocs.io/en/latest/installation.html#installed-apps + "modeltranslation", + # django default apps "django.contrib.admin", "django.contrib.auth", "django.contrib.contenttypes", @@ -170,6 +173,8 @@ "django_extensions", # django-app "user", + "cms.page", + "cms.sitemap", # django-constance "constance", ] @@ -227,7 +232,6 @@ }, } - # Constance Settings CONSTANCE_BACKEND = "constance.backends.database.DatabaseBackend" CONSTANCE_CONFIG: dict[str, tuple[int, str]] = { @@ -257,6 +261,8 @@ USE_TZ = True +MODELTRANSLATION_DEFAULT_LANGUAGE = 'ko' +MODELTRANSLATION_LANGUAGES = ("ko", "en") # Static files (CSS, JavaScript, Images) # https://docs.djangoproject.com/en/5.2/howto/static-files/ diff --git a/pyproject.toml b/pyproject.toml index 134d284..09fa2fe 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -13,6 +13,7 @@ dependencies = [ "django-environ>=0.12.0", "django-extensions>=3.2.3", "django-filter>=25.1", + "django-modeltranslation>=0.19.13", "django-picklefield>=3.3", "django-simple-history>=3.8.0", "django-storages[s3]>=1.14.6", diff --git a/uv.lock b/uv.lock index e745715..0f0f4d6 100644 --- a/uv.lock +++ b/uv.lock @@ -1,4 +1,5 @@ version = 1 +revision = 1 requires-python = "==3.12.*" [[package]] @@ -111,6 +112,7 @@ dependencies = [ { name = "django-environ" }, { name = "django-extensions" }, { name = "django-filter" }, + { name = "django-modeltranslation" }, { name = "django-picklefield" }, { name = "django-simple-history" }, { name = "django-storages", extra = ["s3"] }, @@ -154,6 +156,7 @@ requires-dist = [ { name = "django-environ", specifier = ">=0.12.0" }, { name = "django-extensions", specifier = ">=3.2.3" }, { name = "django-filter", specifier = ">=25.1" }, + { name = "django-modeltranslation", specifier = ">=0.19.13" }, { name = "django-picklefield", specifier = ">=3.3" }, { name = "django-simple-history", specifier = ">=3.8.0" }, { name = "django-storages", extras = ["s3"], specifier = ">=1.14.6" }, @@ -441,6 +444,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/07/a6/70dcd68537c434ba7cb9277d403c5c829caf04f35baf5eb9458be251e382/django_filter-25.1-py3-none-any.whl", hash = "sha256:4fa48677cf5857b9b1347fed23e355ea792464e0fe07244d1fdfb8a806215b80", size = 94114 }, ] +[[package]] +name = "django-modeltranslation" +version = "0.19.13" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "django" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/44/e1/54a2f2cff0bf6c5798a0185f4b962d680999a8f0207a61f8aebb75ff8226/django_modeltranslation-0.19.13.tar.gz", hash = "sha256:df2516fdfb6a865fd1dfdda74e549d437d8ca148c1d435663db36edb6863f34e", size = 76720 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/10/a9/87ddfca789c95e8aaa7d5481d00184046cad925f943aeaee31dd21e62f0b/django_modeltranslation-0.19.13-py3-none-any.whl", hash = "sha256:69b7dc9a8bae152ab84caf969a5ad478f588f4d9f8345545d4f20f66eeb51ef6", size = 53996 }, +] + [[package]] name = "django-picklefield" version = "3.3"