simple-translation/simple_translation/forms.py

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)