- MediaAsset model with file metadata and AV scan status tracking - ContentBlock model (richtext/image/video/embed/download) with ordered blocks per page and unique constraint on (page, order) - CourseTheme one-to-one per course with primary/secondary color and logo - validate_upload/save_upload helpers with extension and size enforcement - scan_media_asset_task Celery stub (marks clean; replace with ClamAV) - REST API: media upload, page content blocks CRUD, block patch/delete - Admin registrations for all three models - Factory-boy factories and pytest test suite for views, upload, and task Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
108 lines
4.6 KiB
Python
108 lines
4.6 KiB
Python
import uuid
|
|
|
|
import django.db.models.deletion
|
|
from django.conf import settings
|
|
from django.db import migrations, models
|
|
|
|
|
|
class Migration(migrations.Migration):
|
|
|
|
initial = True
|
|
|
|
dependencies = [
|
|
("accounts", "0001_initial"),
|
|
("courses", "0001_initial"),
|
|
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
|
]
|
|
|
|
operations = [
|
|
migrations.CreateModel(
|
|
name="MediaAsset",
|
|
fields=[
|
|
("id", models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
|
|
("org_id", models.UUIDField(db_index=True)),
|
|
("file_name", models.CharField(max_length=255)),
|
|
("file_path", models.CharField(max_length=1000)),
|
|
("file_size", models.PositiveBigIntegerField()),
|
|
("mime_type", models.CharField(blank=True, max_length=100)),
|
|
("extension", models.CharField(blank=True, max_length=20)),
|
|
("scan_status", models.CharField(
|
|
choices=[
|
|
("pending", "Pending"), ("clean", "Clean"),
|
|
("infected", "Infected"), ("error", "Error"),
|
|
],
|
|
default="pending",
|
|
max_length=20,
|
|
)),
|
|
("scan_result", models.TextField(blank=True)),
|
|
("created_at", models.DateTimeField(auto_now_add=True)),
|
|
("uploaded_by", models.ForeignKey(
|
|
on_delete=django.db.models.deletion.CASCADE,
|
|
related_name="media_assets",
|
|
to=settings.AUTH_USER_MODEL,
|
|
)),
|
|
],
|
|
options={"db_table": "cms_media_asset", "ordering": ["-created_at"]},
|
|
),
|
|
migrations.CreateModel(
|
|
name="ContentBlock",
|
|
fields=[
|
|
("id", models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
|
|
("order", models.PositiveIntegerField(default=0)),
|
|
("block_type", models.CharField(
|
|
choices=[
|
|
("richtext", "Rich Text"), ("image", "Image"),
|
|
("video", "Video"), ("embed", "Embed"), ("download", "Download"),
|
|
],
|
|
max_length=20,
|
|
)),
|
|
("body", models.TextField(blank=True)),
|
|
("embed_url", models.URLField(blank=True, max_length=1000)),
|
|
("caption", models.CharField(blank=True, max_length=500)),
|
|
("extra", models.JSONField(blank=True, default=dict)),
|
|
("created_at", models.DateTimeField(auto_now_add=True)),
|
|
("updated_at", models.DateTimeField(auto_now=True)),
|
|
("media_asset", models.ForeignKey(
|
|
blank=True, null=True,
|
|
on_delete=django.db.models.deletion.SET_NULL,
|
|
related_name="blocks",
|
|
to="cms.mediaasset",
|
|
)),
|
|
("page", models.ForeignKey(
|
|
on_delete=django.db.models.deletion.CASCADE,
|
|
related_name="content_blocks",
|
|
to="courses.page",
|
|
)),
|
|
],
|
|
options={"db_table": "cms_content_block", "ordering": ["order"]},
|
|
),
|
|
migrations.AddConstraint(
|
|
model_name="contentblock",
|
|
constraint=models.UniqueConstraint(
|
|
fields=("page", "order"), name="unique_page_block_order"
|
|
),
|
|
),
|
|
migrations.CreateModel(
|
|
name="CourseTheme",
|
|
fields=[
|
|
("id", models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
|
|
("primary_color", models.CharField(default="#000000", max_length=7)),
|
|
("secondary_color", models.CharField(default="#ffffff", max_length=7)),
|
|
("extra", models.JSONField(blank=True, default=dict)),
|
|
("updated_at", models.DateTimeField(auto_now=True)),
|
|
("course", models.OneToOneField(
|
|
on_delete=django.db.models.deletion.CASCADE,
|
|
related_name="theme",
|
|
to="courses.course",
|
|
)),
|
|
("logo", models.ForeignKey(
|
|
blank=True, null=True,
|
|
on_delete=django.db.models.deletion.SET_NULL,
|
|
related_name="course_logos",
|
|
to="cms.mediaasset",
|
|
)),
|
|
],
|
|
options={"db_table": "cms_course_theme"},
|
|
),
|
|
]
|