Files
guilan-ace-backend/apps/blog/migrations/0003_blog_platform.py
Amirhossein Khalili 954e78d0cb
Some checks failed
Backend CI/CD / test (push) Has been cancelled
Backend CI/CD / deploy (push) Has been cancelled
feat(backend): add blog publishing platform
2026-06-08 21:31:06 +03:30

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),
]