Django

Code

Changeset 5202

Show
Ignore:
Timestamp:
05/12/07 09:42:46 (2 years ago)
Author:
russellm
Message:

Added docs for form_for_model and form_for_instance, and added a fields argument so it is easy to create forms from a subset of model fields.

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • django/trunk/django/newforms/models.py

    r4878 r5202  
    1313           'ModelChoiceField', 'ModelMultipleChoiceField') 
    1414 
    15 def model_save(self, commit=True): 
    16     """ 
    17     Creates and returns model instance according to self.clean_data. 
    18  
    19     This method is created for any form_for_model Form. 
    20     """ 
    21     if self.errors: 
    22         raise ValueError("The %s could not be created because the data didn't validate." % self._model._meta.object_name) 
    23     return save_instance(self, self._model(), commit) 
    24  
    25 def save_instance(form, instance, commit=True): 
     15def save_instance(form, instance, fields=None, fail_message='saved', commit=True): 
    2616    """ 
    2717    Saves bound Form ``form``'s clean_data into model instance ``instance``. 
     
    3424    opts = instance.__class__._meta 
    3525    if form.errors: 
    36         raise ValueError("The %s could not be changed because the data didn't validate." % opts.object_name
     26        raise ValueError("The %s could not be %s because the data didn't validate." % (opts.object_name, fail_message)
    3727    clean_data = form.clean_data 
    3828    for f in opts.fields: 
    3929        if not f.editable or isinstance(f, models.AutoField) or not f.name in clean_data: 
     30            continue 
     31        if fields and f.name not in fields: 
    4032            continue 
    4133        setattr(instance, f.name, clean_data[f.name]) 
     
    4335        instance.save() 
    4436        for f in opts.many_to_many: 
     37            if fields and f.name not in fields: 
     38                continue 
    4539            if f.name in clean_data: 
    4640                setattr(instance, f.attname, clean_data[f.name]) 
     
    5145    return instance 
    5246 
    53 def make_instance_save(instance): 
    54     "Returns the save() method for a form_for_instance Form." 
     47def make_model_save(model, fields, fail_message): 
     48    "Returns the save() method for a Form." 
    5549    def save(self, commit=True): 
    56         return save_instance(self, instance, commit) 
     50        return save_instance(self, model(), fields, fail_message, commit) 
     51    return save 
     52     
     53def make_instance_save(instance, fields, fail_message): 
     54    "Returns the save() method for a Form." 
     55    def save(self, commit=True): 
     56        return save_instance(self, instance, fields, fail_message, commit) 
    5757    return save 
    5858 
    59 def form_for_model(model, form=BaseForm, formfield_callback=lambda f: f.formfield()): 
     59def form_for_model(model, form=BaseForm, fields=None, formfield_callback=lambda f: f.formfield()): 
    6060    """ 
    6161    Returns a Form class for the given Django model class. 
     
    7272        if not f.editable: 
    7373            continue 
     74        if fields and not f.name in fields: 
     75            continue 
    7476        formfield = formfield_callback(f) 
    7577        if formfield: 
    7678            field_list.append((f.name, formfield)) 
    77     fields = SortedDictFromList(field_list) 
    78     return type(opts.object_name + 'Form', (form,), {'base_fields': fields, '_model': model, 'save': model_save}) 
     79    base_fields = SortedDictFromList(field_list) 
     80    return type(opts.object_name + 'Form', (form,),  
     81        {'base_fields': base_fields, '_model': model, 'save': make_model_save(model, fields, 'created')}) 
    7982 
    80 def form_for_instance(instance, form=BaseForm, formfield_callback=lambda f, **kwargs: f.formfield(**kwargs)): 
     83def form_for_instance(instance, form=BaseForm, fields=None, formfield_callback=lambda f, **kwargs: f.formfield(**kwargs)): 
    8184    """ 
    8285    Returns a Form class for the given Django model instance. 
     
    9598        if not f.editable: 
    9699            continue 
     100        if fields and not f.name in fields: 
     101            continue 
    97102        current_value = f.value_from_object(instance) 
    98103        formfield = formfield_callback(f, initial=current_value) 
    99104        if formfield: 
    100105            field_list.append((f.name, formfield)) 
    101     fields = SortedDictFromList(field_list) 
     106    base_fields = SortedDictFromList(field_list) 
    102107    return type(opts.object_name + 'InstanceForm', (form,), 
    103         {'base_fields': fields, '_model': model, 'save': make_instance_save(instance)}) 
     108        {'base_fields': base_fields, '_model': model, 'save': make_instance_save(instance, fields, 'changed')}) 
    104109 
    105110def form_for_fields(field_list): 
  • django/trunk/docs/newforms.txt

    r4819 r5202  
    871871``help_text``). 
    872872 
     873Generating forms for models 
     874=========================== 
     875 
     876Although you can build customized forms by specifying the fields manually, 
     877in many cases you won't need to. Django provides helper methods to simplify the 
     878common cases of form creation. 
     879 
     880``form_for_model()`` 
     881-------------------- 
     882 
     883This method creates a form based upon the definition for a specific model.  
     884``form_for_model()`` examines the model definition, and creates a new form 
     885class that contains a form field for each model field that is defined. 
     886 
     887The type of fields produced on the generated form is determined by the type 
     888of the model fields. For example, a ``CharField`` on a model will be  
     889represented with a ``CharField`` on the form. Each ``ManyToManyField``  
     890on the model will be represented with a ``MultipleChoiceField`` on the  
     891form. Each ``ForeignKey`` will be represented with a ``ChoiceField``. 
     892A ``ChoiceField`` is also used for any model field that has a ``choices``  
     893attribute specified. 
     894 
     895``form_for_model()`` returns a generated class. This class must then be  
     896instantiated:: 
     897 
     898    # Create the form class 
     899    >>> ArticleForm = form_for_model(Article) 
     900     
     901    # Create an empty form instance 
     902    >>> f = ArticleForm() 
     903  
     904The form produced by ``form_for_model`` also has a ``save()`` method. Once the 
     905form contains valid data, the ``save()`` method can be used to create a model 
     906instance with the attribute values described on the form:: 
     907 
     908    # Create a form instance populated with POST data 
     909    >>> f = ArticleForm(request.POST) 
     910 
     911    # Save the new instance 
     912    >>> new_article = f.save() 
     913 
     914Using an alternate base class 
     915~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
     916 
     917If you want to add other methods to the generated form, you can put those  
     918methods onto a base class, and instruct ``form_for_model()`` to use that 
     919base class.  
     920 
     921By default, every form produced by ``form_for_model()`` extends  
     922``django.newforms.forms.BaseForm``. However, if you provide a ``forms``  
     923argument to ``form_for_model()``, Django will use that class as the base  
     924for the form it generates:: 
     925 
     926    # Create the new base class: 
     927    >>> class MyBase(BaseForm): 
     928    ...     def fiddle(self): 
     929    ...         # Do whatever the method does 
     930 
     931    # Create the form class with a different base class 
     932    >>> ArticleForm = form_for_model(Article, form=MyBase) 
     933 
     934    # Instantiate the form 
     935    >>> f = ArticleForm() 
     936     
     937    # Use the base class method 
     938    >>> f.fiddle() 
     939 
     940Putting a subset of fields on the form 
     941~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
     942**New in Django development version** 
     943 
     944In some cases, you may not want all the model fields to appear on the form.  
     945One option is to set ``editable=False`` on the model field. ``form_for_model()`` 
     946will not include any non-editable fields on a generated form instance. 
     947 
     948However, if you just want to exclude a field from one specific form, you 
     949can use the ``fields`` argument. If you provide a fields argument to 
     950``form_for_model()``, only the fields named will be included on the form.  
     951For example, if you only want the 'title' and 'pub_date' attributes to be  
     952included on the Article form, you would call:: 
     953 
     954    >>> PartialArticleForm = form_for_model(Article, fields=('title', 'pub_date')) 
     955 
     956.. note:: 
     957    If you specify ``fields`` when creating a form with ``form_for_model()`` 
     958    make sure that the fields that are *not* specified can provide default  
     959    values, or are allowed to have a value of ``None``. If a field isn't  
     960    specified on a form, the object created from the form can't provide  
     961    a value for that attribute, which will prevent the new instance from  
     962    being saved. 
     963 
     964Overriding the default field types 
     965~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
     966 
     967Although the form field types generated by ``form_for_model()`` are suitable 
     968for most general purposes, you may have need to override the default field 
     969types on a specific form. In order to do this, ``form_for_model()`` provides  
     970access to the *formfield callback*. 
     971 
     972The formfield callback is a function that, when provided with a model field, 
     973returns a form field instance. When constructing a form, ``form_for_model()`` 
     974asks the formfield callback to provide form field types. The default  
     975implementation asks the model field for an appropriate field type; however,  
     976any other strategy may be employed. If you need to use an alternate strategy,  
     977you can define your own callback, and provide it to ``form_for_model()`` using  
     978the ``formfield_callback`` argument.  
     979 
     980For example, if you wanted to use ``MyDateFormField`` for any ``DateField`` 
     981fields on the model, you could define the callback:: 
     982 
     983    >>> def my_fields(field, **kwargs): 
     984    ...     if isinstance(field, models.DateField): 
     985    ...         return MyDateFormField(**kwargs) 
     986    ...     else:      
     987    ...         return field.formfield(**kwargs) 
     988     
     989    >>> ArticleForm = form_for_model(formfield_callback=my_fields) 
     990 
     991Note that your callback needs to handle *all* possible model field types, not  
     992just the ones that you want to behave differently to the default. 
     993 
     994Finding the model associated with a form 
     995~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
     996 
     997The model class that was used to construct the form is available 
     998using the ``_model`` property of the generated form. 
     999      
     1000``form_for_instance()`` 
     1001----------------------- 
     1002 
     1003``form_for_instance()`` is very similar to ``form_for_model()``. However, 
     1004rather than using a model class to generate a form, it uses an instance of a  
     1005model:: 
     1006 
     1007    # Create an article 
     1008    >>> art = Article(... some data ...) 
     1009    >>> art.save() 
     1010     
     1011    # Create a form 
     1012    >>> ArticleForm = form_for_instance(art) 
     1013     
     1014    # Instantiate the form 
     1015    >>> f = ArticleForm() 
     1016 
     1017When a form created by ``form_for_instance()`` is created, the initial 
     1018data values for the form fields are drawn from the instance. However,  
     1019this data is not bound to the form. You will need to bind data to the  
     1020form before the form can be saved. 
     1021 
     1022When you call ``save()`` on a form created by ``form_for_instance()``,  
     1023the database instance will be updated. 
     1024 
     1025``form_for_instance()`` has ``form``, ``fields`` and ``formfield_callback``  
     1026arguments that behave the same way as they do for ``form_for_model()``. 
     1027 
    8731028More coming soon 
    8741029================ 
  • django/trunk/tests/modeltests/model_forms/models.py

    r5119 r5202  
    180180</select><br /> Hold down "Control", or "Command" on a Mac, to select more than one.</td></tr> 
    181181 
     182You can restrict a form to a subset of the complete list of fields 
     183by providing a 'fields' argument. If you try to save a 
     184model created with such a form, you need to ensure that the fields 
     185that are _not_ on the form have default values, or are allowed to have 
     186a value of None. If a field isn't specified on a form, the object created 
     187from the form can't provide a value for that field! 
     188>>> PartialArticleForm = form_for_model(Article, fields=('headline','pub_date')) 
     189>>> f = PartialArticleForm(auto_id=False) 
     190>>> print f 
     191<tr><th>Headline:</th><td><input type="text" name="headline" maxlength="50" /></td></tr> 
     192<tr><th>Pub date:</th><td><input type="text" name="pub_date" /></td></tr> 
     193 
    182194You can pass a custom Form class to form_for_model. Make sure it's a 
    183195subclass of BaseForm, not Form. 
     
    225237<option value="3">Third test</option> 
    226238</select>  Hold down "Control", or "Command" on a Mac, to select more than one.</li> 
    227 >>> f = TestArticleForm({'headline': u'New headline', 'pub_date': u'1988-01-04', 'writer': u'1', 'article': 'Hello.'}) 
     239>>> f = TestArticleForm({'headline': u'Test headline', 'pub_date': u'1984-02-06', 'writer': u'1', 'article': 'Hello.'}) 
     240>>> f.is_valid() 
     241True 
     242>>> test_art = f.save() 
     243>>> test_art.id 
     244
     245>>> test_art = Article.objects.get(id=1) 
     246>>> test_art.headline 
     247'Test headline' 
     248 
     249You can create a form over a subset of the available fields  
     250by specifying a 'fields' argument to form_for_instance.  
     251>>> PartialArticleForm = form_for_instance(art, fields=('headline','pub_date')) 
     252>>> f = PartialArticleForm({'headline': u'New headline', 'pub_date': u'1988-01-04'}, auto_id=False) 
     253>>> print f.as_ul() 
     254<li>Headline: <input type="text" name="headline" value="New headline" maxlength="50" /></li> 
     255<li>Pub date: <input type="text" name="pub_date" value="1988-01-04" /></li> 
    228256>>> f.is_valid() 
    229257True