114 lines
5.0 KiB
Python
114 lines
5.0 KiB
Python
from django.forms.models import model_to_dict, fields_for_model
|
|
from django.forms.models import ModelForm, ModelFormMetaclass, modelform_factory, model_to_dict
|
|
from django.forms.util import ErrorList, ErrorDict
|
|
from django.core.exceptions import NON_FIELD_ERRORS
|
|
from simple_translation.translation_pool import translation_pool
|
|
|
|
class TranslationModelFormMetaclass(ModelFormMetaclass):
|
|
|
|
def __new__(cls, name, bases, attrs):
|
|
formfield_callback = attrs.get('formfield_callback', None)
|
|
try:
|
|
parents = [b for b in bases if issubclass(b, TranslationModelForm)]
|
|
except NameError:
|
|
# We are defining TranslationModelForm itself.
|
|
parents = None
|
|
|
|
new_class = super(TranslationModelFormMetaclass, cls).__new__(cls, name, bases,
|
|
attrs)
|
|
if not parents:
|
|
return new_class
|
|
|
|
opts = new_class._meta
|
|
if opts.model:
|
|
if translation_pool.is_registered(opts.model):
|
|
info = translation_pool.get_info(opts.model)
|
|
translated_model = info.translated_model
|
|
new_class.child_form_class = modelform_factory(translated_model,
|
|
exclude=[info.translation_of_field], formfield_callback=formfield_callback)
|
|
new_class.declared_fields.update(new_class.child_form_class.declared_fields)
|
|
new_class.base_fields.update(new_class.child_form_class.base_fields)
|
|
return new_class
|
|
|
|
class TranslationModelForm(ModelForm):
|
|
__metaclass__ = TranslationModelFormMetaclass
|
|
|
|
def __init__(self, data=None, files=None, auto_id='id_%s', prefix=None,
|
|
initial={}, error_class=ErrorList, label_suffix=':',
|
|
empty_permitted=False, instance=None):
|
|
|
|
model = self._meta.model
|
|
child_model = self.child_form_class._meta.model
|
|
info = translation_pool.get_info(model)
|
|
|
|
current_language = self.base_fields[info.language_field].initial
|
|
|
|
if instance and instance.pk:
|
|
try:
|
|
child_instance = child_model.objects.get(**{
|
|
info.translation_of_field: instance.pk,
|
|
info.language_field: current_language})
|
|
except child_model.DoesNotExist:
|
|
child_instance = child_model(**{
|
|
info.language_field: current_language})
|
|
else:
|
|
child_instance = child_model(**{info.language_field: current_language})
|
|
|
|
initial.update(model_to_dict(child_instance))
|
|
self.child_form = self.child_form_class(data=data, files=files, auto_id=auto_id, prefix=prefix,
|
|
initial=initial, error_class=error_class, label_suffix=label_suffix,
|
|
empty_permitted=empty_permitted, instance=child_instance)
|
|
|
|
|
|
super(TranslationModelForm, self).__init__(data=data, files=files, auto_id=auto_id, prefix=prefix,
|
|
initial=initial, error_class=error_class, label_suffix=label_suffix,
|
|
empty_permitted=empty_permitted, instance=instance)
|
|
|
|
def full_clean(self):
|
|
super(TranslationModelForm, self).full_clean()
|
|
if self._errors:
|
|
self.cleaned_data = {}
|
|
self.child_form.full_clean()
|
|
if self.child_form._errors:
|
|
if not self._errors:
|
|
self._errors = ErrorDict()
|
|
for k, v in self.child_form._errors.items():
|
|
if k in self._errors and k != NON_FIELD_ERRORS:
|
|
# remove existing errors so there are no duplicates
|
|
del self._errors[k]
|
|
self._update_errors(self.child_form._errors)
|
|
del self.cleaned_data
|
|
|
|
def translation_modelform_factory(model, form=TranslationModelForm, fields=None, exclude=None,
|
|
formfield_callback=None):
|
|
# Create the inner Meta class. FIXME: ideally, we should be able to
|
|
# construct a ModelForm without creating and passing in a temporary
|
|
# inner class.
|
|
info = translation_pool.get_info(model)
|
|
translated_model = info.translated_model
|
|
translation_fields = [f[0].name for f in translated_model._meta.get_fields_with_model()]
|
|
# Build up a list of attributes that the Meta object will have.
|
|
attrs = {'model': model}
|
|
if fields is not None:
|
|
attrs['fields'] = [ field for field in fields if not field in translation_fields]
|
|
if exclude is not None:
|
|
attrs['exclude'] = exclude
|
|
|
|
# If parent form class already has an inner Meta, the Meta we're
|
|
# creating needs to inherit from the parent's inner meta.
|
|
parent = (object,)
|
|
if hasattr(form, 'Meta'):
|
|
parent = (form.Meta, object)
|
|
Meta = type('Meta', parent, attrs)
|
|
|
|
# Give this new form class a reasonable name.
|
|
class_name = model.__name__ + 'Form'
|
|
|
|
# Class attributes for the new form class.
|
|
form_class_attrs = {
|
|
'Meta': Meta,
|
|
'formfield_callback': formfield_callback
|
|
}
|
|
|
|
return TranslationModelFormMetaclass(class_name, (form,), form_class_attrs)
|