Fixed #6162. ModelForm's __init__ signature now matches Form's. This is a backwards incompatbile change. Based largely on a patch by ubernostrum.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@6915 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Joseph Kocherhans 2007-12-13 02:48:04 +00:00
parent f9410dc40d
commit e415eff0ea
3 changed files with 48 additions and 40 deletions

View File

@ -262,11 +262,16 @@ class ModelFormMetaclass(type):
return type.__new__(cls, name, bases, attrs) return type.__new__(cls, name, bases, attrs)
class BaseModelForm(BaseForm): class BaseModelForm(BaseForm):
def __init__(self, instance, data=None, files=None, auto_id='id_%s', prefix=None, def __init__(self, data=None, files=None, auto_id='id_%s', prefix=None,
initial=None, error_class=ErrorList, label_suffix=':'): initial=None, error_class=ErrorList, label_suffix=':', instance=None):
self.instance = instance
opts = self._meta opts = self._meta
object_data = model_to_dict(instance, opts.fields, opts.exclude) if instance is None:
# if we didn't get an instance, instantiate a new one
self.instance = opts.model()
object_data = {}
else:
self.instance = instance
object_data = model_to_dict(instance, opts.fields, opts.exclude)
# if initial was provided, it should override the values from instance # if initial was provided, it should override the values from instance
if initial is not None: if initial is not None:
object_data.update(initial) object_data.update(initial)

View File

@ -24,12 +24,11 @@ For example::
... model = Article ... model = Article
# Creating a form to add an article. # Creating a form to add an article.
>>> article = Article() >>> form = ArticleForm()
>>> form = ArticleForm(article)
# Creating a form to change an existing article. # Creating a form to change an existing article.
>>> article = Article.objects.get(pk=1) >>> article = Article.objects.get(pk=1)
>>> form = ArticleForm(article) >>> form = ArticleForm(instance=article)
Field types Field types
----------- -----------
@ -166,18 +165,23 @@ we'll discuss in a moment.)::
The ``save()`` method The ``save()`` method
--------------------- ---------------------
Every form produced by ``ModelForm`` also has a ``save()`` method. This Every form produced by ``ModelForm`` also has a ``save()``
method creates and saves a database object from the data bound to the form. method. This method creates and saves a database object from the data
A subclass of ``ModelForm`` also requires a model instance as the first bound to the form. A subclass of ``ModelForm`` can accept an existing
arument to its constructor. For example:: model instance as the keyword argument ``instance``; if this is
supplied, ``save()`` will update that instance. If it's not supplied,
``save()`` will create a new instance of the specified model::
# Create a form instance from POST data. # Create a form instance from POST data.
>>> a = Article() >>> f = ArticleForm(request.POST)
>>> f = ArticleForm(a, request.POST)
# Save a new Article object from the form's data. # Save a new Article object from the form's data.
>>> new_article = f.save() >>> new_article = f.save()
# Create a form to edit an existing Article.
>>> a = Article.objects.get(pk=1)
>>> f = ArticleForm(instance=a)
Note that ``save()`` will raise a ``ValueError`` if the data in the form Note that ``save()`` will raise a ``ValueError`` if the data in the form
doesn't validate -- i.e., ``if form.errors``. doesn't validate -- i.e., ``if form.errors``.
@ -201,8 +205,7 @@ you've manually saved the instance produced by the form, you can invoke
``save_m2m()`` to save the many-to-many form data. For example:: ``save_m2m()`` to save the many-to-many form data. For example::
# Create a form instance with POST data. # Create a form instance with POST data.
>>> a = Author() >>> f = AuthorForm(request.POST)
>>> f = AuthorForm(a, request.POST)
# Create, but don't save the new author instance. # Create, but don't save the new author instance.
>>> new_author = f.save(commit=False) >>> new_author = f.save(commit=False)
@ -277,7 +280,7 @@ model fields:
manually set anyextra required fields:: manually set anyextra required fields::
instance = Instance(required_field='value') instance = Instance(required_field='value')
form = InstanceForm(instance, request.POST) form = InstanceForm(request.POST, instance=instance)
new_instance = form.save() new_instance = form.save()
instance = form.save(commit=False) instance = form.save(commit=False)

View File

@ -167,7 +167,7 @@ ImproperlyConfigured: BadForm's base classes define more than one model.
>>> class CategoryForm(ModelForm): >>> class CategoryForm(ModelForm):
... class Meta: ... class Meta:
... model = Category ... model = Category
>>> f = CategoryForm(Category()) >>> f = CategoryForm()
>>> print f >>> print f
<tr><th><label for="id_name">Name:</label></th><td><input id="id_name" type="text" name="name" maxlength="20" /></td></tr> <tr><th><label for="id_name">Name:</label></th><td><input id="id_name" type="text" name="name" maxlength="20" /></td></tr>
<tr><th><label for="id_slug">Slug:</label></th><td><input id="id_slug" type="text" name="slug" maxlength="20" /></td></tr> <tr><th><label for="id_slug">Slug:</label></th><td><input id="id_slug" type="text" name="slug" maxlength="20" /></td></tr>
@ -179,13 +179,13 @@ ImproperlyConfigured: BadForm's base classes define more than one model.
>>> print f['name'] >>> print f['name']
<input id="id_name" type="text" name="name" maxlength="20" /> <input id="id_name" type="text" name="name" maxlength="20" />
>>> f = CategoryForm(Category(), auto_id=False) >>> f = CategoryForm(auto_id=False)
>>> print f.as_ul() >>> print f.as_ul()
<li>Name: <input type="text" name="name" maxlength="20" /></li> <li>Name: <input type="text" name="name" maxlength="20" /></li>
<li>Slug: <input type="text" name="slug" maxlength="20" /></li> <li>Slug: <input type="text" name="slug" maxlength="20" /></li>
<li>The URL: <input type="text" name="url" maxlength="40" /></li> <li>The URL: <input type="text" name="url" maxlength="40" /></li>
>>> f = CategoryForm(Category(), {'name': 'Entertainment', 'slug': 'entertainment', 'url': 'entertainment'}) >>> f = CategoryForm({'name': 'Entertainment', 'slug': 'entertainment', 'url': 'entertainment'})
>>> f.is_valid() >>> f.is_valid()
True True
>>> f.cleaned_data >>> f.cleaned_data
@ -196,7 +196,7 @@ True
>>> Category.objects.all() >>> Category.objects.all()
[<Category: Entertainment>] [<Category: Entertainment>]
>>> f = CategoryForm(Category(), {'name': "It's a test", 'slug': 'its-test', 'url': 'test'}) >>> f = CategoryForm({'name': "It's a test", 'slug': 'its-test', 'url': 'test'})
>>> f.is_valid() >>> f.is_valid()
True True
>>> f.cleaned_data >>> f.cleaned_data
@ -210,7 +210,7 @@ True
If you call save() with commit=False, then it will return an object that If you call save() with commit=False, then it will return an object that
hasn't yet been saved to the database. In this case, it's up to you to call hasn't yet been saved to the database. In this case, it's up to you to call
save() on the resulting model instance. save() on the resulting model instance.
>>> f = CategoryForm(Category(), {'name': 'Third test', 'slug': 'third-test', 'url': 'third'}) >>> f = CategoryForm({'name': 'Third test', 'slug': 'third-test', 'url': 'third'})
>>> f.is_valid() >>> f.is_valid()
True True
>>> f.cleaned_data >>> f.cleaned_data
@ -225,7 +225,7 @@ True
[<Category: Entertainment>, <Category: It's a test>, <Category: Third test>] [<Category: Entertainment>, <Category: It's a test>, <Category: Third test>]
If you call save() with invalid data, you'll get a ValueError. If you call save() with invalid data, you'll get a ValueError.
>>> f = CategoryForm(Category(), {'name': '', 'slug': '', 'url': 'foo'}) >>> f = CategoryForm({'name': '', 'slug': '', 'url': 'foo'})
>>> f.errors >>> f.errors
{'name': [u'This field is required.'], 'slug': [u'This field is required.']} {'name': [u'This field is required.'], 'slug': [u'This field is required.']}
>>> f.cleaned_data >>> f.cleaned_data
@ -236,7 +236,7 @@ AttributeError: 'CategoryForm' object has no attribute 'cleaned_data'
Traceback (most recent call last): Traceback (most recent call last):
... ...
ValueError: The Category could not be created because the data didn't validate. ValueError: The Category could not be created because the data didn't validate.
>>> f = CategoryForm(Category(), {'name': '', 'slug': '', 'url': 'foo'}) >>> f = CategoryForm({'name': '', 'slug': '', 'url': 'foo'})
>>> f.save() >>> f.save()
Traceback (most recent call last): Traceback (most recent call last):
... ...
@ -253,7 +253,7 @@ fields with the 'choices' attribute are represented by a ChoiceField.
>>> class ArticleForm(ModelForm): >>> class ArticleForm(ModelForm):
... class Meta: ... class Meta:
... model = Article ... model = Article
>>> f = ArticleForm(Article(), auto_id=False) >>> f = ArticleForm(auto_id=False)
>>> print f >>> print f
<tr><th>Headline:</th><td><input type="text" name="headline" maxlength="50" /></td></tr> <tr><th>Headline:</th><td><input type="text" name="headline" maxlength="50" /></td></tr>
<tr><th>Slug:</th><td><input type="text" name="slug" maxlength="50" /></td></tr> <tr><th>Slug:</th><td><input type="text" name="slug" maxlength="50" /></td></tr>
@ -286,7 +286,7 @@ from the form can't provide a value for that field!
... class Meta: ... class Meta:
... model = Article ... model = Article
... fields = ('headline','pub_date') ... fields = ('headline','pub_date')
>>> f = PartialArticleForm(Article(), auto_id=False) >>> f = PartialArticleForm(auto_id=False)
>>> print f >>> print f
<tr><th>Headline:</th><td><input type="text" name="headline" maxlength="50" /></td></tr> <tr><th>Headline:</th><td><input type="text" name="headline" maxlength="50" /></td></tr>
<tr><th>Pub date:</th><td><input type="text" name="pub_date" /></td></tr> <tr><th>Pub date:</th><td><input type="text" name="pub_date" /></td></tr>
@ -298,7 +298,7 @@ current values are inserted as 'initial' data in each Field.
>>> class RoykoForm(ModelForm): >>> class RoykoForm(ModelForm):
... class Meta: ... class Meta:
... model = Writer ... model = Writer
>>> f = RoykoForm(w, auto_id=False) >>> f = RoykoForm(auto_id=False, instance=w)
>>> print f >>> print f
<tr><th>Name:</th><td><input type="text" name="name" value="Mike Royko" maxlength="50" /><br />Use both first and last names.</td></tr> <tr><th>Name:</th><td><input type="text" name="name" value="Mike Royko" maxlength="50" /><br />Use both first and last names.</td></tr>
@ -309,7 +309,7 @@ current values are inserted as 'initial' data in each Field.
>>> class TestArticleForm(ModelForm): >>> class TestArticleForm(ModelForm):
... class Meta: ... class Meta:
... model = Article ... model = Article
>>> f = TestArticleForm(art, auto_id=False) >>> f = TestArticleForm(auto_id=False, instance=art)
>>> print f.as_ul() >>> print f.as_ul()
<li>Headline: <input type="text" name="headline" value="Test article" maxlength="50" /></li> <li>Headline: <input type="text" name="headline" value="Test article" maxlength="50" /></li>
<li>Slug: <input type="text" name="slug" value="test-article" maxlength="50" /></li> <li>Slug: <input type="text" name="slug" value="test-article" maxlength="50" /></li>
@ -331,7 +331,7 @@ current values are inserted as 'initial' data in each Field.
<option value="2">It&#39;s a test</option> <option value="2">It&#39;s a test</option>
<option value="3">Third test</option> <option value="3">Third test</option>
</select> Hold down "Control", or "Command" on a Mac, to select more than one.</li> </select> Hold down "Control", or "Command" on a Mac, to select more than one.</li>
>>> f = TestArticleForm(art, {'headline': u'Test headline', 'slug': 'test-headline', 'pub_date': u'1984-02-06', 'writer': u'1', 'article': 'Hello.'}) >>> f = TestArticleForm({'headline': u'Test headline', 'slug': 'test-headline', 'pub_date': u'1984-02-06', 'writer': u'1', 'article': 'Hello.'}, instance=art)
>>> f.is_valid() >>> f.is_valid()
True True
>>> test_art = f.save() >>> test_art = f.save()
@ -347,7 +347,7 @@ by specifying a 'fields' argument to form_for_instance.
... class Meta: ... class Meta:
... model = Article ... model = Article
... fields=('headline', 'slug', 'pub_date') ... fields=('headline', 'slug', 'pub_date')
>>> f = PartialArticleForm(art, {'headline': u'New headline', 'slug': 'new-headline', 'pub_date': u'1988-01-04'}, auto_id=False) >>> f = PartialArticleForm({'headline': u'New headline', 'slug': 'new-headline', 'pub_date': u'1988-01-04'}, auto_id=False, instance=art)
>>> print f.as_ul() >>> print f.as_ul()
<li>Headline: <input type="text" name="headline" value="New headline" maxlength="50" /></li> <li>Headline: <input type="text" name="headline" value="New headline" maxlength="50" /></li>
<li>Slug: <input type="text" name="slug" value="new-headline" maxlength="50" /></li> <li>Slug: <input type="text" name="slug" value="new-headline" maxlength="50" /></li>
@ -370,7 +370,7 @@ Add some categories and test the many-to-many form output.
>>> class TestArticleForm(ModelForm): >>> class TestArticleForm(ModelForm):
... class Meta: ... class Meta:
... model = Article ... model = Article
>>> f = TestArticleForm(new_art, auto_id=False) >>> f = TestArticleForm(auto_id=False, instance=new_art)
>>> print f.as_ul() >>> print f.as_ul()
<li>Headline: <input type="text" name="headline" value="New headline" maxlength="50" /></li> <li>Headline: <input type="text" name="headline" value="New headline" maxlength="50" /></li>
<li>Slug: <input type="text" name="slug" value="new-headline" maxlength="50" /></li> <li>Slug: <input type="text" name="slug" value="new-headline" maxlength="50" /></li>
@ -393,8 +393,8 @@ Add some categories and test the many-to-many form output.
<option value="3">Third test</option> <option value="3">Third test</option>
</select> Hold down "Control", or "Command" on a Mac, to select more than one.</li> </select> Hold down "Control", or "Command" on a Mac, to select more than one.</li>
>>> f = TestArticleForm(new_art, {'headline': u'New headline', 'slug': u'new-headline', 'pub_date': u'1988-01-04', >>> f = TestArticleForm({'headline': u'New headline', 'slug': u'new-headline', 'pub_date': u'1988-01-04',
... 'writer': u'1', 'article': u'Hello.', 'categories': [u'1', u'2']}) ... 'writer': u'1', 'article': u'Hello.', 'categories': [u'1', u'2']}, instance=new_art)
>>> new_art = f.save() >>> new_art = f.save()
>>> new_art.id >>> new_art.id
1 1
@ -403,8 +403,8 @@ Add some categories and test the many-to-many form output.
[<Category: Entertainment>, <Category: It's a test>] [<Category: Entertainment>, <Category: It's a test>]
Now, submit form data with no categories. This deletes the existing categories. Now, submit form data with no categories. This deletes the existing categories.
>>> f = TestArticleForm(new_art, {'headline': u'New headline', 'slug': u'new-headline', 'pub_date': u'1988-01-04', >>> f = TestArticleForm({'headline': u'New headline', 'slug': u'new-headline', 'pub_date': u'1988-01-04',
... 'writer': u'1', 'article': u'Hello.'}) ... 'writer': u'1', 'article': u'Hello.'}, instance=new_art)
>>> new_art = f.save() >>> new_art = f.save()
>>> new_art.id >>> new_art.id
1 1
@ -416,7 +416,7 @@ Create a new article, with categories, via the form.
>>> class ArticleForm(ModelForm): >>> class ArticleForm(ModelForm):
... class Meta: ... class Meta:
... model = Article ... model = Article
>>> f = ArticleForm(Article(), {'headline': u'The walrus was Paul', 'slug': u'walrus-was-paul', 'pub_date': u'1967-11-01', >>> f = ArticleForm({'headline': u'The walrus was Paul', 'slug': u'walrus-was-paul', 'pub_date': u'1967-11-01',
... 'writer': u'1', 'article': u'Test.', 'categories': [u'1', u'2']}) ... 'writer': u'1', 'article': u'Test.', 'categories': [u'1', u'2']})
>>> new_art = f.save() >>> new_art = f.save()
>>> new_art.id >>> new_art.id
@ -429,7 +429,7 @@ Create a new article, with no categories, via the form.
>>> class ArticleForm(ModelForm): >>> class ArticleForm(ModelForm):
... class Meta: ... class Meta:
... model = Article ... model = Article
>>> f = ArticleForm(Article(), {'headline': u'The walrus was Paul', 'slug': u'walrus-was-paul', 'pub_date': u'1967-11-01', >>> f = ArticleForm({'headline': u'The walrus was Paul', 'slug': u'walrus-was-paul', 'pub_date': u'1967-11-01',
... 'writer': u'1', 'article': u'Test.'}) ... 'writer': u'1', 'article': u'Test.'})
>>> new_art = f.save() >>> new_art = f.save()
>>> new_art.id >>> new_art.id
@ -443,7 +443,7 @@ The m2m data won't be saved until save_m2m() is invoked on the form.
>>> class ArticleForm(ModelForm): >>> class ArticleForm(ModelForm):
... class Meta: ... class Meta:
... model = Article ... model = Article
>>> f = ArticleForm(Article(), {'headline': u'The walrus was Paul', 'slug': 'walrus-was-paul', 'pub_date': u'1967-11-01', >>> f = ArticleForm({'headline': u'The walrus was Paul', 'slug': 'walrus-was-paul', 'pub_date': u'1967-11-01',
... 'writer': u'1', 'article': u'Test.', 'categories': [u'1', u'2']}) ... 'writer': u'1', 'article': u'Test.', 'categories': [u'1', u'2']})
>>> new_art = f.save(commit=False) >>> new_art = f.save(commit=False)
@ -474,7 +474,7 @@ existing Category instance.
<Category: Third test> <Category: Third test>
>>> cat.id >>> cat.id
3 3
>>> form = ShortCategory(cat, {'name': 'Third', 'slug': 'third', 'url': '3rd'}) >>> form = ShortCategory({'name': 'Third', 'slug': 'third', 'url': '3rd'}, instance=cat)
>>> form.save() >>> form.save()
<Category: Third> <Category: Third>
>>> Category.objects.get(id=3) >>> Category.objects.get(id=3)
@ -486,7 +486,7 @@ the data in the database when the form is instantiated.
>>> class ArticleForm(ModelForm): >>> class ArticleForm(ModelForm):
... class Meta: ... class Meta:
... model = Article ... model = Article
>>> f = ArticleForm(Article(), auto_id=False) >>> f = ArticleForm(auto_id=False)
>>> print f.as_ul() >>> print f.as_ul()
<li>Headline: <input type="text" name="headline" maxlength="50" /></li> <li>Headline: <input type="text" name="headline" maxlength="50" /></li>
<li>Slug: <input type="text" name="slug" maxlength="50" /></li> <li>Slug: <input type="text" name="slug" maxlength="50" /></li>
@ -690,7 +690,7 @@ ValidationError: [u'Select a valid choice. 4 is not one of the available choices
>>> class PhoneNumberForm(ModelForm): >>> class PhoneNumberForm(ModelForm):
... class Meta: ... class Meta:
... model = PhoneNumber ... model = PhoneNumber
>>> f = PhoneNumberForm(PhoneNumber(), {'phone': '(312) 555-1212', 'description': 'Assistance'}) >>> f = PhoneNumberForm({'phone': '(312) 555-1212', 'description': 'Assistance'})
>>> f.is_valid() >>> f.is_valid()
True True
>>> f.cleaned_data >>> f.cleaned_data