Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

[1.0.X] Fixed #10349 -- Modified ManyToManyFields to allow initial fo…

…rm values to be callables. Thanks to fas for the report and patch.

Merge of r10652 from trunk.

git-svn-id: http://code.djangoproject.com/svn/django/branches/releases/1.0.X@10653 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information...
commit d22290b2ce00909c2f92b6dacadfbfb0deeefd7f 1 parent 3c222b1
Russell Keith-Magee authored May 02, 2009
5  django/db/models/fields/related.py
@@ -943,7 +943,10 @@ def formfield(self, **kwargs):
24  tests/modeltests/model_forms/models.py
@@ -569,6 +569,30 @@ def __unicode__(self):
569 569
 <option value="3">Third test</option>
570 570
 </select>  Hold down "Control", or "Command" on a Mac, to select more than one.</li>
571 571
 
  572
+Initial values can be provided for model forms
  573
+>>> f = TestArticleForm(auto_id=False, initial={'headline': 'Your headline here', 'categories': ['1','2']})
  574
+>>> print f.as_ul()
  575
+<li>Headline: <input type="text" name="headline" value="Your headline here" maxlength="50" /></li>
  576
+<li>Slug: <input type="text" name="slug" maxlength="50" /></li>
  577
+<li>Pub date: <input type="text" name="pub_date" /></li>
  578
+<li>Writer: <select name="writer">
  579
+<option value="" selected="selected">---------</option>
  580
+<option value="1">Mike Royko</option>
  581
+<option value="2">Bob Woodward</option>
  582
+</select></li>
  583
+<li>Article: <textarea rows="10" cols="40" name="article"></textarea></li>
  584
+<li>Status: <select name="status">
  585
+<option value="" selected="selected">---------</option>
  586
+<option value="1">Draft</option>
  587
+<option value="2">Pending</option>
  588
+<option value="3">Live</option>
  589
+</select></li>
  590
+<li>Categories: <select multiple="multiple" name="categories">
  591
+<option value="1" selected="selected">Entertainment</option>
  592
+<option value="2" selected="selected">It&#39;s a test</option>
  593
+<option value="3">Third test</option>
  594
+</select>  Hold down "Control", or "Command" on a Mac, to select more than one.</li>
  595
+
572 596
 >>> f = TestArticleForm({'headline': u'New headline', 'slug': u'new-headline', 'pub_date': u'1988-01-04',
573 597
 ...     'writer': u'1', 'article': u'Hello.', 'categories': [u'1', u'2']}, instance=new_art)
574 598
 >>> new_art = f.save()
48  tests/regressiontests/forms/forms.py
@@ -1146,37 +1146,63 @@
1146 1146
 >>> class UserRegistration(Form):
1147 1147
 ...    username = CharField(max_length=10)
1148 1148
 ...    password = CharField(widget=PasswordInput)
  1149
+...    options = MultipleChoiceField(choices=[('f','foo'),('b','bar'),('w','whiz')])
1149 1150
 
1150 1151
 We need to define functions that get called later.
1151 1152
 >>> def initial_django():
1152 1153
 ...     return 'django'
1153 1154
 >>> def initial_stephane():
1154 1155
 ...     return 'stephane'
  1156
+>>> def initial_options():
  1157
+...     return ['f','b']
  1158
+>>> def initial_other_options():
  1159
+...     return ['b','w']
  1160
+
1155 1161
 
1156 1162
 Here, we're not submitting any data, so the initial value will be displayed.
1157  
->>> p = UserRegistration(initial={'username': initial_django}, auto_id=False)
  1163
+>>> p = UserRegistration(initial={'username': initial_django, 'options': initial_options}, auto_id=False)
1158 1164
 >>> print p.as_ul()
1159 1165
 <li>Username: <input type="text" name="username" value="django" maxlength="10" /></li>
1160 1166
 <li>Password: <input type="password" name="password" /></li>
  1167
+<li>Options: <select multiple="multiple" name="options">
  1168
+<option value="f" selected="selected">foo</option>
  1169
+<option value="b" selected="selected">bar</option>
  1170
+<option value="w">whiz</option>
  1171
+</select></li>
1161 1172
 
1162 1173
 The 'initial' parameter is meaningless if you pass data.
1163  
->>> p = UserRegistration({}, initial={'username': initial_django}, auto_id=False)
  1174
+>>> p = UserRegistration({}, initial={'username': initial_django, 'options': initial_options}, auto_id=False)
1164 1175
 >>> print p.as_ul()
1165 1176
 <li><ul class="errorlist"><li>This field is required.</li></ul>Username: <input type="text" name="username" maxlength="10" /></li>
1166 1177
 <li><ul class="errorlist"><li>This field is required.</li></ul>Password: <input type="password" name="password" /></li>
  1178
+<li><ul class="errorlist"><li>This field is required.</li></ul>Options: <select multiple="multiple" name="options">
  1179
+<option value="f">foo</option>
  1180
+<option value="b">bar</option>
  1181
+<option value="w">whiz</option>
  1182
+</select></li>
1167 1183
 >>> p = UserRegistration({'username': u''}, initial={'username': initial_django}, auto_id=False)
1168 1184
 >>> print p.as_ul()
1169 1185
 <li><ul class="errorlist"><li>This field is required.</li></ul>Username: <input type="text" name="username" maxlength="10" /></li>
1170 1186
 <li><ul class="errorlist"><li>This field is required.</li></ul>Password: <input type="password" name="password" /></li>
1171  
->>> p = UserRegistration({'username': u'foo'}, initial={'username': initial_django}, auto_id=False)
  1187
+<li><ul class="errorlist"><li>This field is required.</li></ul>Options: <select multiple="multiple" name="options">
  1188
+<option value="f">foo</option>
  1189
+<option value="b">bar</option>
  1190
+<option value="w">whiz</option>
  1191
+</select></li>
  1192
+>>> p = UserRegistration({'username': u'foo', 'options':['f','b']}, initial={'username': initial_django}, auto_id=False)
1172 1193
 >>> print p.as_ul()
1173 1194
 <li>Username: <input type="text" name="username" value="foo" maxlength="10" /></li>
1174 1195
 <li><ul class="errorlist"><li>This field is required.</li></ul>Password: <input type="password" name="password" /></li>
  1196
+<li>Options: <select multiple="multiple" name="options">
  1197
+<option value="f" selected="selected">foo</option>
  1198
+<option value="b" selected="selected">bar</option>
  1199
+<option value="w">whiz</option>
  1200
+</select></li>
1175 1201
 
1176 1202
 A callable 'initial' value is *not* used as a fallback if data is not provided.
1177 1203
 In this example, we don't provide a value for 'username', and the form raises a
1178 1204
 validation error rather than using the initial value for 'username'.
1179  
->>> p = UserRegistration({'password': 'secret'}, initial={'username': initial_django})
  1205
+>>> p = UserRegistration({'password': 'secret'}, initial={'username': initial_django, 'options': initial_options})
1180 1206
 >>> p.errors['username']
1181 1207
 [u'This field is required.']
1182 1208
 >>> p.is_valid()
@@ -1187,14 +1213,26 @@
1187 1213
 >>> class UserRegistration(Form):
1188 1214
 ...    username = CharField(max_length=10, initial=initial_django)
1189 1215
 ...    password = CharField(widget=PasswordInput)
  1216
+...    options = MultipleChoiceField(choices=[('f','foo'),('b','bar'),('w','whiz')], initial=initial_other_options)
  1217
+
1190 1218
 >>> p = UserRegistration(auto_id=False)
1191 1219
 >>> print p.as_ul()
1192 1220
 <li>Username: <input type="text" name="username" value="django" maxlength="10" /></li>
1193 1221
 <li>Password: <input type="password" name="password" /></li>
1194  
->>> p = UserRegistration(initial={'username': initial_stephane}, auto_id=False)
  1222
+<li>Options: <select multiple="multiple" name="options">
  1223
+<option value="f">foo</option>
  1224
+<option value="b" selected="selected">bar</option>
  1225
+<option value="w" selected="selected">whiz</option>
  1226
+</select></li>
  1227
+>>> p = UserRegistration(initial={'username': initial_stephane, 'options': initial_options}, auto_id=False)
1195 1228
 >>> print p.as_ul()
1196 1229
 <li>Username: <input type="text" name="username" value="stephane" maxlength="10" /></li>
1197 1230
 <li>Password: <input type="password" name="password" /></li>
  1231
+<li>Options: <select multiple="multiple" name="options">
  1232
+<option value="f" selected="selected">foo</option>
  1233
+<option value="b" selected="selected">bar</option>
  1234
+<option value="w">whiz</option>
  1235
+</select></li>
1198 1236
 
1199 1237
 # Help text ###################################################################
1200 1238
 
14  tests/regressiontests/model_forms_regress/models.py
@@ -14,3 +14,17 @@ class Meta:
14 14
 
15 15
 class FilePathModel(models.Model):
16 16
     path = models.FilePathField(path=os.path.dirname(__file__), match=".*\.py$", blank=True)
  17
+
  18
+class Publication(models.Model):
  19
+    title = models.CharField(max_length=30)
  20
+    date = models.DateField()
  21
+
  22
+    def __unicode__(self):
  23
+        return self.title
  24
+
  25
+class Article(models.Model):
  26
+    headline = models.CharField(max_length=100)
  27
+    publications = models.ManyToManyField(Publication)
  28
+
  29
+    def __unicode__(self):
  30
+        return self.headline
43  tests/regressiontests/model_forms_regress/tests.py
... ...
@@ -1,18 +1,22 @@
  1
+from datetime import date
  2
+
1 3
 from django import db
2 4
 from django import forms
  5
+from django.forms.models import modelform_factory
3 6
 from django.conf import settings
4 7
 from django.test import TestCase
5  
-from models import Person, Triple, FilePathModel
  8
+
  9
+from models import Person, Triple, FilePathModel, Article, Publication
6 10
 
7 11
 class ModelMultipleChoiceFieldTests(TestCase):
8  
-    
  12
+
9 13
     def setUp(self):
10 14
         self.old_debug = settings.DEBUG
11 15
         settings.DEBUG = True
12  
-        
  16
+
13 17
     def tearDown(self):
14 18
         settings.DEBUG = self.old_debug
15  
-    
  19
+
16 20
     def test_model_multiple_choice_number_of_queries(self):
17 21
         """
18 22
         Test that ModelMultipleChoiceField does O(1) queries instead of
@@ -20,11 +24,11 @@ def test_model_multiple_choice_number_of_queries(self):
20 24
         """
21 25
         for i in range(30):
22 26
             Person.objects.create(name="Person %s" % i)
23  
-        
  27
+
24 28
         db.reset_queries()
25 29
         f = forms.ModelMultipleChoiceField(queryset=Person.objects.all())
26 30
         selected = f.clean([1, 3, 5, 7, 9])
27  
-        self.assertEquals(len(db.connection.queries), 1)        
  31
+        self.assertEquals(len(db.connection.queries), 1)
28 32
 
29 33
 class TripleForm(forms.ModelForm):
30 34
     class Meta:
@@ -59,3 +63,30 @@ def test_file_path_field_blank(self):
59 63
         names = [p[1] for p in form['path'].field.choices]
60 64
         names.sort()
61 65
         self.assertEqual(names, ['---------', '__init__.py', 'models.py', 'tests.py'])
  66
+
  67
+class ManyToManyCallableInitialTests(TestCase):
  68
+    def test_callable(self):
  69
+        "Regression for #10349: A callable can be provided as the initial value for an m2m field"
  70
+
  71
+        # Set up a callable initial value
  72
+        def formfield_for_dbfield(db_field, **kwargs):
  73
+            if db_field.name == 'publications':
  74
+                kwargs['initial'] = lambda: Publication.objects.all().order_by('date')[:2]
  75
+            return db_field.formfield(**kwargs)
  76
+
  77
+        # Set up some Publications to use as data
  78
+        Publication(title="First Book", date=date(2007,1,1)).save()
  79
+        Publication(title="Second Book", date=date(2008,1,1)).save()
  80
+        Publication(title="Third Book", date=date(2009,1,1)).save()
  81
+
  82
+        # Create a ModelForm, instantiate it, and check that the output is as expected
  83
+        ModelForm = modelform_factory(Article, formfield_callback=formfield_for_dbfield)
  84
+        form = ModelForm()
  85
+        self.assertEquals(form.as_ul(), u"""<li><label for="id_headline">Headline:</label> <input id="id_headline" type="text" name="headline" maxlength="100" /></li>
  86
+<li><label for="id_publications">Publications:</label> <select multiple="multiple" name="publications" id="id_publications">
  87
+<option value="1" selected="selected">First Book</option>
  88
+<option value="2" selected="selected">Second Book</option>
  89
+<option value="3">Third Book</option>
  90
+</select>  Hold down "Control", or "Command" on a Mac, to select more than one.</li>""")
  91
+
  92
+

0 notes on commit d22290b

Please sign in to comment.
Something went wrong with that request. Please try again.