Initial
This commit is contained in:
0
django_translatable_fields/management/__init__.py
Normal file
0
django_translatable_fields/management/__init__.py
Normal file
@@ -0,0 +1,177 @@
|
||||
"""
|
||||
Management command to create migrations for translatable field changes
|
||||
"""
|
||||
|
||||
from django.core.management.base import BaseCommand
|
||||
from django.core.management import call_command
|
||||
from django.apps import apps
|
||||
from django.db import models
|
||||
from django.db.migrations.writer import MigrationWriter
|
||||
from django.db.migrations import Migration
|
||||
from django.utils.translation import get_language
|
||||
import os
|
||||
|
||||
from ...operations import ConvertTranslatableField
|
||||
from ...fields import TranslatableMixin
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
help = 'Create migrations for translatable field changes (translate=True/False)'
|
||||
|
||||
def add_arguments(self, parser):
|
||||
parser.add_argument(
|
||||
'--app',
|
||||
type=str,
|
||||
help='Specific app to check for translatable changes'
|
||||
)
|
||||
parser.add_argument(
|
||||
'--language',
|
||||
type=str,
|
||||
default=None,
|
||||
help='Language to use when converting from translatable to non-translatable (default: current language)'
|
||||
)
|
||||
parser.add_argument(
|
||||
'--dry-run',
|
||||
action='store_true',
|
||||
help='Show what would be created without actually creating migrations'
|
||||
)
|
||||
|
||||
def handle(self, *args, **options):
|
||||
app_label = options.get('app')
|
||||
language = options.get('language') or get_language() or 'en'
|
||||
dry_run = options.get('dry_run', False)
|
||||
|
||||
if app_label:
|
||||
apps_to_check = [apps.get_app_config(app_label)]
|
||||
else:
|
||||
apps_to_check = apps.get_app_configs()
|
||||
|
||||
changes_found = False
|
||||
|
||||
for app_config in apps_to_check:
|
||||
changes = self.detect_translatable_changes(app_config)
|
||||
|
||||
if changes:
|
||||
changes_found = True
|
||||
self.stdout.write(
|
||||
self.style.SUCCESS(f'Found translatable changes in {app_config.label}:')
|
||||
)
|
||||
|
||||
for model_name, field_changes in changes.items():
|
||||
for field_name, change in field_changes.items():
|
||||
from_trans = change['from_translatable']
|
||||
to_trans = change['to_translatable']
|
||||
|
||||
direction = "translatable" if to_trans else "non-translatable"
|
||||
self.stdout.write(f" {model_name}.{field_name} -> {direction}")
|
||||
|
||||
if not dry_run:
|
||||
self.create_migration(app_config, model_name, field_name,
|
||||
from_trans, to_trans, language)
|
||||
|
||||
if not changes_found:
|
||||
self.stdout.write(self.style.WARNING('No translatable field changes detected.'))
|
||||
elif not dry_run:
|
||||
self.stdout.write(
|
||||
self.style.SUCCESS('Migration files created successfully.')
|
||||
)
|
||||
self.stdout.write('Run "python manage.py migrate" to apply the changes.')
|
||||
|
||||
def detect_translatable_changes(self, app_config):
|
||||
"""Detect changes in translatable field settings"""
|
||||
changes = {}
|
||||
|
||||
# Get current migration state
|
||||
try:
|
||||
from django.db.migrations.loader import MigrationLoader
|
||||
loader = MigrationLoader(None)
|
||||
|
||||
# Get the latest migration state for this app
|
||||
if app_config.label in loader.graph.nodes:
|
||||
project_state = loader.project_state()
|
||||
|
||||
for model_name, model in app_config.get_models():
|
||||
model_name = model_name.__name__
|
||||
|
||||
# Check current model fields
|
||||
for field in model._meta.get_fields():
|
||||
if isinstance(field, TranslatableMixin):
|
||||
current_translatable = field.translatable
|
||||
|
||||
# Try to get the field from the migration state
|
||||
try:
|
||||
migration_model = project_state.models.get(
|
||||
(app_config.label, model_name.lower())
|
||||
)
|
||||
if migration_model:
|
||||
migration_field = migration_model.fields.get(field.name)
|
||||
if migration_field:
|
||||
# Check if translatable setting changed
|
||||
old_translatable = getattr(migration_field, 'translatable', True)
|
||||
|
||||
if old_translatable != current_translatable:
|
||||
if model_name not in changes:
|
||||
changes[model_name] = {}
|
||||
|
||||
changes[model_name][field.name] = {
|
||||
'from_translatable': old_translatable,
|
||||
'to_translatable': current_translatable
|
||||
}
|
||||
except (KeyError, AttributeError):
|
||||
# Field doesn't exist in migrations yet, skip
|
||||
pass
|
||||
|
||||
except Exception as e:
|
||||
self.stdout.write(
|
||||
self.style.WARNING(f'Could not detect changes in {app_config.label}: {e}')
|
||||
)
|
||||
|
||||
return changes
|
||||
|
||||
def create_migration(self, app_config, model_name, field_name, from_translatable, to_translatable, language):
|
||||
"""Create a migration file with the conversion operation"""
|
||||
|
||||
# Create the operation
|
||||
operation = ConvertTranslatableField(
|
||||
model_name=model_name.lower(),
|
||||
field_name=field_name,
|
||||
from_translatable=from_translatable,
|
||||
to_translatable=to_translatable,
|
||||
language=language
|
||||
)
|
||||
|
||||
# Create migration
|
||||
migration = Migration(
|
||||
f"convert_{field_name}_translatable",
|
||||
app_config.label
|
||||
)
|
||||
migration.operations = [operation]
|
||||
|
||||
# Find migrations directory
|
||||
migrations_dir = os.path.join(app_config.path, 'migrations')
|
||||
if not os.path.exists(migrations_dir):
|
||||
os.makedirs(migrations_dir)
|
||||
|
||||
# Generate migration file
|
||||
writer = MigrationWriter(migration)
|
||||
migration_string = writer.as_string()
|
||||
|
||||
# Find next migration number
|
||||
existing_migrations = [
|
||||
f for f in os.listdir(migrations_dir)
|
||||
if f.endswith('.py') and f[0].isdigit()
|
||||
]
|
||||
|
||||
if existing_migrations:
|
||||
numbers = [int(f.split('_')[0]) for f in existing_migrations if f.split('_')[0].isdigit()]
|
||||
next_number = max(numbers) + 1 if numbers else 1
|
||||
else:
|
||||
next_number = 1
|
||||
|
||||
filename = f"{next_number:04d}_convert_{field_name}_translatable.py"
|
||||
filepath = os.path.join(migrations_dir, filename)
|
||||
|
||||
with open(filepath, 'w') as f:
|
||||
f.write(migration_string)
|
||||
|
||||
self.stdout.write(f"Created migration: {filepath}")
|
||||
Reference in New Issue
Block a user