Ticket #6630: 01-move-fields-and-exclude.diff
File 01-move-fields-and-exclude.diff, 12.8 KB (added by , 14 years ago) |
---|
-
django/contrib/auth/forms.py
diff --git a/django/contrib/auth/forms.py b/django/contrib/auth/forms.py
a b 20 20 21 21 class Meta: 22 22 model = User 23 fields = ("username", )23 fields = ("username", "password1", "password2") 24 24 25 25 def clean_username(self): 26 26 username = self.cleaned_data["username"] -
django/forms/forms.py
diff --git a/django/forms/forms.py b/django/forms/forms.py
a b 23 23 return u'' 24 24 return name.replace('_', ' ').capitalize() 25 25 26 def get_declared_fields( bases, attrs, with_base_fields=True):26 def get_declared_fields(attrs): 27 27 """ 28 Create a list of form field instances from the passed in 'attrs', plus any 29 similar fields on the base classes (in 'bases'). This is used by both the 30 Form and ModelForm metclasses. 31 32 If 'with_base_fields' is True, all fields from the bases are used. 33 Otherwise, only fields in the 'declared_fields' attribute on the bases are 34 used. The distinction is useful in ModelForm subclassing. 35 Also integrates any additional media definitions 28 Create a list of form field instances from the passed in 'attrs'. This is 29 used by both the Form and ModelForm metaclasses. 36 30 """ 37 fields = [(field_name, attrs.pop(field_name)) for field_name, obj in attrs.items() if isinstance(obj, Field)] 31 fields = [(field_name, attrs.pop(field_name)) for field_name, obj 32 in attrs.items() if isinstance(obj, Field)] 38 33 fields.sort(lambda x, y: cmp(x[1].creation_counter, y[1].creation_counter)) 39 40 # If this class is subclassing another Form, add that Form's fields.41 # Note that we loop over the bases in *reverse*. This is necessary in42 # order to preserve the correct order of fields.43 if with_base_fields:44 for base in bases[::-1]:45 if hasattr(base, 'base_fields'):46 fields = base.base_fields.items() + fields47 else:48 for base in bases[::-1]:49 if hasattr(base, 'declared_fields'):50 fields = base.declared_fields.items() + fields51 52 34 return SortedDict(fields) 53 35 54 class DeclarativeFieldsMetaclass(type): 36 def get_base_fields(new_class): 37 """ 38 Create a list of form field instances declared on the form and its base 39 classes. This is used by both the Form and ModelForm metaclasses. 40 41 """ 42 # Note that we loop over the bases in *reverse*. This is necessary in order 43 # to preserve the correct order of fields. 44 fields = SortedDict() 45 for base in reversed(new_class.mro()): 46 if hasattr(base, 'declared_fields'): 47 fields.update(base.declared_fields) 48 return fields 49 50 def select_fields(fields, opts): 51 """ 52 Select some fields based on options. This is used by both the Form and 53 ModelForm metaclasses. 54 55 Option ``fields`` is an optional list of field names. If provided, only 56 the named fields will be included in the returned fields and fields are 57 sorted by it. 58 59 Option ``exclude`` is an optional list of field names. If provided, 60 the named fields will be excluded from the returned fields, even if they 61 are listed in the ``fields`` argument. 62 """ 63 selected_fields = SortedDict() 64 for field_name, field in fields.items(): 65 if opts.fields and not field_name in opts.fields: 66 continue 67 if opts.exclude and field_name in opts.exclude: 68 continue 69 selected_fields[field_name] = field 70 if opts.fields: 71 selected_fields = SortedDict((field_name, selected_fields.get(field_name)) 72 for field_name in opts.fields if field_name in selected_fields) 73 return selected_fields 74 75 class FormOptions(object): 76 def __init__(self, options=None): 77 self.fields = getattr(options, 'fields', None) 78 self.exclude = getattr(options, 'exclude', None) 79 80 class FormMetaclass(type): 55 81 """ 56 82 Metaclass that converts Field attributes to a dictionary called 57 'base_fields', taking into account parent class 'base_fields' as well. 83 'base_fields', taking into account parent class fields as well. 84 Also integrates any additional media definitions. 58 85 """ 59 86 def __new__(cls, name, bases, attrs): 60 attrs['base_fields'] = get_declared_fields(bases, attrs) 61 new_class = super(DeclarativeFieldsMetaclass, 62 cls).__new__(cls, name, bases, attrs) 87 attrs['declared_fields'] = get_declared_fields(attrs) 88 new_class = super(FormMetaclass, cls).__new__(cls, name, bases, attrs) 89 opts = new_class._meta = FormOptions(getattr(new_class, 'Meta', None)) 90 new_class.base_fields = select_fields(get_base_fields(new_class), opts) 63 91 if 'media' not in attrs: 64 92 new_class.media = media_property(new_class) 65 93 return new_class … … 384 412 # fancy metaclass stuff purely for the semantic sugar -- it allows one 385 413 # to define a form using declarative syntax. 386 414 # BaseForm itself has no way of designating self.fields. 387 __metaclass__ = DeclarativeFieldsMetaclass415 __metaclass__ = FormMetaclass 388 416 389 417 class BoundField(StrAndUnicode): 390 418 "A Field plus data" -
django/forms/models.py
diff --git a/django/forms/models.py b/django/forms/models.py
a b 12 12 from django.core.exceptions import ValidationError, NON_FIELD_ERRORS 13 13 from django.core.validators import EMPTY_VALUES 14 14 from util import ErrorList 15 from forms import BaseForm, get_declared_fields 15 from forms import BaseForm, get_declared_fields, get_base_fields, select_fields 16 16 from fields import Field, ChoiceField 17 17 from widgets import SelectMultiple, HiddenInput, MultipleHiddenInput 18 18 from widgets import media_property … … 195 195 self.exclude = getattr(options, 'exclude', None) 196 196 self.widgets = getattr(options, 'widgets', None) 197 197 198 199 198 class ModelFormMetaclass(type): 200 199 def __new__(cls, name, bases, attrs): 201 200 formfield_callback = attrs.pop('formfield_callback', 202 201 lambda f, **kwargs: f.formfield(**kwargs)) 203 try: 204 parents = [b for b in bases if issubclass(b, ModelForm)] 205 except NameError: 206 # We are defining ModelForm itself. 207 parents = None 208 declared_fields = get_declared_fields(bases, attrs, False) 209 new_class = super(ModelFormMetaclass, cls).__new__(cls, name, bases, 210 attrs) 211 if not parents: 212 return new_class 213 214 if 'media' not in attrs: 215 new_class.media = media_property(new_class) 202 attrs['declared_fields'] = get_declared_fields(attrs) 203 new_class = super(ModelFormMetaclass, cls).__new__(cls, name, bases, attrs) 216 204 opts = new_class._meta = ModelFormOptions(getattr(new_class, 'Meta', None)) 217 205 if opts.model: 218 206 # If a model is defined, extract form fields from it. 219 fields = fields_for_model(opts.model, opts.fields,220 opts.exclude, opts.widgets,formfield_callback)207 fields = fields_for_model(opts.model, widgets=opts.widgets, 208 formfield_callback=formfield_callback) 221 209 # Override default model fields with any custom declared ones 222 210 # (plus, include all the other declared fields). 223 fields.update( declared_fields)211 fields.update(get_base_fields(new_class)) 224 212 else: 225 fields = declared_fields 226 new_class.declared_fields = declared_fields 227 new_class.base_fields = fields 213 fields = get_base_fields(new_class) 214 new_class.base_fields = select_fields(fields, opts) 215 if 'media' not in attrs: 216 new_class.media = media_property(new_class) 228 217 return new_class 229 218 230 219 class BaseModelForm(BaseForm): -
tests/regressiontests/forms/forms.py
diff --git a/tests/regressiontests/forms/forms.py b/tests/regressiontests/forms/forms.py
a b 928 928 <tr><th>Field13:</th><td><input type="text" name="field13" /></td></tr> 929 929 <tr><th>Field14:</th><td><input type="text" name="field14" /></td></tr> 930 930 931 It is possible to select some fields and reorder them. 932 >>> class OrderedTestForm(TestForm): 933 ... class Meta: 934 ... fields = ('field4', 'field2', 'field11', 'field8') 935 >>> p = OrderedTestForm(auto_id=False) 936 >>> print p 937 <tr><th>Field4:</th><td><input type="text" name="field4" /></td></tr> 938 <tr><th>Field2:</th><td><input type="text" name="field2" /></td></tr> 939 <tr><th>Field11:</th><td><input type="text" name="field11" /></td></tr> 940 <tr><th>Field8:</th><td><input type="text" name="field8" /></td></tr> 941 942 Or to exlude some fields. 943 >>> class ExcludingTestForm(TestForm): 944 ... class Meta: 945 ... exclude = ('field14', 'field3', 'field5', 'field8', 'field1') 946 >>> p = ExcludingTestForm(auto_id=False) 947 >>> print p 948 <tr><th>Field2:</th><td><input type="text" name="field2" /></td></tr> 949 <tr><th>Field4:</th><td><input type="text" name="field4" /></td></tr> 950 <tr><th>Field6:</th><td><input type="text" name="field6" /></td></tr> 951 <tr><th>Field7:</th><td><input type="text" name="field7" /></td></tr> 952 <tr><th>Field9:</th><td><input type="text" name="field9" /></td></tr> 953 <tr><th>Field10:</th><td><input type="text" name="field10" /></td></tr> 954 <tr><th>Field11:</th><td><input type="text" name="field11" /></td></tr> 955 <tr><th>Field12:</th><td><input type="text" name="field12" /></td></tr> 956 <tr><th>Field13:</th><td><input type="text" name="field13" /></td></tr> 957 958 Or to use fields and exlude at once. 959 >>> class CombinedTestForm(TestForm): 960 ... class Meta: 961 ... fields = ('field4', 'field2', 'field11', 'field8') 962 ... exclude = ('field14', 'field3', 'field5', 'field8', 'field1') 963 >>> p = CombinedTestForm(auto_id=False) 964 >>> print p 965 <tr><th>Field4:</th><td><input type="text" name="field4" /></td></tr> 966 <tr><th>Field2:</th><td><input type="text" name="field2" /></td></tr> 967 <tr><th>Field11:</th><td><input type="text" name="field11" /></td></tr> 968 931 969 Some Field classes have an effect on the HTML attributes of their associated 932 970 Widget. If you set max_length in a CharField and its associated widget is 933 971 either a TextInput or PasswordInput, then the widget's rendered HTML will … … 1314 1352 <li>Birthday: <input type="text" name="birthday" /></li> 1315 1353 <li>Instrument: <input type="text" name="instrument" /></li> 1316 1354 1317 Yes, you can subclass multiple forms. The fields are added in the order in1318 which the parent classes are listed.1355 Yes, you can subclass multiple forms. The fields are added in the order given 1356 by the method resolution order. 1319 1357 >>> class Person(Form): 1320 1358 ... first_name = CharField() 1321 1359 ... last_name = CharField() 1322 1360 ... birthday = DateField() 1323 1361 >>> class Instrument(Form): 1324 1362 ... instrument = CharField() 1325 >>> class Beatle( Person, Instrument):1363 >>> class Beatle(Instrument, Person): 1326 1364 ... haircut_type = CharField() 1327 1365 >>> b = Beatle(auto_id=False) 1328 1366 >>> print b.as_ul() … … 1332 1370 <li>Instrument: <input type="text" name="instrument" /></li> 1333 1371 <li>Haircut type: <input type="text" name="haircut_type" /></li> 1334 1372 1373 You can use also more complex inheritence. 1374 >>> class FormA(Form): 1375 ... field_a = CharField() 1376 >>> class FormB(Form): 1377 ... field_b = CharField() 1378 >>> class FormC(Form): 1379 ... field_c = CharField() 1380 >>> class FormD(FormB, FormC): 1381 ... field_d = CharField() 1382 >>> class FormE(FormC, FormA): 1383 ... field_e = CharField() 1384 >>> class FormF(FormD, FormE): 1385 ... field_f = CharField() 1386 >>> print FormF(auto_id=False).as_ul() 1387 <li>Field a: <input type="text" name="field_a" /></li> 1388 <li>Field c: <input type="text" name="field_c" /></li> 1389 <li>Field e: <input type="text" name="field_e" /></li> 1390 <li>Field b: <input type="text" name="field_b" /></li> 1391 <li>Field d: <input type="text" name="field_d" /></li> 1392 <li>Field f: <input type="text" name="field_f" /></li> 1393 1394 But you can reorder fields by meta attributes. 1395 >>> class FormG(FormF): 1396 ... field_g = CharField() 1397 ... class Meta: 1398 ... fields = ('field_c', 'field_e', 'field_a', 'field_b', 'field_g') 1399 ... exclude = ('field_b', 'field_d') 1400 >>> print FormG(auto_id=False).as_ul() 1401 <li>Field c: <input type="text" name="field_c" /></li> 1402 <li>Field e: <input type="text" name="field_e" /></li> 1403 <li>Field a: <input type="text" name="field_a" /></li> 1404 <li>Field g: <input type="text" name="field_g" /></li> 1405 1335 1406 # Forms with prefixes ######################################################### 1336 1407 1337 1408 Sometimes it's necessary to have multiple forms display on the same HTML page, -
tests/regressiontests/forms/models.py
diff --git a/tests/regressiontests/forms/models.py b/tests/regressiontests/forms/models.py
a b 145 145 >>> f.is_valid() 146 146 True 147 147 >>> f.cleaned_data['name'] 148 u'Hello' 148 Traceback (most recent call last): 149 ... 150 KeyError: 'name' 149 151 >>> obj = f.save() 150 152 >>> obj.name 151 153 u'class default value'