Implement ForeignKey support (does not support reverse relations)

This commit is contained in:
Braden MacDonald 2013-03-06 13:21:09 -08:00
parent ea0e2db689
commit 420e3b6c52
4 changed files with 89 additions and 10 deletions

View file

@ -25,6 +25,7 @@ SUPPORTED_FIELDS = (
fields.TimeField,
fields.files.FileField,
fields.files.ImageField,
fields.related.ForeignKey,
)
try:
SUPPORTED_FIELDS += (fields.GenericIPAddressField,) # Django 1.4+ only
@ -50,6 +51,8 @@ def create_translation_field(model, field_name, lang):
if not (isinstance(field, SUPPORTED_FIELDS) or cls_name in mt_settings.CUSTOM_FIELDS):
raise ImproperlyConfigured(
'%s is not supported by modeltranslation.' % cls_name)
if isinstance(field, fields.related.ForeignKey) and field.rel.related_name != '+':
raise ImproperlyConfigured('Translated ForeignKey fields must use related_name="+"')
translation_class = field_factory(field.__class__)
return translation_class(translated_field=field, language=lang)
@ -127,8 +130,10 @@ class TranslationField(object):
def get_attname_column(self):
attname = self.get_attname()
column = build_localized_fieldname(
self.translated_field.db_column or self.translated_field.name, self.language) or attname
if self.translated_field.db_column:
column = build_localized_fieldname(self.translated_field.db_column)
else:
column = attname
return attname, column
def south_field_triple(self):
@ -137,8 +142,12 @@ class TranslationField(object):
"""
# We'll just introspect the _actual_ field.
from south.modelsinspector import introspector
field_class = '%s.%s' % (self.translated_field.__class__.__module__,
self.translated_field.__class__.__name__)
try:
# Check if the field provides its own 'field_class':
field_class = self.translated_field.south_field_triple()[0]
except AttributeError:
field_class = '%s.%s' % (self.translated_field.__class__.__module__,
self.translated_field.__class__.__name__)
args, kwargs = introspector(self)
# That's our definition!
return (field_class, args, kwargs)

View file

@ -43,8 +43,8 @@ except ImportError:
# so we'll just pass in None.
request = None
# How much models are registered for tests.
TEST_MODELS = 22
# How many models are registered for tests.
TEST_MODELS = 23
class reload_override_settings(override_settings):
@ -619,6 +619,64 @@ class FileFieldsTest(ModeltranslationTestBase):
inst.image_de.delete()
class ForeignKeyFieldsTest(ModeltranslationTestBase):
def test_translated_models(self):
field_names = dir(models.ForeignKeyModel())
self.failUnless('id' in field_names)
for f in ('test', 'test_de', 'test_en', 'optional', 'optional_en', 'optional_de'):
self.failUnless(f in field_names)
self.failUnless('%s_id' % f in field_names)
def test_db_column_names(self):
meta = models.ForeignKeyModel._meta
# Make sure the correct database columns always get used:
attname, col = meta.get_field('test').get_attname_column()
self.failUnlessEqual(attname, 'test_id')
self.failUnlessEqual(attname, col)
attname, col = meta.get_field('test_en').get_attname_column()
self.failUnlessEqual(attname, 'test_en_id')
self.failUnlessEqual(attname, col)
attname, col = meta.get_field('test_de').get_attname_column()
self.failUnlessEqual(attname, 'test_de_id')
self.failUnlessEqual(attname, col)
def test_translated_models_instance(self):
test_inst1 = models.TestModel(title_en='title1_en', title_de='title1_de')
test_inst1.save()
test_inst2 = models.TestModel(title_en='title2_en', title_de='title2_de')
test_inst2.save()
inst = models.ForeignKeyModel()
trans_real.activate("de")
inst.test = test_inst1
inst.optional = None
trans_real.activate("en")
inst.optional = test_inst2
inst.save()
trans_real.activate("de")
self.failUnlessEqual(inst.test.title, 'title1_de')
self.failUnlessEqual(inst.test_de_id, test_inst1.pk)
self.failUnlessEqual(inst.test_de.title, 'title1_de')
self.failUnlessEqual(inst.optional, None)
# Test fallbacks:
trans_real.activate("en")
with default_fallback():
self.failUnlessEqual(inst.test.pk, test_inst1.pk)
self.failUnlessEqual(inst.test.title, 'title1_en')
# Test English:
self.failUnlessEqual(inst.optional.title, 'title2_en')
self.failUnlessEqual(inst.optional_en_id, test_inst2.pk)
self.failUnlessEqual(inst.optional_en.title, 'title2_en')
class OtherFieldsTest(ModeltranslationTestBase):
def test_translated_models(self):
inst = models.OtherFieldsModel.objects.create()

View file

@ -34,6 +34,11 @@ class FileFieldsModel(models.Model):
file = models.FileField(upload_to='modeltranslation_tests', null=True, blank=True)
image = models.ImageField(upload_to='modeltranslation_tests', null=True, blank=True)
########## Foreign Key fields testing
class ForeignKeyModel(models.Model):
test = models.ForeignKey(TestModel, null=True, related_name="+")
optional = models.ForeignKey(TestModel, blank=True, null=True, related_name="+")
########## Custom fields testing

View file

@ -3,10 +3,10 @@ from django.utils.translation import ugettext_lazy
from modeltranslation.translator import translator, TranslationOptions
from modeltranslation.tests.models import (
TestModel, FallbackModel, FallbackModel2, FileFieldsModel, OtherFieldsModel, DescriptorModel,
AbstractModelA, AbstractModelB, Slugged, MetaData, Displayable, Page, RichText, RichTextPage,
MultitableModelA, MultitableModelB, MultitableModelC, ManagerTestModel, CustomManagerTestModel,
CustomManager2TestModel, GroupFieldsetsModel, NameModel)
TestModel, FallbackModel, FallbackModel2, FileFieldsModel, ForeignKeyModel, OtherFieldsModel,
DescriptorModel, AbstractModelA, AbstractModelB, Slugged, MetaData, Displayable, Page,
RichText, RichTextPage, MultitableModelA, MultitableModelB, MultitableModelC, ManagerTestModel,
CustomManagerTestModel, CustomManager2TestModel, GroupFieldsetsModel, NameModel)
class TestTranslationOptions(TranslationOptions):
@ -35,6 +35,13 @@ class FileFieldsModelTranslationOptions(TranslationOptions):
translator.register(FileFieldsModel, FileFieldsModelTranslationOptions)
########## Foreign Key fields testing
class ForeignKeyModelTranslationOptions(TranslationOptions):
fields = ('test', 'optional',)
translator.register(ForeignKeyModel, ForeignKeyModelTranslationOptions)
########## Custom fields testing
class OtherFieldsModelTranslationOptions(TranslationOptions):