diff --git a/django/db/models/fields/__init__.py b/django/db/models/fields/__init__.py index fe317ac24f..d26bf7e954 100644 --- a/django/db/models/fields/__init__.py +++ b/django/db/models/fields/__init__.py @@ -334,10 +334,10 @@ class Field(object): return self._choices choices = property(_get_choices) - def formfield(self): + def formfield(self, initial=None): "Returns a django.newforms.Field instance for this database Field." # TODO: This is just a temporary default during development. - return forms.CharField(required=not self.blank, label=capfirst(self.verbose_name)) + return forms.CharField(required=not self.blank, label=capfirst(self.verbose_name), initial=initial) class AutoField(Field): empty_strings_allowed = False @@ -375,7 +375,7 @@ class AutoField(Field): super(AutoField, self).contribute_to_class(cls, name) cls._meta.has_auto_field = True - def formfield(self): + def formfield(self, initial=None): return None class BooleanField(Field): @@ -392,8 +392,8 @@ class BooleanField(Field): def get_manipulator_field_objs(self): return [oldforms.CheckboxField] - def formfield(self): - return forms.BooleanField(required=not self.blank, label=capfirst(self.verbose_name)) + def formfield(self, initial=None): + return forms.BooleanField(required=not self.blank, label=capfirst(self.verbose_name), initial=initial) class CharField(Field): def get_manipulator_field_objs(self): @@ -409,8 +409,8 @@ class CharField(Field): raise validators.ValidationError, gettext_lazy("This field cannot be null.") return str(value) - def formfield(self): - return forms.CharField(max_length=self.maxlength, required=not self.blank, label=capfirst(self.verbose_name)) + def formfield(self, initial=None): + return forms.CharField(max_length=self.maxlength, required=not self.blank, label=capfirst(self.verbose_name), initial=initial) # TODO: Maybe move this into contrib, because it's specialized. class CommaSeparatedIntegerField(CharField): @@ -483,8 +483,8 @@ class DateField(Field): val = self._get_val_from_obj(obj) return {self.attname: (val is not None and val.strftime("%Y-%m-%d") or '')} - def formfield(self): - return forms.DateField(required=not self.blank, label=capfirst(self.verbose_name)) + def formfield(self, initial=None): + return forms.DateField(required=not self.blank, label=capfirst(self.verbose_name), initial=initial) class DateTimeField(DateField): def to_python(self, value): @@ -544,8 +544,8 @@ class DateTimeField(DateField): return {date_field: (val is not None and val.strftime("%Y-%m-%d") or ''), time_field: (val is not None and val.strftime("%H:%M:%S") or '')} - def formfield(self): - return forms.DateTimeField(required=not self.blank, label=capfirst(self.verbose_name)) + def formfield(self, initial=None): + return forms.DateTimeField(required=not self.blank, label=capfirst(self.verbose_name), initial=initial) class EmailField(CharField): def __init__(self, *args, **kwargs): @@ -561,8 +561,8 @@ class EmailField(CharField): def validate(self, field_data, all_data): validators.isValidEmail(field_data, all_data) - def formfield(self): - return forms.EmailField(required=not self.blank, label=capfirst(self.verbose_name)) + def formfield(self, initial=None): + return forms.EmailField(required=not self.blank, label=capfirst(self.verbose_name), initial=initial) class FileField(Field): def __init__(self, verbose_name=None, name=None, upload_to='', **kwargs): @@ -696,8 +696,8 @@ class IntegerField(Field): def get_manipulator_field_objs(self): return [oldforms.IntegerField] - def formfield(self): - return forms.IntegerField(required=not self.blank, label=capfirst(self.verbose_name)) + def formfield(self, initial=None): + return forms.IntegerField(required=not self.blank, label=capfirst(self.verbose_name), initial=initial) class IPAddressField(Field): def __init__(self, *args, **kwargs): @@ -793,8 +793,8 @@ class TimeField(Field): val = self._get_val_from_obj(obj) return {self.attname: (val is not None and val.strftime("%H:%M:%S") or '')} - def formfield(self): - return forms.TimeField(required=not self.blank, label=capfirst(self.verbose_name)) + def formfield(self, initial=None): + return forms.TimeField(required=not self.blank, label=capfirst(self.verbose_name), initial=initial) class URLField(Field): def __init__(self, verbose_name=None, name=None, verify_exists=True, **kwargs): @@ -806,8 +806,8 @@ class URLField(Field): def get_manipulator_field_objs(self): return [oldforms.URLField] - def formfield(self): - return forms.URLField(required=not self.blank, verify_exists=self.verify_exists, label=capfirst(self.verbose_name)) + def formfield(self, initial=None): + return forms.URLField(required=not self.blank, verify_exists=self.verify_exists, label=capfirst(self.verbose_name), initial=initial) class USStateField(Field): def get_manipulator_field_objs(self): diff --git a/django/db/models/fields/related.py b/django/db/models/fields/related.py index 194ffd9610..8cc26c9ee4 100644 --- a/django/db/models/fields/related.py +++ b/django/db/models/fields/related.py @@ -548,8 +548,8 @@ class ForeignKey(RelatedField, Field): def contribute_to_related_class(self, cls, related): setattr(cls, related.get_accessor_name(), ForeignRelatedObjectsDescriptor(related)) - def formfield(self): - return forms.ChoiceField(choices=self.get_choices_default(), required=not self.blank, label=capfirst(self.verbose_name)) + def formfield(self, initial=None): + return forms.ChoiceField(choices=self.get_choices_default(), required=not self.blank, label=capfirst(self.verbose_name), initial=initial) class OneToOneField(RelatedField, IntegerField): def __init__(self, to, to_field=None, **kwargs): @@ -612,8 +612,8 @@ class OneToOneField(RelatedField, IntegerField): if not cls._meta.one_to_one_field: cls._meta.one_to_one_field = self - def formfield(self): - return forms.ChoiceField(choices=self.get_choices_default(), required=not self.blank, label=capfirst(self.verbose_name)) + def formfield(self, initial=None): + return forms.ChoiceField(choices=self.get_choices_default(), required=not self.blank, label=capfirst(self.verbose_name), initial=initial) class ManyToManyField(RelatedField, Field): def __init__(self, to, **kwargs): @@ -721,8 +721,8 @@ class ManyToManyField(RelatedField, Field): def set_attributes_from_rel(self): pass - def formfield(self): - return forms.MultipleChoiceField(choices=self.get_choices_default(), required=not self.blank, label=capfirst(self.verbose_name)) + def formfield(self, initial=None): + return forms.MultipleChoiceField(choices=self.get_choices_default(), required=not self.blank, label=capfirst(self.verbose_name), initial=initial) class ManyToOneRel(object): def __init__(self, to, field_name, num_in_admin=3, min_num_in_admin=None, diff --git a/django/newforms/models.py b/django/newforms/models.py index 6b111d7ee1..12acc0ac83 100644 --- a/django/newforms/models.py +++ b/django/newforms/models.py @@ -5,10 +5,14 @@ and database field objects. from forms import BaseForm, DeclarativeFieldsMetaclass, SortedDictFromList -__all__ = ('form_for_model', 'form_for_fields') +__all__ = ('form_for_model', 'form_for_instance', 'form_for_fields') def create(self, save=True): - "Creates and returns model instance according to self.clean_data." + """ + Creates and returns model instance according to self.clean_data. + + This method is created for any form_for_model Form. + """ if self.errors: raise ValueError("The %s could not be created because the data didn't validate." % self._model._meta.object_name) obj = self._model(**self.clean_data) @@ -16,7 +20,7 @@ def create(self, save=True): obj.save() return obj -def form_for_model(model, form=None): +def form_for_model(model, form=BaseForm): """ Returns a Form class for the given Django model class. @@ -29,9 +33,25 @@ def form_for_model(model, form=None): if formfield: field_list.append((f.name, formfield)) fields = SortedDictFromList(field_list) - form = form or BaseForm return type(opts.object_name + 'Form', (form,), {'fields': fields, '_model': model, 'create': create}) +def form_for_instance(instance, form=BaseForm): + """ + Returns a Form class for the given Django model instance. + + Provide 'form' if you want to use a custom BaseForm subclass. + """ + model = instance.__class__ + opts = model._meta + field_list = [] + for f in opts.fields + opts.many_to_many: + current_value = getattr(instance, f.attname) + formfield = f.formfield(initial=current_value) + if formfield: + field_list.append((f.name, formfield)) + fields = SortedDictFromList(field_list) + return type(opts.object_name + 'InstanceForm', (form,), {'fields': fields, '_model': model}) + def form_for_fields(field_list): "Returns a Form class for the given list of Django database field instances." fields = SortedDictFromList([(f.name, f.formfield()) for f in field_list]) diff --git a/tests/modeltests/model_forms/models.py b/tests/modeltests/model_forms/models.py index 566dad42ea..8ae9ddc6fb 100644 --- a/tests/modeltests/model_forms/models.py +++ b/tests/modeltests/model_forms/models.py @@ -36,7 +36,7 @@ class Article(models.Model): return self.headline __test__ = {'API_TESTS': """ ->>> from django.newforms import form_for_model, BaseForm +>>> from django.newforms import form_for_model, form_for_instance, BaseForm >>> Category.objects.all() [] @@ -141,4 +141,13 @@ subclass of BaseForm, not Form. >>> f = CategoryForm() >>> f.say_hello() hello + +Use form_for_instance to create a Form from a model instance. The difference +between this Form and one created via form_for_model is that the object's +current values are inserted as 'initial' data in each Field. +>>> w = Writer.objects.get(name='Mike Royko') +>>> RoykoForm = form_for_instance(w) +>>> f = RoykoForm(auto_id=False) +>>> print f +Name: """}