feat: add mock content initialization command
This commit is contained in:
1
apps/content/management/commands/__init__.py
Normal file
1
apps/content/management/commands/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
|
||||
182
apps/content/management/commands/seed_mock_content.py
Normal file
182
apps/content/management/commands/seed_mock_content.py
Normal file
@@ -0,0 +1,182 @@
|
||||
from django.contrib.admin.models import CHANGE, LogEntry
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.core.management.base import BaseCommand
|
||||
from django.db import DEFAULT_DB_ALIAS, connections, transaction
|
||||
|
||||
from apps.content.current import AboutContent, AboutItem, HomeContent, ListingContent, SiteContent
|
||||
|
||||
|
||||
SITE_MOCK = {
|
||||
"official_name": "نام انجمن نمونه",
|
||||
"short_name": "انجمن نمونه",
|
||||
"faculty_subline": "دانشکده نمونه",
|
||||
"description": "این متن نمونه است و پس از ورود محتوای اصلی از پنل مدیریت جایگزین میشود.",
|
||||
"logo": "",
|
||||
"logo_alt": "",
|
||||
"nav_home": "خانه",
|
||||
"nav_about": "درباره ما",
|
||||
"nav_events": "رویدادها",
|
||||
"nav_blog": "بلاگ",
|
||||
"email": "contact@example.invalid",
|
||||
"phone": "0000000000",
|
||||
"telegram_url": "https://example.invalid/telegram",
|
||||
"instagram_url": "https://example.invalid/instagram",
|
||||
"website_url": "https://example.invalid",
|
||||
"address": "نشانی نمونه",
|
||||
"footer_copy": "متن نمونه پابرگ؛ محتوای نهایی را از پنل مدیریت وارد کنید.",
|
||||
"seo_title": "عنوان نمونه سایت",
|
||||
"seo_description": "توضیحات نمونه برای موتورهای جستوجو.",
|
||||
"seo_image": "",
|
||||
"seo_image_alt": "",
|
||||
}
|
||||
|
||||
HOME_MOCK = {
|
||||
"title": "عنوان نمونه صفحه اصلی",
|
||||
"intro": "این متن نمونه صفحه اصلی است و پس از ورود محتوای اصلی جایگزین میشود.",
|
||||
"hero_heading": "عنوان نمونه صفحه اصلی",
|
||||
"hero_body": "توضیحات نمونه برای معرفی انجمن و فعالیتهای آن.",
|
||||
"hero_image": "",
|
||||
"hero_image_alt": "",
|
||||
"hero_action_label": "مشاهده نمونهها",
|
||||
"featured_events_heading": "رویدادهای نمونه",
|
||||
"featured_events_intro": "توضیحات نمونه برای بخش رویدادها.",
|
||||
"featured_events_link_label": "همه رویدادها",
|
||||
"featured_posts_heading": "نوشتههای نمونه",
|
||||
"featured_posts_intro": "توضیحات نمونه برای بخش نوشتهها.",
|
||||
"featured_posts_link_label": "همه نوشتهها",
|
||||
"participation_heading": "به ما بپیوندید",
|
||||
"participation_body": "این متن نمونه راههای مشارکت را توضیح میدهد.",
|
||||
"participation_image": "",
|
||||
"participation_image_alt": "",
|
||||
"participation_action_label": "اطلاعات بیشتر",
|
||||
"seo_title": "عنوان نمونه صفحه اصلی",
|
||||
"seo_description": "توضیحات نمونه صفحه اصلی برای موتورهای جستوجو.",
|
||||
"seo_image": "",
|
||||
"seo_image_alt": "",
|
||||
}
|
||||
|
||||
ABOUT_MOCK = {
|
||||
"title": "عنوان نمونه صفحه درباره ما",
|
||||
"intro": "این متن نمونه معرفی انجمن است و بعداً از پنل مدیریت قابل ویرایش است.",
|
||||
"identity_heading": "معرفی نمونه",
|
||||
"identity_body": "اطلاعات نمونه درباره هویت انجمن.",
|
||||
"mission_heading": "ماموریت نمونه",
|
||||
"mission_body": "توضیحات نمونه درباره ماموریت انجمن.",
|
||||
"activities_heading": "فعالیتهای نمونه",
|
||||
"activities_body": "توضیحات نمونه درباره فعالیتها.",
|
||||
"values_heading": "ارزشهای نمونه",
|
||||
"values_body": "یادگیری، همکاری و مسئولیتپذیری نمونه.",
|
||||
"committees_heading": "کمیتههای نمونه",
|
||||
"committees_body": "اطلاعات نمونه درباره کمیتهها.",
|
||||
"affiliations_heading": "همکاریهای نمونه",
|
||||
"affiliations_body": "اطلاعات نمونه درباره گروههای همکار.",
|
||||
"faq_heading": "پرسشهای نمونه",
|
||||
"contact_heading": "راههای ارتباطی نمونه",
|
||||
"seo_title": "عنوان نمونه صفحه درباره ما",
|
||||
"seo_description": "توضیحات نمونه صفحه درباره ما برای موتورهای جستوجو.",
|
||||
"seo_image": "",
|
||||
"seo_image_alt": "",
|
||||
}
|
||||
|
||||
LISTING_MOCKS = {
|
||||
"events": {
|
||||
"title": "عنوان نمونه رویدادها",
|
||||
"intro": "این متن نمونه برای معرفی فهرست رویدادهاست.",
|
||||
"seo_title": "عنوان نمونه رویدادها",
|
||||
"seo_description": "توضیحات نمونه صفحه رویدادها برای موتورهای جستوجو.",
|
||||
"seo_image": "",
|
||||
"seo_image_alt": "",
|
||||
},
|
||||
"blog": {
|
||||
"title": "عنوان نمونه بلاگ",
|
||||
"intro": "این متن نمونه برای معرفی نوشتههای بلاگ است.",
|
||||
"seo_title": "عنوان نمونه بلاگ",
|
||||
"seo_description": "توضیحات نمونه صفحه بلاگ برای موتورهای جستوجو.",
|
||||
"seo_image": "",
|
||||
"seo_image_alt": "",
|
||||
},
|
||||
}
|
||||
|
||||
ABOUT_ITEM_MOCKS = (
|
||||
("mission", "ماموریت نمونه", "این متن نمونه برای توضیح ماموریت انجمن است."),
|
||||
("activities", "فعالیت نمونه", "این متن نمونه یکی از فعالیتهای انجمن را معرفی میکند."),
|
||||
("values", "ارزش نمونه", "این متن نمونه یکی از ارزشهای انجمن را توضیح میدهد."),
|
||||
("committees", "کمیته نمونه", "این متن نمونه برای معرفی یک کمیته است."),
|
||||
("affiliations", "همکاری نمونه", "این متن نمونه برای معرفی یک گروه همکار است."),
|
||||
("faq", "پرسش نمونه", "پاسخ نمونه؛ اطلاعات نهایی را از پنل مدیریت وارد کنید."),
|
||||
)
|
||||
|
||||
RECORDS = (
|
||||
("site", SiteContent, {"pk": 1}, SITE_MOCK),
|
||||
("home", HomeContent, {"pk": 1}, HOME_MOCK),
|
||||
("about", AboutContent, {"pk": 1}, ABOUT_MOCK),
|
||||
("events", ListingContent, {"pk": "events"}, LISTING_MOCKS["events"]),
|
||||
("blog", ListingContent, {"pk": "blog"}, LISTING_MOCKS["blog"]),
|
||||
)
|
||||
|
||||
|
||||
def has_admin_change(model, object_id, using):
|
||||
content_type = ContentType.objects.db_manager(using).get_for_model(model)
|
||||
return LogEntry.objects.using(using).filter(
|
||||
content_type=content_type,
|
||||
object_id=str(object_id),
|
||||
action_flag=CHANGE,
|
||||
).exists()
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
help = "Fill untouched site content records with obvious sample copy."
|
||||
|
||||
def add_arguments(self, parser):
|
||||
parser.add_argument(
|
||||
"--database",
|
||||
default=DEFAULT_DB_ALIAS,
|
||||
choices=tuple(connections),
|
||||
help="Database alias to update.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--dry-run",
|
||||
action="store_true",
|
||||
help="Report intended changes without writing to the database.",
|
||||
)
|
||||
|
||||
def handle(self, *args, database, dry_run, **options):
|
||||
updated = 0
|
||||
skipped = 0
|
||||
prefix = "Dry run: " if dry_run else ""
|
||||
|
||||
with transaction.atomic(using=database):
|
||||
for label, model, lookup, values in RECORDS:
|
||||
record = model.objects.using(database).select_for_update().filter(**lookup).first()
|
||||
object_id = lookup["pk"]
|
||||
if record is not None and has_admin_change(model, object_id, database):
|
||||
self.stdout.write(f"Skipped {label}: it has a Django admin change entry.")
|
||||
skipped += 1
|
||||
continue
|
||||
|
||||
if dry_run:
|
||||
action = "create" if record is None else "replace"
|
||||
self.stdout.write(f"Dry run: would {action} {label} with mock content.")
|
||||
updated += 1
|
||||
continue
|
||||
|
||||
if record is None:
|
||||
record = model(**lookup)
|
||||
for field, value in values.items():
|
||||
setattr(record, field, value)
|
||||
record.save(using=database)
|
||||
updated += 1
|
||||
self.stdout.write(f"Loaded mock content for {label}.")
|
||||
|
||||
if label == "about":
|
||||
AboutItem.objects.using(database).filter(about_id=record.pk).delete()
|
||||
AboutItem.objects.using(database).bulk_create(
|
||||
[
|
||||
AboutItem(about_id=record.pk, kind=kind, title=title, text=text, position=position)
|
||||
for position, (kind, title, text) in enumerate(ABOUT_ITEM_MOCKS)
|
||||
]
|
||||
)
|
||||
|
||||
action = "would be updated" if dry_run else "updated"
|
||||
summary = f"{updated} content records {action}; {skipped} admin-edited records skipped."
|
||||
self.stdout.write(f"{prefix}{summary}")
|
||||
Reference in New Issue
Block a user