/dojango/forms/models.py
Python | 212 lines | 187 code | 10 blank | 15 comment | 3 complexity | 871b581bb42f3bf3b11f78effb72987a MD5 | raw file
1from django.forms import * 2from django.forms.models import BaseModelFormSet 3from django.forms.models import BaseInlineFormSet 4from django.forms.models import ModelChoiceIterator 5from django.forms.models import InlineForeignKeyHiddenInput, InlineForeignKeyField 6 7from django.utils.text import capfirst 8 9from formsets import BaseFormSet 10 11from django.db.models import fields 12 13from dojango.forms.fields import * 14from dojango.forms.widgets import DojoWidgetMixin, Textarea, Select, SelectMultiple, HiddenInput 15 16__all__ = ( 17 'ModelForm', 'BaseModelForm', 'model_to_dict', 'fields_for_model', 18 'save_instance', 'ModelChoiceField', 'ModelMultipleChoiceField', 19) 20 21class ModelChoiceField(DojoFieldMixin, models.ModelChoiceField): 22 """ 23 Overwritten 'ModelChoiceField' using the 'DojoFieldMixin' functionality. 24 """ 25 widget = Select 26 27class ModelMultipleChoiceField(DojoFieldMixin, models.ModelMultipleChoiceField): 28 """ 29 Overwritten 'ModelMultipleChoiceField' using the 'DojoFieldMixin' functonality. 30 """ 31 widget = SelectMultiple 32 33# Fields ##################################################################### 34 35class InlineForeignKeyHiddenInput(DojoWidgetMixin, InlineForeignKeyHiddenInput): 36 """ 37 Overwritten InlineForeignKeyHiddenInput to use the dojango widget mixin 38 """ 39 dojo_type = 'dijit.form.TextBox' # otherwise dijit.form.Form can't get its values 40 41class InlineForeignKeyField(DojoFieldMixin, InlineForeignKeyField, Field): 42 """ 43 Overwritten InlineForeignKeyField to use the dojango field mixin and passing 44 the dojango InlineForeignKeyHiddenInput as widget. 45 """ 46 def __init__(self, parent_instance, *args, **kwargs): 47 self.parent_instance = parent_instance 48 self.pk_field = kwargs.pop("pk_field", False) 49 self.to_field = kwargs.pop("to_field", None) 50 if self.parent_instance is not None: 51 if self.to_field: 52 kwargs["initial"] = getattr(self.parent_instance, self.to_field) 53 else: 54 kwargs["initial"] = self.parent_instance.pk 55 56 kwargs["required"] = False 57 kwargs["widget"] = InlineForeignKeyHiddenInput 58 # don't call the the superclass of this one. Use the superclass of the 59 # normal django InlineForeignKeyField 60 Field.__init__(self, *args, **kwargs) 61 62# our customized model field => form field map 63# here it is defined which form field is used by which model field, when creating a ModelForm 64MODEL_TO_FORM_FIELD_MAP = ( 65 # (model_field, form_field, [optional widget]) 66 # the order of these fields is very important for inherited model fields 67 # e.g. the CharField must be checked at last, because several other 68 # fields are a subclass of it. 69 (fields.CommaSeparatedIntegerField, CharField), 70 (fields.DateTimeField, DateTimeField), # must be in front of the DateField 71 (fields.DateField, DateField), 72 (fields.DecimalField, DecimalField), 73 (fields.EmailField, EmailField), 74 (fields.FilePathField, FilePathField), 75 (fields.FloatField, FloatField), 76 (fields.related.ForeignKey, ModelChoiceField), 77 (fields.files.ImageField, ImageField), 78 (fields.files.FileField, FileField), 79 (fields.IPAddressField, IPAddressField), 80 (fields.related.ManyToManyField, ModelMultipleChoiceField), 81 (fields.NullBooleanField, CharField), 82 (fields.BooleanField, BooleanField), 83 (fields.PositiveSmallIntegerField, IntegerField), 84 (fields.PositiveIntegerField, IntegerField), 85 (fields.SlugField, SlugField), 86 (fields.SmallIntegerField, IntegerField), 87 (fields.IntegerField, IntegerField), 88 (fields.TimeField, TimeField), 89 (fields.URLField, URLField), 90 (fields.TextField, CharField, Textarea), 91 (fields.CharField, CharField), 92) 93 94def formfield_function(field, **kwargs): 95 """ 96 Custom formfield function, so we can inject our own form fields. The 97 mapping of model fields to form fields is defined in 'MODEL_TO_FORM_FIELD_MAP'. 98 It uses the default django mapping as fallback, if there is no match in our 99 custom map. 100 101 field -- a model field 102 """ 103 for field_map in MODEL_TO_FORM_FIELD_MAP: 104 if isinstance(field, field_map[0]): 105 defaults = {} 106 if field.choices: 107 # the normal django field forms.TypedChoiceField is wired hard 108 # within the original db/models/fields.py. 109 # If we use our custom Select widget, we also have to pass in 110 # some additional validation field attributes. 111 defaults['widget'] = Select(attrs={ 112 'extra_field_attrs':{ 113 'required':not field.blank, 114 'help_text':field.help_text, 115 } 116 }) 117 elif len(field_map) == 3: 118 defaults['widget']=field_map[2] 119 defaults.update(kwargs) 120 return field.formfield(form_class=field_map[1], **defaults) 121 # return the default formfield, if there is no equivalent 122 return field.formfield(**kwargs) 123 124# ModelForms ################################################################# 125 126def fields_for_model(*args, **kwargs): 127 """Changed fields_for_model function, where we use our own formfield_callback""" 128 kwargs["formfield_callback"] = formfield_function 129 return models.fields_for_model(*args, **kwargs) 130 131class ModelFormMetaclass(models.ModelFormMetaclass): 132 """ 133 Overwritten 'ModelFormMetaClass'. We attach our own formfield generation 134 function. 135 """ 136 def __new__(cls, name, bases, attrs): 137 # this is how we can replace standard django form fields with dojo ones 138 attrs["formfield_callback"] = formfield_function 139 return super(ModelFormMetaclass, cls).__new__(cls, name, bases, attrs) 140 141class ModelForm(models.ModelForm): 142 """ 143 Overwritten 'ModelForm' using the metaclass defined above. 144 """ 145 __metaclass__ = ModelFormMetaclass 146 147def modelform_factory(*args, **kwargs): 148 """Changed modelform_factory function, where we use our own formfield_callback""" 149 kwargs["formfield_callback"] = formfield_function 150 kwargs["form"] = ModelForm 151 return models.modelform_factory(*args, **kwargs) 152 153# ModelFormSets ############################################################## 154 155class BaseModelFormSet(BaseModelFormSet, BaseFormSet): 156 157 def add_fields(self, form, index): 158 """Overwritten BaseModelFormSet using the dojango BaseFormSet and 159 the ModelChoiceField. 160 NOTE: This method was copied from django 1.3 beta 1""" 161 from django.db.models import AutoField, OneToOneField, ForeignKey 162 self._pk_field = pk = self.model._meta.pk 163 def pk_is_not_editable(pk): 164 return ((not pk.editable) or (pk.auto_created or isinstance(pk, AutoField)) 165 or (pk.rel and pk.rel.parent_link and pk_is_not_editable(pk.rel.to._meta.pk))) 166 if pk_is_not_editable(pk) or pk.name not in form.fields: 167 if form.is_bound: 168 pk_value = form.instance.pk 169 else: 170 try: 171 if index is not None: 172 pk_value = self.get_queryset()[index].pk 173 else: 174 pk_value = None 175 except IndexError: 176 pk_value = None 177 if isinstance(pk, OneToOneField) or isinstance(pk, ForeignKey): 178 qs = pk.rel.to._default_manager.get_query_set() 179 else: 180 qs = self.model._default_manager.get_query_set() 181 qs = qs.using(form.instance._state.db) 182 form.fields[self._pk_field.name] = ModelChoiceField(qs, initial=pk_value, required=False, widget=HiddenInput) 183 BaseFormSet.add_fields(self, form, index) 184 185def modelformset_factory(*args, **kwargs): 186 """Changed modelformset_factory function, where we use our own formfield_callback""" 187 kwargs["formfield_callback"] = formfield_function 188 kwargs["formset"] = BaseModelFormSet 189 return models.modelformset_factory(*args, **kwargs) 190 191# InlineFormSets ############################################################# 192 193class BaseInlineFormSet(BaseInlineFormSet, BaseModelFormSet): 194 """Overwritten BaseInlineFormSet using the dojango InlineForeignKeyFields. 195 NOTE: This method was copied from django 1.1""" 196 def add_fields(self, form, index): 197 super(BaseInlineFormSet, self).add_fields(form, index) 198 if self._pk_field == self.fk: 199 form.fields[self._pk_field.name] = InlineForeignKeyField(self.instance, pk_field=True) 200 else: 201 kwargs = { 202 'label': getattr(form.fields.get(self.fk.name), 'label', capfirst(self.fk.verbose_name)) 203 } 204 if self.fk.rel.field_name != self.fk.rel.to._meta.pk.name: 205 kwargs['to_field'] = self.fk.rel.field_name 206 form.fields[self.fk.name] = InlineForeignKeyField(self.instance, **kwargs) 207 208def inlineformset_factory(*args, **kwargs): 209 """Changed inlineformset_factory function, where we use our own formfield_callback""" 210 kwargs["formfield_callback"] = formfield_function 211 kwargs["formset"] = BaseInlineFormSet 212 return models.inlineformset_factory(*args, **kwargs)