212 lines
9 KiB
Python
Executable file
212 lines
9 KiB
Python
Executable file
from django.forms import *
|
|
from django.forms.models import BaseModelFormSet
|
|
from django.forms.models import BaseInlineFormSet
|
|
from django.forms.models import ModelChoiceIterator
|
|
from django.forms.models import InlineForeignKeyHiddenInput, InlineForeignKeyField
|
|
|
|
from django.utils.text import capfirst
|
|
|
|
from formsets import BaseFormSet
|
|
|
|
from django.db.models import fields
|
|
|
|
from dojango.forms.fields import *
|
|
from dojango.forms.widgets import DojoWidgetMixin, Textarea, Select, SelectMultiple, HiddenInput
|
|
|
|
__all__ = (
|
|
'ModelForm', 'BaseModelForm', 'model_to_dict', 'fields_for_model',
|
|
'save_instance', 'ModelChoiceField', 'ModelMultipleChoiceField',
|
|
)
|
|
|
|
class ModelChoiceField(DojoFieldMixin, models.ModelChoiceField):
|
|
"""
|
|
Overwritten 'ModelChoiceField' using the 'DojoFieldMixin' functionality.
|
|
"""
|
|
widget = Select
|
|
|
|
class ModelMultipleChoiceField(DojoFieldMixin, models.ModelMultipleChoiceField):
|
|
"""
|
|
Overwritten 'ModelMultipleChoiceField' using the 'DojoFieldMixin' functonality.
|
|
"""
|
|
widget = SelectMultiple
|
|
|
|
# Fields #####################################################################
|
|
|
|
class InlineForeignKeyHiddenInput(DojoWidgetMixin, InlineForeignKeyHiddenInput):
|
|
"""
|
|
Overwritten InlineForeignKeyHiddenInput to use the dojango widget mixin
|
|
"""
|
|
dojo_type = 'dijit.form.TextBox' # otherwise dijit.form.Form can't get its values
|
|
|
|
class InlineForeignKeyField(DojoFieldMixin, InlineForeignKeyField, Field):
|
|
"""
|
|
Overwritten InlineForeignKeyField to use the dojango field mixin and passing
|
|
the dojango InlineForeignKeyHiddenInput as widget.
|
|
"""
|
|
def __init__(self, parent_instance, *args, **kwargs):
|
|
self.parent_instance = parent_instance
|
|
self.pk_field = kwargs.pop("pk_field", False)
|
|
self.to_field = kwargs.pop("to_field", None)
|
|
if self.parent_instance is not None:
|
|
if self.to_field:
|
|
kwargs["initial"] = getattr(self.parent_instance, self.to_field)
|
|
else:
|
|
kwargs["initial"] = self.parent_instance.pk
|
|
|
|
kwargs["required"] = False
|
|
kwargs["widget"] = InlineForeignKeyHiddenInput
|
|
# don't call the the superclass of this one. Use the superclass of the
|
|
# normal django InlineForeignKeyField
|
|
Field.__init__(self, *args, **kwargs)
|
|
|
|
# our customized model field => form field map
|
|
# here it is defined which form field is used by which model field, when creating a ModelForm
|
|
MODEL_TO_FORM_FIELD_MAP = (
|
|
# (model_field, form_field, [optional widget])
|
|
# the order of these fields is very important for inherited model fields
|
|
# e.g. the CharField must be checked at last, because several other
|
|
# fields are a subclass of it.
|
|
(fields.CommaSeparatedIntegerField, CharField),
|
|
(fields.DateTimeField, DateTimeField), # must be in front of the DateField
|
|
(fields.DateField, DateField),
|
|
(fields.DecimalField, DecimalField),
|
|
(fields.EmailField, EmailField),
|
|
(fields.FilePathField, FilePathField),
|
|
(fields.FloatField, FloatField),
|
|
(fields.related.ForeignKey, ModelChoiceField),
|
|
(fields.files.ImageField, ImageField),
|
|
(fields.files.FileField, FileField),
|
|
(fields.IPAddressField, IPAddressField),
|
|
(fields.related.ManyToManyField, ModelMultipleChoiceField),
|
|
(fields.NullBooleanField, CharField),
|
|
(fields.BooleanField, BooleanField),
|
|
(fields.PositiveSmallIntegerField, IntegerField),
|
|
(fields.PositiveIntegerField, IntegerField),
|
|
(fields.SlugField, SlugField),
|
|
(fields.SmallIntegerField, IntegerField),
|
|
(fields.IntegerField, IntegerField),
|
|
(fields.TimeField, TimeField),
|
|
(fields.URLField, URLField),
|
|
(fields.TextField, CharField, Textarea),
|
|
(fields.CharField, CharField),
|
|
)
|
|
|
|
def formfield_function(field, **kwargs):
|
|
"""
|
|
Custom formfield function, so we can inject our own form fields. The
|
|
mapping of model fields to form fields is defined in 'MODEL_TO_FORM_FIELD_MAP'.
|
|
It uses the default django mapping as fallback, if there is no match in our
|
|
custom map.
|
|
|
|
field -- a model field
|
|
"""
|
|
for field_map in MODEL_TO_FORM_FIELD_MAP:
|
|
if isinstance(field, field_map[0]):
|
|
defaults = {}
|
|
if field.choices:
|
|
# the normal django field forms.TypedChoiceField is wired hard
|
|
# within the original db/models/fields.py.
|
|
# If we use our custom Select widget, we also have to pass in
|
|
# some additional validation field attributes.
|
|
defaults['widget'] = Select(attrs={
|
|
'extra_field_attrs':{
|
|
'required':not field.blank,
|
|
'help_text':field.help_text,
|
|
}
|
|
})
|
|
elif len(field_map) == 3:
|
|
defaults['widget']=field_map[2]
|
|
defaults.update(kwargs)
|
|
return field.formfield(form_class=field_map[1], **defaults)
|
|
# return the default formfield, if there is no equivalent
|
|
return field.formfield(**kwargs)
|
|
|
|
# ModelForms #################################################################
|
|
|
|
def fields_for_model(*args, **kwargs):
|
|
"""Changed fields_for_model function, where we use our own formfield_callback"""
|
|
kwargs["formfield_callback"] = formfield_function
|
|
return models.fields_for_model(*args, **kwargs)
|
|
|
|
class ModelFormMetaclass(models.ModelFormMetaclass):
|
|
"""
|
|
Overwritten 'ModelFormMetaClass'. We attach our own formfield generation
|
|
function.
|
|
"""
|
|
def __new__(cls, name, bases, attrs):
|
|
# this is how we can replace standard django form fields with dojo ones
|
|
attrs["formfield_callback"] = formfield_function
|
|
return super(ModelFormMetaclass, cls).__new__(cls, name, bases, attrs)
|
|
|
|
class ModelForm(models.ModelForm):
|
|
"""
|
|
Overwritten 'ModelForm' using the metaclass defined above.
|
|
"""
|
|
__metaclass__ = ModelFormMetaclass
|
|
|
|
def modelform_factory(*args, **kwargs):
|
|
"""Changed modelform_factory function, where we use our own formfield_callback"""
|
|
kwargs["formfield_callback"] = formfield_function
|
|
kwargs["form"] = ModelForm
|
|
return models.modelform_factory(*args, **kwargs)
|
|
|
|
# ModelFormSets ##############################################################
|
|
|
|
class BaseModelFormSet(BaseModelFormSet, BaseFormSet):
|
|
|
|
def add_fields(self, form, index):
|
|
"""Overwritten BaseModelFormSet using the dojango BaseFormSet and
|
|
the ModelChoiceField.
|
|
NOTE: This method was copied from django 1.3 beta 1"""
|
|
from django.db.models import AutoField, OneToOneField, ForeignKey
|
|
self._pk_field = pk = self.model._meta.pk
|
|
def pk_is_not_editable(pk):
|
|
return ((not pk.editable) or (pk.auto_created or isinstance(pk, AutoField))
|
|
or (pk.rel and pk.rel.parent_link and pk_is_not_editable(pk.rel.to._meta.pk)))
|
|
if pk_is_not_editable(pk) or pk.name not in form.fields:
|
|
if form.is_bound:
|
|
pk_value = form.instance.pk
|
|
else:
|
|
try:
|
|
if index is not None:
|
|
pk_value = self.get_queryset()[index].pk
|
|
else:
|
|
pk_value = None
|
|
except IndexError:
|
|
pk_value = None
|
|
if isinstance(pk, OneToOneField) or isinstance(pk, ForeignKey):
|
|
qs = pk.rel.to._default_manager.get_query_set()
|
|
else:
|
|
qs = self.model._default_manager.get_query_set()
|
|
qs = qs.using(form.instance._state.db)
|
|
form.fields[self._pk_field.name] = ModelChoiceField(qs, initial=pk_value, required=False, widget=HiddenInput)
|
|
BaseFormSet.add_fields(self, form, index)
|
|
|
|
def modelformset_factory(*args, **kwargs):
|
|
"""Changed modelformset_factory function, where we use our own formfield_callback"""
|
|
kwargs["formfield_callback"] = formfield_function
|
|
kwargs["formset"] = BaseModelFormSet
|
|
return models.modelformset_factory(*args, **kwargs)
|
|
|
|
# InlineFormSets #############################################################
|
|
|
|
class BaseInlineFormSet(BaseInlineFormSet, BaseModelFormSet):
|
|
"""Overwritten BaseInlineFormSet using the dojango InlineForeignKeyFields.
|
|
NOTE: This method was copied from django 1.1"""
|
|
def add_fields(self, form, index):
|
|
super(BaseInlineFormSet, self).add_fields(form, index)
|
|
if self._pk_field == self.fk:
|
|
form.fields[self._pk_field.name] = InlineForeignKeyField(self.instance, pk_field=True)
|
|
else:
|
|
kwargs = {
|
|
'label': getattr(form.fields.get(self.fk.name), 'label', capfirst(self.fk.verbose_name))
|
|
}
|
|
if self.fk.rel.field_name != self.fk.rel.to._meta.pk.name:
|
|
kwargs['to_field'] = self.fk.rel.field_name
|
|
form.fields[self.fk.name] = InlineForeignKeyField(self.instance, **kwargs)
|
|
|
|
def inlineformset_factory(*args, **kwargs):
|
|
"""Changed inlineformset_factory function, where we use our own formfield_callback"""
|
|
kwargs["formfield_callback"] = formfield_function
|
|
kwargs["formset"] = BaseInlineFormSet
|
|
return models.inlineformset_factory(*args, **kwargs)
|