298 lines
12 KiB
Python
298 lines
12 KiB
Python
# Generated by Django 5.2.5 on 2026-06-08 17:29
|
|
|
|
import apps.blog.models
|
|
import django.db.models.deletion
|
|
import markdown
|
|
import re
|
|
from django.conf import settings
|
|
from django.db import migrations, models
|
|
|
|
|
|
def backfill_post_render_fields(apps, schema_editor):
|
|
Post = apps.get_model('blog', 'Post')
|
|
for post in Post.objects.all():
|
|
html = markdown.markdown(
|
|
post.content or '',
|
|
extensions=[
|
|
'markdown.extensions.extra',
|
|
'markdown.extensions.codehilite',
|
|
'markdown.extensions.toc',
|
|
],
|
|
)
|
|
plain_text = re.sub(r'<[^<]+?>', ' ', html).replace('\n', ' ').strip()
|
|
post.content_html = html
|
|
word_count = len((post.content or '').split())
|
|
post.reading_time = max(1, (word_count + 199) // 200)
|
|
if not post.excerpt and plain_text:
|
|
post.excerpt = f'{plain_text[:297]}...' if len(plain_text) > 300 else plain_text
|
|
post.save(update_fields=['content_html', 'reading_time', 'excerpt'])
|
|
|
|
|
|
def seed_blog_role_groups(apps, schema_editor):
|
|
ContentType = apps.get_model('contenttypes', 'ContentType')
|
|
Permission = apps.get_model('auth', 'Permission')
|
|
Group = apps.get_model('auth', 'Group')
|
|
|
|
post_ct, _ = ContentType.objects.get_or_create(app_label='blog', model='post')
|
|
category_ct, _ = ContentType.objects.get_or_create(app_label='blog', model='category')
|
|
tag_ct, _ = ContentType.objects.get_or_create(app_label='blog', model='tag')
|
|
|
|
permission_specs = [
|
|
(post_ct, 'access_blog_admin', 'Can access blog admin'),
|
|
(post_ct, 'review_blog_post', 'Can review blog posts'),
|
|
(post_ct, 'publish_blog_post', 'Can publish blog posts'),
|
|
(post_ct, 'moderate_blog_comment', 'Can moderate blog comments'),
|
|
(post_ct, 'upload_blog_asset', 'Can upload blog assets'),
|
|
(post_ct, 'add_post', 'Can add post'),
|
|
(post_ct, 'change_post', 'Can change post'),
|
|
(category_ct, 'add_category', 'Can add category'),
|
|
(category_ct, 'change_category', 'Can change category'),
|
|
(tag_ct, 'add_tag', 'Can add tag'),
|
|
(tag_ct, 'change_tag', 'Can change tag'),
|
|
]
|
|
permissions = {}
|
|
for content_type, codename, name in permission_specs:
|
|
permission, _ = Permission.objects.get_or_create(
|
|
content_type=content_type,
|
|
codename=codename,
|
|
defaults={'name': name},
|
|
)
|
|
permissions[codename] = permission
|
|
|
|
editor, _ = Group.objects.get_or_create(name='blog_editor')
|
|
editor.permissions.add(
|
|
permissions['add_post'],
|
|
permissions['change_post'],
|
|
permissions['access_blog_admin'],
|
|
permissions['upload_blog_asset'],
|
|
)
|
|
|
|
supervisor, _ = Group.objects.get_or_create(name='blog_supervisor')
|
|
supervisor.permissions.add(
|
|
permissions['add_post'],
|
|
permissions['change_post'],
|
|
permissions['access_blog_admin'],
|
|
permissions['upload_blog_asset'],
|
|
permissions['review_blog_post'],
|
|
permissions['publish_blog_post'],
|
|
permissions['moderate_blog_comment'],
|
|
permissions['add_category'],
|
|
permissions['change_category'],
|
|
permissions['add_tag'],
|
|
permissions['change_tag'],
|
|
)
|
|
|
|
Group.objects.get_or_create(name='association_admin')
|
|
|
|
|
|
class Migration(migrations.Migration):
|
|
|
|
dependencies = [
|
|
('auth', '0012_alter_user_first_name_max_length'),
|
|
('blog', '0002_initial'),
|
|
('contenttypes', '0002_remove_content_type_name'),
|
|
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
|
]
|
|
|
|
operations = [
|
|
migrations.CreateModel(
|
|
name='PostAsset',
|
|
fields=[
|
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
|
('created_at', models.DateTimeField(auto_now_add=True)),
|
|
('updated_at', models.DateTimeField(auto_now=True)),
|
|
('is_deleted', models.BooleanField(default=False)),
|
|
('deleted_at', models.DateTimeField(blank=True, null=True)),
|
|
('file', models.FileField(upload_to=apps.blog.models.post_asset_upload_to)),
|
|
('file_type', models.CharField(choices=[('image', 'Image'), ('video', 'Video'), ('document', 'Document'), ('archive', 'Archive'), ('other', 'Other')], default='other', max_length=16)),
|
|
('title', models.CharField(blank=True, max_length=200)),
|
|
('alt_text', models.CharField(blank=True, max_length=200)),
|
|
('caption', models.TextField(blank=True)),
|
|
('size', models.PositiveBigIntegerField(default=0)),
|
|
('mime_type', models.CharField(blank=True, max_length=120)),
|
|
],
|
|
options={
|
|
'ordering': ['-created_at'],
|
|
},
|
|
),
|
|
migrations.CreateModel(
|
|
name='SavedPost',
|
|
fields=[
|
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
|
('created_at', models.DateTimeField(auto_now_add=True)),
|
|
],
|
|
),
|
|
migrations.AlterModelOptions(
|
|
name='post',
|
|
options={'ordering': ['-created_at'], 'permissions': [('access_blog_admin', 'Can access blog admin'), ('review_blog_post', 'Can review blog posts'), ('publish_blog_post', 'Can publish blog posts'), ('moderate_blog_comment', 'Can moderate blog comments'), ('upload_blog_asset', 'Can upload blog assets')]},
|
|
),
|
|
migrations.AddField(
|
|
model_name='comment',
|
|
name='hidden_at',
|
|
field=models.DateTimeField(blank=True, null=True),
|
|
),
|
|
migrations.AddField(
|
|
model_name='comment',
|
|
name='hidden_by',
|
|
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='hidden_blog_comments', to=settings.AUTH_USER_MODEL),
|
|
),
|
|
migrations.AddField(
|
|
model_name='comment',
|
|
name='moderation_note',
|
|
field=models.TextField(blank=True),
|
|
),
|
|
migrations.AddField(
|
|
model_name='post',
|
|
name='canonical_url',
|
|
field=models.URLField(blank=True),
|
|
),
|
|
migrations.AddField(
|
|
model_name='post',
|
|
name='content_html',
|
|
field=models.TextField(blank=True, editable=False),
|
|
),
|
|
migrations.AddField(
|
|
model_name='post',
|
|
name='focus_keyword',
|
|
field=models.CharField(blank=True, max_length=120),
|
|
),
|
|
migrations.AddField(
|
|
model_name='post',
|
|
name='noindex',
|
|
field=models.BooleanField(default=False),
|
|
),
|
|
migrations.AddField(
|
|
model_name='post',
|
|
name='og_description',
|
|
field=models.CharField(blank=True, max_length=200),
|
|
),
|
|
migrations.AddField(
|
|
model_name='post',
|
|
name='og_image',
|
|
field=models.ImageField(blank=True, null=True, upload_to='blog/og/'),
|
|
),
|
|
migrations.AddField(
|
|
model_name='post',
|
|
name='og_title',
|
|
field=models.CharField(blank=True, max_length=95),
|
|
),
|
|
migrations.AddField(
|
|
model_name='post',
|
|
name='published_by',
|
|
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='published_blog_posts', to=settings.AUTH_USER_MODEL),
|
|
),
|
|
migrations.AddField(
|
|
model_name='post',
|
|
name='reading_time',
|
|
field=models.PositiveIntegerField(default=1),
|
|
),
|
|
migrations.AddField(
|
|
model_name='post',
|
|
name='review_note',
|
|
field=models.TextField(blank=True),
|
|
),
|
|
migrations.AddField(
|
|
model_name='post',
|
|
name='reviewed_at',
|
|
field=models.DateTimeField(blank=True, null=True),
|
|
),
|
|
migrations.AddField(
|
|
model_name='post',
|
|
name='reviewed_by',
|
|
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='reviewed_blog_posts', to=settings.AUTH_USER_MODEL),
|
|
),
|
|
migrations.AddField(
|
|
model_name='post',
|
|
name='seo_description',
|
|
field=models.CharField(blank=True, max_length=170),
|
|
),
|
|
migrations.AddField(
|
|
model_name='post',
|
|
name='seo_title',
|
|
field=models.CharField(blank=True, max_length=70),
|
|
),
|
|
migrations.AddField(
|
|
model_name='post',
|
|
name='submitted_at',
|
|
field=models.DateTimeField(blank=True, null=True),
|
|
),
|
|
migrations.AlterField(
|
|
model_name='category',
|
|
name='slug',
|
|
field=models.SlugField(allow_unicode=True, blank=True, max_length=100, unique=True),
|
|
),
|
|
migrations.AlterField(
|
|
model_name='post',
|
|
name='slug',
|
|
field=models.SlugField(allow_unicode=True, blank=True, max_length=200, unique=True),
|
|
),
|
|
migrations.AlterField(
|
|
model_name='post',
|
|
name='status',
|
|
field=models.CharField(choices=[('draft', 'Draft'), ('submitted', 'Submitted for review'), ('changes_requested', 'Changes requested'), ('published', 'Published'), ('archived', 'Archived')], default='draft', max_length=24),
|
|
),
|
|
migrations.AlterField(
|
|
model_name='tag',
|
|
name='slug',
|
|
field=models.SlugField(allow_unicode=True, blank=True, unique=True),
|
|
),
|
|
migrations.AddIndex(
|
|
model_name='comment',
|
|
index=models.Index(fields=['author', 'created_at'], name='blog_commen_author__9faedb_idx'),
|
|
),
|
|
migrations.AddIndex(
|
|
model_name='like',
|
|
index=models.Index(fields=['user', 'created_at'], name='blog_like_user_id_7a46aa_idx'),
|
|
),
|
|
migrations.AddIndex(
|
|
model_name='post',
|
|
index=models.Index(fields=['author', 'status'], name='blog_post_author__95cbf7_idx'),
|
|
),
|
|
migrations.AddIndex(
|
|
model_name='post',
|
|
index=models.Index(fields=['slug', 'status'], name='blog_post_slug_714acb_idx'),
|
|
),
|
|
migrations.AddField(
|
|
model_name='postasset',
|
|
name='post',
|
|
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='assets', to='blog.post'),
|
|
),
|
|
migrations.AddField(
|
|
model_name='postasset',
|
|
name='uploaded_by',
|
|
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='blog_assets', to=settings.AUTH_USER_MODEL),
|
|
),
|
|
migrations.AddField(
|
|
model_name='savedpost',
|
|
name='post',
|
|
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='saves', to='blog.post'),
|
|
),
|
|
migrations.AddField(
|
|
model_name='savedpost',
|
|
name='user',
|
|
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='saved_posts', to=settings.AUTH_USER_MODEL),
|
|
),
|
|
migrations.AddIndex(
|
|
model_name='postasset',
|
|
index=models.Index(fields=['post', 'file_type'], name='blog_postas_post_id_d81393_idx'),
|
|
),
|
|
migrations.AddIndex(
|
|
model_name='postasset',
|
|
index=models.Index(fields=['uploaded_by', 'created_at'], name='blog_postas_uploade_c579a7_idx'),
|
|
),
|
|
migrations.AddIndex(
|
|
model_name='savedpost',
|
|
index=models.Index(fields=['post'], name='blog_savedp_post_id_62b622_idx'),
|
|
),
|
|
migrations.AddIndex(
|
|
model_name='savedpost',
|
|
index=models.Index(fields=['user', 'created_at'], name='blog_savedp_user_id_c04172_idx'),
|
|
),
|
|
migrations.AlterUniqueTogether(
|
|
name='savedpost',
|
|
unique_together={('post', 'user')},
|
|
),
|
|
migrations.RunPython(backfill_post_render_fields, migrations.RunPython.noop),
|
|
migrations.RunPython(seed_blog_role_groups, migrations.RunPython.noop),
|
|
]
|