From e415eff0ead50882525129b0d5fd8ca95f1d30a7 Mon Sep 17 00:00:00 2001 From: Joseph Kocherhans Date: Thu, 13 Dec 2007 02:48:04 +0000 Subject: [PATCH] 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 --- django/newforms/models.py | 13 ++++--- docs/modelforms.txt | 27 ++++++++------- tests/modeltests/model_forms/models.py | 48 +++++++++++++------------- 3 files changed, 48 insertions(+), 40 deletions(-) diff --git a/django/newforms/models.py b/django/newforms/models.py index 4eb61761a1f..8d6ee1fda27 100644 --- a/django/newforms/models.py +++ b/django/newforms/models.py @@ -262,11 +262,16 @@ class ModelFormMetaclass(type): return type.__new__(cls, name, bases, attrs) class BaseModelForm(BaseForm): - def __init__(self, instance, data=None, files=None, auto_id='id_%s', prefix=None, - initial=None, error_class=ErrorList, label_suffix=':'): - self.instance = instance + def __init__(self, data=None, files=None, auto_id='id_%s', prefix=None, + initial=None, error_class=ErrorList, label_suffix=':', instance=None): 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 is not None: object_data.update(initial) diff --git a/docs/modelforms.txt b/docs/modelforms.txt index 7b5975e51ef..372abf98114 100644 --- a/docs/modelforms.txt +++ b/docs/modelforms.txt @@ -24,12 +24,11 @@ For example:: ... model = Article # Creating a form to add an article. - >>> article = Article() - >>> form = ArticleForm(article) + >>> form = ArticleForm() # Creating a form to change an existing article. >>> article = Article.objects.get(pk=1) - >>> form = ArticleForm(article) + >>> form = ArticleForm(instance=article) Field types ----------- @@ -166,18 +165,23 @@ we'll discuss in a moment.):: The ``save()`` method --------------------- -Every form produced by ``ModelForm`` also has a ``save()`` method. This -method creates and saves a database object from the data bound to the form. -A subclass of ``ModelForm`` also requires a model instance as the first -arument to its constructor. For example:: +Every form produced by ``ModelForm`` also has a ``save()`` +method. This method creates and saves a database object from the data +bound to the form. A subclass of ``ModelForm`` can accept an existing +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. - >>> a = Article() - >>> f = ArticleForm(a, request.POST) + >>> f = ArticleForm(request.POST) # Save a new Article object from the form's data. >>> 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 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:: # Create a form instance with POST data. - >>> a = Author() - >>> f = AuthorForm(a, request.POST) + >>> f = AuthorForm(request.POST) # Create, but don't save the new author instance. >>> new_author = f.save(commit=False) @@ -277,7 +280,7 @@ model fields: manually set anyextra required fields:: instance = Instance(required_field='value') - form = InstanceForm(instance, request.POST) + form = InstanceForm(request.POST, instance=instance) new_instance = form.save() instance = form.save(commit=False) diff --git a/tests/modeltests/model_forms/models.py b/tests/modeltests/model_forms/models.py index cd929562707..5e9584496e6 100644 --- a/tests/modeltests/model_forms/models.py +++ b/tests/modeltests/model_forms/models.py @@ -167,7 +167,7 @@ ImproperlyConfigured: BadForm's base classes define more than one model. >>> class CategoryForm(ModelForm): ... class Meta: ... model = Category ->>> f = CategoryForm(Category()) +>>> f = CategoryForm() >>> print f @@ -179,13 +179,13 @@ ImproperlyConfigured: BadForm's base classes define more than one model. >>> print f['name'] ->>> f = CategoryForm(Category(), auto_id=False) +>>> f = CategoryForm(auto_id=False) >>> print f.as_ul()
  • Name:
  • Slug:
  • The URL:
  • ->>> f = CategoryForm(Category(), {'name': 'Entertainment', 'slug': 'entertainment', 'url': 'entertainment'}) +>>> f = CategoryForm({'name': 'Entertainment', 'slug': 'entertainment', 'url': 'entertainment'}) >>> f.is_valid() True >>> f.cleaned_data @@ -196,7 +196,7 @@ True >>> Category.objects.all() [] ->>> 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() True >>> f.cleaned_data @@ -210,7 +210,7 @@ True 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 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() True >>> f.cleaned_data @@ -225,7 +225,7 @@ True [, , ] 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 {'name': [u'This field is required.'], 'slug': [u'This field is required.']} >>> f.cleaned_data @@ -236,7 +236,7 @@ AttributeError: 'CategoryForm' object has no attribute 'cleaned_data' Traceback (most recent call last): ... 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() Traceback (most recent call last): ... @@ -253,7 +253,7 @@ fields with the 'choices' attribute are represented by a ChoiceField. >>> class ArticleForm(ModelForm): ... class Meta: ... model = Article ->>> f = ArticleForm(Article(), auto_id=False) +>>> f = ArticleForm(auto_id=False) >>> print f Headline: Slug: @@ -286,7 +286,7 @@ from the form can't provide a value for that field! ... class Meta: ... model = Article ... fields = ('headline','pub_date') ->>> f = PartialArticleForm(Article(), auto_id=False) +>>> f = PartialArticleForm(auto_id=False) >>> print f Headline: Pub date: @@ -298,7 +298,7 @@ current values are inserted as 'initial' data in each Field. >>> class RoykoForm(ModelForm): ... class Meta: ... model = Writer ->>> f = RoykoForm(w, auto_id=False) +>>> f = RoykoForm(auto_id=False, instance=w) >>> print f Name:
    Use both first and last names. @@ -309,7 +309,7 @@ current values are inserted as 'initial' data in each Field. >>> class TestArticleForm(ModelForm): ... class Meta: ... model = Article ->>> f = TestArticleForm(art, auto_id=False) +>>> f = TestArticleForm(auto_id=False, instance=art) >>> print f.as_ul()
  • Headline:
  • Slug:
  • @@ -331,7 +331,7 @@ current values are inserted as 'initial' data in each Field. Hold down "Control", or "Command" on a Mac, to select more than one. ->>> 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() True >>> test_art = f.save() @@ -347,7 +347,7 @@ by specifying a 'fields' argument to form_for_instance. ... class Meta: ... model = Article ... 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()
  • Headline:
  • Slug:
  • @@ -370,7 +370,7 @@ Add some categories and test the many-to-many form output. >>> class TestArticleForm(ModelForm): ... class Meta: ... model = Article ->>> f = TestArticleForm(new_art, auto_id=False) +>>> f = TestArticleForm(auto_id=False, instance=new_art) >>> print f.as_ul()
  • Headline:
  • Slug:
  • @@ -393,8 +393,8 @@ Add some categories and test the many-to-many form output. Hold down "Control", or "Command" on a Mac, to select more than one. ->>> f = TestArticleForm(new_art, {'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']}) +>>> 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']}, instance=new_art) >>> new_art = f.save() >>> new_art.id 1 @@ -403,8 +403,8 @@ Add some categories and test the many-to-many form output. [, ] 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', -... 'writer': u'1', 'article': u'Hello.'}) +>>> f = TestArticleForm({'headline': u'New headline', 'slug': u'new-headline', 'pub_date': u'1988-01-04', +... 'writer': u'1', 'article': u'Hello.'}, instance=new_art) >>> new_art = f.save() >>> new_art.id 1 @@ -416,7 +416,7 @@ Create a new article, with categories, via the form. >>> class ArticleForm(ModelForm): ... class Meta: ... 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']}) >>> new_art = f.save() >>> new_art.id @@ -429,7 +429,7 @@ Create a new article, with no categories, via the form. >>> class ArticleForm(ModelForm): ... class Meta: ... 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.'}) >>> new_art = f.save() >>> 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 Meta: ... 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']}) >>> new_art = f.save(commit=False) @@ -474,7 +474,7 @@ existing Category instance. >>> cat.id 3 ->>> form = ShortCategory(cat, {'name': 'Third', 'slug': 'third', 'url': '3rd'}) +>>> form = ShortCategory({'name': 'Third', 'slug': 'third', 'url': '3rd'}, instance=cat) >>> form.save() >>> Category.objects.get(id=3) @@ -486,7 +486,7 @@ the data in the database when the form is instantiated. >>> class ArticleForm(ModelForm): ... class Meta: ... model = Article ->>> f = ArticleForm(Article(), auto_id=False) +>>> f = ArticleForm(auto_id=False) >>> print f.as_ul()
  • Headline:
  • Slug:
  • @@ -690,7 +690,7 @@ ValidationError: [u'Select a valid choice. 4 is not one of the available choices >>> class PhoneNumberForm(ModelForm): ... class Meta: ... model = PhoneNumber ->>> f = PhoneNumberForm(PhoneNumber(), {'phone': '(312) 555-1212', 'description': 'Assistance'}) +>>> f = PhoneNumberForm({'phone': '(312) 555-1212', 'description': 'Assistance'}) >>> f.is_valid() True >>> f.cleaned_data