Ticket #5733: 5733_formset_for_queryset.diff

File 5733_formset_for_queryset.diff, 8.5 KB (added by Brian Rosner, 17 years ago)

incomplete, but proof of concept with tests

  • django/newforms/models.py

     
    1616__all__ = (
    1717    'save_instance', 'form_for_model', 'form_for_instance', 'form_for_fields',
    1818    'ModelChoiceField', 'ModelMultipleChoiceField', 'formset_for_model',
    19     'inline_formset'
     19    'formset_for_queryset', 'inline_formset'
    2020)
    2121
    2222def save_instance(form, instance, fields=None, fail_message='saved', commit=True):
     
    240240    A ``FormSet`` attatched to a particular model or sequence of model instances.
    241241    """
    242242    model = None
     243    queryset = None
    243244
    244     def __init__(self, data=None, files=None, auto_id='id_%s', prefix=None, instances=None):
    245         self.instances = instances
     245    def __init__(self, data=None, files=None, auto_id='id_%s', prefix=None):
    246246        kwargs = {'data': data, 'files': files, 'auto_id': auto_id, 'prefix': prefix}
    247         if instances:
    248             kwargs['initial'] = [initial_data(instance) for instance in instances]
     247        if self.queryset:
     248            kwargs['initial'] = [initial_data(instance) for instance in self.queryset]
    249249        super(BaseModelFormSet, self).__init__(**kwargs)
    250250
    251251    def save_new(self, form, commit=True):
     
    261261        as necessary, and returns the list of instances.
    262262        """
    263263        saved_instances = []
    264         # put self.instances into a dict so they are easy to lookup by pk
    265         instances = {}
    266         for instance in self.instances:
    267             instances[instance._get_pk_val()] = instance
    268         if self.instances:
     264        if self.queryset:
     265            # put self.instances into a dict so they are easy to lookup by pk
     266            instances = {}
     267            for instance in self.queryset:
     268                instances[instance._get_pk_val()] = instance
    269269            # update/save existing instances
    270270            for form in self.change_forms:
    271271                instance = instances[form.cleaned_data[self.model._meta.pk.attname]]
     
    292292    FormSet.model = model
    293293    return FormSet
    294294
     295def formset_for_queryset(queryset, form=BaseForm, formfield_callback=lambda f: f.formfield(), formset=BaseModelFormSet, extra=1, orderable=False, deletable=True, fields=None):
     296    model = queryset.model
     297    form = form_for_model(model, form=form, fields=fields, formfield_callback=formfield_callback)
     298    FormSet = formset_for_form(form, formset, extra, orderable, deletable)
     299    FormSet.model = model
     300    FormSet.queryset = queryset
     301    return FormSet
     302
    295303class InlineFormset(BaseModelFormSet):
    296304    """A formset for child objects related to a parent."""
    297305    def __init__(self, instance=None, data=None, files=None):
    298306        from django.db.models.fields.related import RelatedObject
    299307        self.instance = instance
     308        if self.instance:
     309            self.queryset = self.get_queryset()
    300310        # is there a better way to get the object descriptor?
    301311        self.rel_name = RelatedObject(self.fk.rel.to, self.model, self.fk).get_accessor_name()
    302         super(InlineFormset, self).__init__(data, files, instances=self.get_inline_objects(), prefix=self.rel_name)
     312        super(InlineFormset, self).__init__(data, files, prefix=self.rel_name)
     313   
     314    def get_queryset(self):
     315        kwargs = {'%s__%s__exact' % (self.fk.name, self.fk.rel.get_related_field().name): self.instance._get_pk_val()}
     316        return self.model._default_manager.filter(**kwargs)
    303317
    304     def get_inline_objects(self):
    305         if self.instance is None:
    306             return []
    307         return getattr(self.instance, self.rel_name).all()
    308 
    309318    def save_new(self, form, commit=True):
    310319        kwargs = {self.fk.get_attname(): self.instance._get_pk_val()}
    311320        new_obj = self.model(**kwargs)
  • tests/modeltests/model_formsets/models.py

     
     1
     2import datetime
     3from django.db import models
     4
     5class Blog(models.Model):
     6    title = models.CharField(max_length=100)
     7    is_active = models.BooleanField()
     8   
     9    def __unicode__(self):
     10        return self.title
     11
     12class Post(models.Model):
     13    blog = models.ForeignKey(Blog)
     14    title = models.CharField(max_length=100)
     15    pub_date = models.DateTimeField(default=datetime.datetime.now)
     16   
     17    def __unicode__(self):
     18        return self.title
     19
     20__test__ = {'API_TESTS': """
     21>>> from django.newforms import formset_for_model, formset_for_queryset, inline_formset
     22
     23>>> Blog.objects.all()
     24[]
     25
     26>>> BlogFormSet = formset_for_queryset(Blog.objects.all())
     27>>> formset = BlogFormSet()
     28>>> for form in formset.forms:
     29...     print form
     30<tr><th><label for="id_form-0-title">Title:</label></th><td><input id="id_form-0-title" type="text" name="form-0-title" maxlength="100" /></td></tr>
     31<tr><th><label for="id_form-0-is_active">Is active:</label></th><td><input type="checkbox" name="form-0-is_active" id="id_form-0-is_active" /></td></tr>
     32<tr><th><label for="id_form-0-DELETE">Delete:</label></th><td><input type="checkbox" name="form-0-DELETE" id="id_form-0-DELETE" /><input type="hidden" name="form-0-id" id="id_form-0-id" /></td></tr>
     33
     34Create an actual blog object.
     35
     36>>> b1 = Blog(title=u'My First Blog')
     37>>> b1.save()
     38
     39>>> BlogFormSet = formset_for_queryset(Blog.objects.all())
     40>>> formset = BlogFormSet()
     41>>> for form in formset.forms:
     42...     print form
     43<tr><th><label for="id_form-0-title">Title:</label></th><td><input id="id_form-0-title" type="text" name="form-0-title" value="My First Blog" maxlength="100" /></td></tr>
     44<tr><th><label for="id_form-0-is_active">Is active:</label></th><td><input type="checkbox" name="form-0-is_active" id="id_form-0-is_active" /></td></tr>
     45<tr><th><label for="id_form-0-DELETE">Delete:</label></th><td><input type="checkbox" name="form-0-DELETE" id="id_form-0-DELETE" /><input type="hidden" name="form-0-id" value="1" id="id_form-0-id" /></td></tr>
     46<tr><th><label for="id_form-1-title">Title:</label></th><td><input id="id_form-1-title" type="text" name="form-1-title" maxlength="100" /></td></tr>
     47<tr><th><label for="id_form-1-is_active">Is active:</label></th><td><input type="checkbox" name="form-1-is_active" id="id_form-1-is_active" /></td></tr>
     48<tr><th><label for="id_form-1-DELETE">Delete:</label></th><td><input type="checkbox" name="form-1-DELETE" id="id_form-1-DELETE" /><input type="hidden" name="form-1-id" id="id_form-1-id" /></td></tr>
     49
     50Now POST some data and see if the instance was updated correctly.
     51
     52>>> data = {
     53...     'form-COUNT': '2',
     54...     'form-0-id': '1',
     55...     'form-0-title': 'My First Blog',
     56...     'form-0-DELETE': '',
     57...     'form-1-id': '',
     58...     'form-1-title': 'My Second Blog',
     59...     'form-1-DELETE': '',
     60... }
     61>>> formset = BlogFormSet(data)
     62>>> formset.is_valid()
     63True
     64>>> formset.save()
     65[<Blog: My First Blog>, <Blog: My Second Blog>]
     66
     67Now lets create one post in each blog.
     68
     69>>> b1.post_set.add(Post(title=u'How to Pour a Guinness'))
     70>>> b2 = Blog.objects.get(pk=2)
     71>>> b2.post_set.add(Post(title=u'Bad Pigs! Why Your Bacon Tastes Bad.'))
     72
     73>>> PostFormSet = inline_formset(Blog, Post, extra=1)
     74>>> formset = PostFormSet(b2)
     75>>> for form in formset.forms:
     76...     print form
     77<tr><th><label for="id_post_set-0-title">Title:</label></th><td><input id="id_post_set-0-title" type="text" name="post_set-0-title" value="Bad Pigs! Why Your Bacon Tastes Bad." maxlength="100" /></td></tr>
     78<tr><th><label for="id_post_set-0-pub_date">Pub date:</label></th><td><input type="text" name="post_set-0-pub_date" value="2007-10-11 00:42:31.209597" id="id_post_set-0-pub_date" /></td></tr>
     79<tr><th><label for="id_post_set-0-DELETE">Delete:</label></th><td><input type="checkbox" name="post_set-0-DELETE" id="id_post_set-0-DELETE" /><input type="hidden" name="post_set-0-id" value="2" id="id_post_set-0-id" /></td></tr>
     80<tr><th><label for="id_post_set-1-title">Title:</label></th><td><input id="id_post_set-1-title" type="text" name="post_set-1-title" maxlength="100" /></td></tr>
     81<tr><th><label for="id_post_set-1-pub_date">Pub date:</label></th><td><input type="text" name="post_set-1-pub_date" id="id_post_set-1-pub_date" /></td></tr>
     82<tr><th><label for="id_post_set-1-DELETE">Delete:</label></th><td><input type="checkbox" name="post_set-1-DELETE" id="id_post_set-1-DELETE" /><input type="hidden" name="post_set-1-id" id="id_post_set-1-id" /></td></tr>
     83"""}
Back to Top