Normalize article dates and update ArticlePage model to use DateTimeField; configure S3 storage settings and update requirements for django-storages
This commit is contained in:
parent
b04ad110a6
commit
d75ea17b32
46
innovedus_cms/home/migrations/0013_alter_articlepage_date.py
Normal file
46
innovedus_cms/home/migrations/0013_alter_articlepage_date.py
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
# Generated by Django 5.2.7 on 2025-11-10 05:37
|
||||||
|
|
||||||
|
import datetime as dt
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
|
from django.db import migrations, models
|
||||||
|
from django.utils import timezone
|
||||||
|
|
||||||
|
|
||||||
|
def normalize_article_dates(apps, schema_editor):
|
||||||
|
ArticlePage = apps.get_model("home", "ArticlePage")
|
||||||
|
|
||||||
|
for page in ArticlePage.objects.all():
|
||||||
|
if page.date is None:
|
||||||
|
continue
|
||||||
|
|
||||||
|
value = page.date
|
||||||
|
if isinstance(value, dt.datetime):
|
||||||
|
base = value
|
||||||
|
elif isinstance(value, dt.date):
|
||||||
|
base = dt.datetime.combine(value, dt.time.min)
|
||||||
|
else:
|
||||||
|
continue
|
||||||
|
|
||||||
|
if settings.USE_TZ and timezone.is_naive(base):
|
||||||
|
base = timezone.make_aware(base, timezone.get_default_timezone())
|
||||||
|
|
||||||
|
normalized = base.replace(hour=0, minute=0, second=0, microsecond=0)
|
||||||
|
if normalized != page.date:
|
||||||
|
ArticlePage.objects.filter(pk=page.pk).update(date=normalized)
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('home', '0012_rename_recommended_articlepage_trending_and_more'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='articlepage',
|
||||||
|
name='date',
|
||||||
|
field=models.DateTimeField(verbose_name='Published date'),
|
||||||
|
),
|
||||||
|
migrations.RunPython(normalize_article_dates, migrations.RunPython.noop),
|
||||||
|
]
|
||||||
@ -35,7 +35,7 @@ class CategoryMixin:
|
|||||||
"title": category.title,
|
"title": category.title,
|
||||||
"items": ArticlePage.objects.child_of(category)
|
"items": ArticlePage.objects.child_of(category)
|
||||||
.live()
|
.live()
|
||||||
.order_by("-first_published_at")[:HORIZON_SIZE],
|
.order_by("-date")[:HORIZON_SIZE],
|
||||||
"url": category.url,
|
"url": category.url,
|
||||||
"layout": "horizon",
|
"layout": "horizon",
|
||||||
}
|
}
|
||||||
@ -45,7 +45,7 @@ class CategoryMixin:
|
|||||||
paginator = Paginator(
|
paginator = Paginator(
|
||||||
ArticlePage.objects.child_of(self)
|
ArticlePage.objects.child_of(self)
|
||||||
.live()
|
.live()
|
||||||
.order_by("-first_published_at"),
|
.order_by("-date"),
|
||||||
PAGE_SIZE,
|
PAGE_SIZE,
|
||||||
)
|
)
|
||||||
page_number = request.GET.get("page") if request else None
|
page_number = request.GET.get("page") if request else None
|
||||||
@ -84,7 +84,7 @@ class CategoryMixin:
|
|||||||
# No request means no pagination (e.g., homepage)
|
# No request means no pagination (e.g., homepage)
|
||||||
return {
|
return {
|
||||||
"title": latest_page.title,
|
"title": latest_page.title,
|
||||||
"items": ArticlePage.objects.live().order_by("-first_published_at")[
|
"items": ArticlePage.objects.live().order_by("-date")[
|
||||||
:BLOCK_SIZE
|
:BLOCK_SIZE
|
||||||
],
|
],
|
||||||
"url": latest_page.url,
|
"url": latest_page.url,
|
||||||
@ -92,7 +92,7 @@ class CategoryMixin:
|
|||||||
else:
|
else:
|
||||||
# Paginated view
|
# Paginated view
|
||||||
paginator = Paginator(
|
paginator = Paginator(
|
||||||
ArticlePage.objects.live().order_by("-first_published_at"), PAGE_SIZE
|
ArticlePage.objects.live().order_by("-date"), PAGE_SIZE
|
||||||
)
|
)
|
||||||
page_number = request.GET.get("page")
|
page_number = request.GET.get("page")
|
||||||
|
|
||||||
@ -111,7 +111,7 @@ class CategoryMixin:
|
|||||||
def get_trending_articles(self, request=None, exclude_ids=None):
|
def get_trending_articles(self, request=None, exclude_ids=None):
|
||||||
trending_page = TrendingPage.objects.first()
|
trending_page = TrendingPage.objects.first()
|
||||||
articles_qs = ArticlePage.objects.filter(trending=True).live().order_by(
|
articles_qs = ArticlePage.objects.filter(trending=True).live().order_by(
|
||||||
"-first_published_at"
|
"-date"
|
||||||
)
|
)
|
||||||
|
|
||||||
# Exclude specified article IDs
|
# Exclude specified article IDs
|
||||||
@ -178,7 +178,7 @@ class HomePage(Page, CategoryMixin):
|
|||||||
"url": category.url,
|
"url": category.url,
|
||||||
"items": ArticlePage.objects.descendant_of(category)
|
"items": ArticlePage.objects.descendant_of(category)
|
||||||
.live()
|
.live()
|
||||||
.order_by("-first_published_at")[:HORIZON_SIZE],
|
.order_by("-date")[:HORIZON_SIZE],
|
||||||
"layout": "horizon",
|
"layout": "horizon",
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@ -256,7 +256,7 @@ class ArticlePage(Page):
|
|||||||
related_name="+",
|
related_name="+",
|
||||||
help_text="文章內文橫幅圖片",
|
help_text="文章內文橫幅圖片",
|
||||||
)
|
)
|
||||||
date = models.DateField("Published date")
|
date = models.DateTimeField("Published date")
|
||||||
intro = models.CharField(max_length=250, blank=True)
|
intro = models.CharField(max_length=250, blank=True)
|
||||||
body = StreamField(
|
body = StreamField(
|
||||||
[
|
[
|
||||||
@ -292,7 +292,7 @@ class ArticlePage(Page):
|
|||||||
.exclude(id=self.id)
|
.exclude(id=self.id)
|
||||||
.filter(tags__id__in=tag_ids)
|
.filter(tags__id__in=tag_ids)
|
||||||
.distinct()
|
.distinct()
|
||||||
.order_by("-first_published_at")[:4]
|
.order_by("-date")[:4]
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
related_articles = ArticlePage.objects.none()
|
related_articles = ArticlePage.objects.none()
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 3.1 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 1.9 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 4.7 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 90 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 60 KiB |
@ -170,13 +170,21 @@ STATIC_ROOT = os.path.join(BASE_DIR, "static")
|
|||||||
STATIC_URL = "/static/"
|
STATIC_URL = "/static/"
|
||||||
|
|
||||||
MEDIA_ROOT = os.path.join(BASE_DIR, "media")
|
MEDIA_ROOT = os.path.join(BASE_DIR, "media")
|
||||||
MEDIA_URL = "/media/"
|
MEDIA_URL = f'{os.environ.get("AWS_S3_ENDPOINT_URL")}/{os.environ.get("AWS_STORAGE_BUCKET_NAME")}/'
|
||||||
|
|
||||||
# Default storage settings
|
# Default storage settings
|
||||||
# See https://docs.djangoproject.com/en/5.2/ref/settings/#std-setting-STORAGES
|
# See https://docs.djangoproject.com/en/5.2/ref/settings/#std-setting-STORAGES
|
||||||
STORAGES = {
|
STORAGES = {
|
||||||
"default": {
|
"default": {
|
||||||
"BACKEND": "django.core.files.storage.FileSystemStorage",
|
"BACKEND": "storages.backends.s3boto3.S3Boto3Storage",
|
||||||
|
"OPTIONS": {
|
||||||
|
"endpoint_url": os.environ.get("AWS_S3_ENDPOINT_URL"),
|
||||||
|
"access_key": os.environ.get("AWS_ACCESS_KEY_ID"),
|
||||||
|
"secret_key": os.environ.get("AWS_SECRET_ACCESS_KEY"),
|
||||||
|
"bucket_name": os.environ.get("AWS_STORAGE_BUCKET_NAME"),
|
||||||
|
"region_name": os.environ.get("AWS_S3_REGION_NAME", default="us-east-1"),
|
||||||
|
"addressing_style": "path",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
"staticfiles": {
|
"staticfiles": {
|
||||||
"BACKEND": "django.contrib.staticfiles.storage.StaticFilesStorage",
|
"BACKEND": "django.contrib.staticfiles.storage.StaticFilesStorage",
|
||||||
|
|||||||
@ -4,3 +4,4 @@ gunicorn
|
|||||||
dj-database-url
|
dj-database-url
|
||||||
psycopg[binary]
|
psycopg[binary]
|
||||||
python-dotenv
|
python-dotenv
|
||||||
|
django-storages[boto3]
|
||||||
Loading…
x
Reference in New Issue
Block a user