Added a new management command sync_database_fields to sync the database after a new model has been registered or a new language has been added. Resolves issue 62 (thanks to Sébastien Fievet and the authors of django-transmeta).

This commit is contained in:
Dirk Eschler 2012-07-11 12:57:37 +00:00
parent 10c9a95da6
commit e2bc3f2d4d
3 changed files with 147 additions and 2 deletions

View file

@ -8,3 +8,4 @@ CONTRIBUTORS
Carl J. Meyer
Jaap Roes
Bojan Mihelac
Sébastien Fievet

View file

@ -1,14 +1,18 @@
v0.4.0-alpha1
=============
FIXED: Dynamic TranslationOptions model name.
ADDED: New management command sync_database_fields to sync the database after
a new model has been registered or a new language has been added.
(thanks to Sébastien Fievet and the authors of django-transmeta,
resolves issue 62)
CHANGED: Use app-level translation files in favour of a single project-level
one. Adds an autoregister feature similiar to the one provided by
Django's admin. A new setting MODELTRANSLATION_TRANSLATION_FILES keeps
backwards compatibility with older versions. See documentation for
details. This is basically a merge from both
django-modeltranslation-wrapper and hyperweek's branch at github.
(thanks to Jacek Tomaszewski, hyperweek and haineault)
(thanks to Jacek Tomaszewski, Sébastien Fievet and Maxime Haineault,
resolves issues 19, 58 and 71)
CHANGED: Moved tests to separate folder and added tests for TranslationAdmin.
To run the tests the settings provided in model.tests.modeltranslation
@ -20,6 +24,8 @@ CHANGED: Major refactoring of the admin integration. Subclassed BaseModelAdmin
and get_fieldsets. This should resolve several problems with the
exclude and fieldsets options and properly support options in inlines.
(resolves issue 72)
FIXED: Dynamic TranslationOptions model name.
FIXED: Widgets for translated fields are not properly copied from original
fields.
(thanks to boris-chervenkov, resolves issue 74)
@ -35,6 +41,7 @@ Date: 2012-02-23
CHANGED: jQuery search path in tabbed_translation_fields.js. This allows use of
a version of jQuery other than the one provided by Django. Users who
want to force the use of Django's jQuery can include force_jquery.js.
FIXED: Another attempt to include static files during installation.
(resolves reopened issue 61)

View file

@ -0,0 +1,137 @@
# -*- coding: utf-8 -*-
"""
Detect new translatable fields in all models and sync database structure.
You will need to execute this command in two cases:
1. When you add new languages to settings.LANGUAGES.
2. When you add new translatable fields to your models.
Credits: Heavily inspired by django-transmeta's sync_transmeta_db command.
"""
from django.conf import settings
from django.core.management.base import BaseCommand
from django.core.management.color import no_style
from django.db import connection, transaction
from django.db.models import get_models
from modeltranslation.translator import translator, NotRegistered
from modeltranslation.utils import build_localized_fieldname
def ask_for_confirmation(sql_sentences, model_full_name):
print '\nSQL to synchronize "%s" schema:' % model_full_name
for sentence in sql_sentences:
print ' %s' % sentence
while True:
prompt = ('\nAre you sure that you want to execute the previous SQL: '
'(y/n) [n]: ')
answer = raw_input(prompt).strip()
if answer == '':
return False
elif answer not in ('y', 'n', 'yes', 'no'):
print 'Please answer yes or no'
elif answer == 'y' or answer == 'yes':
return True
else:
return False
def print_missing_langs(missing_langs, field_name, model_name):
print 'Missing languages in "%s" field from "%s" model: %s' % (
field_name, model_name, ", ".join(missing_langs))
class Command(BaseCommand):
help = ('Detect new translatable fields or new available languages and '
'sync database structure')
def handle(self, *args, **options):
"""
Command execution.
"""
self.cursor = connection.cursor()
self.introspection = connection.introspection
all_models = get_models()
found_missing_fields = False
for model in all_models:
try:
options = translator.get_options_for_model(model)
# options returns full-wide spectrum of localized fields but
# we only to synchronize the local fields attached to the model.
local_field_names = [field.name for field
in model._meta.local_fields]
translatable_fields = [field for field
in options.localized_fieldnames
if field in local_field_names]
model_full_name = '%s.%s' % (model._meta.app_label,
model._meta.module_name)
db_table = model._meta.db_table
for field_name in translatable_fields:
missing_langs = list(
self.get_missing_languages(field_name, db_table))
if missing_langs:
found_missing_fields = True
print_missing_langs(
missing_langs, field_name, model_full_name)
sql_sentences = self.get_sync_sql(
field_name, missing_langs, model)
execute_sql = ask_for_confirmation(
sql_sentences, model_full_name)
if execute_sql:
print 'Executing SQL...',
for sentence in sql_sentences:
self.cursor.execute(sentence)
print 'Done'
else:
print 'SQL not executed'
except NotRegistered:
pass
transaction.commit_unless_managed()
if not found_missing_fields:
print 'No new translatable fields detected'
def get_table_fields(self, db_table):
"""
Gets table fields from schema.
"""
db_table_desc = self.introspection.get_table_description(
self.cursor, db_table)
return [t[0] for t in db_table_desc]
def get_missing_languages(self, field_name, db_table):
"""
Gets only missings fields.
"""
db_table_fields = self.get_table_fields(db_table)
for lang_code, lang_name in settings.LANGUAGES:
if build_localized_fieldname(
field_name, lang_code) not in db_table_fields:
yield lang_code
def get_sync_sql(self, field_name, missing_langs, model):
"""
Returns SQL needed for sync schema for a new translatable field.
"""
qn = connection.ops.quote_name
style = no_style()
sql_output = []
db_table = model._meta.db_table
for lang in missing_langs:
new_field = build_localized_fieldname(field_name, lang)
f = model._meta.get_field(new_field)
col_type = f.db_type(connection)
field_sql = [style.SQL_FIELD(qn(f.column)),
style.SQL_COLTYPE(col_type)]
# column creation
sql_output.append(
"ALTER TABLE %s ADD COLUMN %s;" % (
qn(db_table), ' '.join(field_sql)))
if not f.null and lang == settings.LANGUAGE_CODE:
sql_output.append("ALTER TABLE %s MODIFY COLUMN %s %s %s;" % \
(qn(db_table), qn(f.column), col_type,
style.SQL_KEYWORD('NOT NULL')))
return sql_output