Django

Code

Changeset 6655

Show
Ignore:
Timestamp:
11/06/07 15:53:15 (1 year ago)
Author:
jkocherhans
Message:

newforms-admin: Fixed #5733. Added formset_for_queryset. This is backwards incompatble for anyone using formset_for_model or BaseModelFormSet?.

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • django/branches/newforms-admin/django/newforms/models.py

    r6654 r6655  
    1616__all__ = ( 
    1717    'save_instance', 'form_for_model', 'form_for_instance', 'form_for_fields', 
    18     'ModelChoiceField', 'ModelMultipleChoiceField', 'formset_for_model', 
    19     'inline_formset' 
     18    'formset_for_model', 'formset_for_queryset', 'inline_formset', 
     19    'ModelChoiceField', 'ModelMultipleChoiceField', 
    2020) 
    2121 
     
    238238class BaseModelFormSet(BaseFormSet): 
    239239    """ 
    240     A ``FormSet`` attatched to a particular model or sequence of model instances
     240    A ``FormSet`` for editing a queryset and/or adding new objects to it
    241241    """ 
    242242    model = None 
    243  
    244     def __init__(self, data=None, files=None, auto_id='id_%s', prefix=None, instances=None): 
    245         self.instances = instances 
     243    queryset = None 
     244 
     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(obj) for obj in self.get_queryset()
    249249        super(BaseModelFormSet, self).__init__(**kwargs) 
     250 
     251    def get_queryset(self): 
     252        return self.queryset._clone() 
    250253 
    251254    def save_new(self, form, commit=True): 
     
    261264        as necessary, and returns the list of instances. 
    262265        """ 
     266        return self.save_existing_objects(commit) + self.save_new_objects(commit) 
     267 
     268    def save_existing_objects(self, commit=True): 
     269        if not self.queryset: 
     270            return [] 
     271        # Put the objects from self.get_queryset into a dict so they are easy to lookup by pk 
     272        existing_objects = {} 
     273        for obj in self.get_queryset(): 
     274            existing_objects[obj._get_pk_val()] = obj 
    263275        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: 
    269             # update/save existing instances 
    270             for form in self.change_forms: 
    271                 instance = instances[form.cleaned_data[self.model._meta.pk.attname]] 
    272                 if self.deletable and form.cleaned_data[DELETION_FIELD_NAME]: 
    273                     instance.delete() 
    274                 else: 
    275                     saved_instances.append(self.save_instance(form, instance, commit=commit)) 
    276         # create/save new instances 
     276        for form in self.change_forms: 
     277            obj = existing_objects[form.cleaned_data[self.model._meta.pk.attname]] 
     278            if self.deletable and form.cleaned_data[DELETION_FIELD_NAME]: 
     279                obj.delete() 
     280            else: 
     281                saved_instances.append(self.save_instance(form, obj, commit=commit)) 
     282        return saved_instances 
     283 
     284    def save_new_objects(self, commit=True): 
     285        new_objects = [] 
    277286        for form in self.add_forms: 
    278287            if form.is_empty(): 
    279288                continue 
    280             saved_instances.append(self.save_new(form, commit=commit)) 
    281         return saved_instances 
     289            # If someone has marked an add form for deletion, don't save the 
     290            # object. At some point it would be nice if we didn't display 
     291            # the deletion widget for add forms. 
     292            if self.deletable and form.cleaned_data[DELETION_FIELD_NAME]: 
     293                continue 
     294            new_objects.append(self.save_new(form, commit=commit)) 
     295        return new_objects 
    282296 
    283297    def add_fields(self, form, index): 
     
    287301        super(BaseModelFormSet, self).add_fields(form, index) 
    288302 
    289 def formset_for_model(model, form=BaseForm, formfield_callback=lambda f: f.formfield(), formset=BaseModelFormSet, extra=1, orderable=False, deletable=False, fields=None): 
    290     form = form_for_model(model, form=form, fields=fields, formfield_callback=formfield_callback) 
     303def formset_for_queryset(queryset, form=BaseForm, formfield_callback=lambda f: f.formfield(), 
     304                         formset=BaseModelFormSet, extra=1, orderable=False, deletable=False, fields=None): 
     305    """ 
     306    Returns a FormSet class for the given QuerySet. This FormSet will contain 
     307    change forms for every instance in the QuerySet as well as the number of 
     308    add forms specified by ``extra``. 
     309     
     310    Provide ``extra`` to determine the number of add forms to display. 
     311     
     312    Provide ``deletable`` if you want to allow the formset to delete any 
     313    objects in the given queryset. 
     314     
     315    Provide ``form`` if you want to use a custom BaseForm subclass. 
     316     
     317    Provide ``formfield_callback`` if you want to define different logic for 
     318    determining the formfield for a given database field. It's a callable that 
     319    takes a database Field instance and returns a form Field instance. 
     320     
     321    Provide ``formset`` if you want to use a custom BaseModelFormSet subclass. 
     322    """ 
     323    form = form_for_model(queryset.model, form=form, fields=fields, formfield_callback=formfield_callback) 
    291324    FormSet = formset_for_form(form, formset, extra, orderable, deletable) 
    292     FormSet.model = model 
     325    FormSet.model = queryset.model 
     326    FormSet.queryset = queryset 
    293327    return FormSet 
     328 
     329def formset_for_model(model, form=BaseForm, formfield_callback=lambda f: f.formfield(), 
     330                      formset=BaseModelFormSet, extra=1, orderable=False, deletable=False, fields=None): 
     331    """ 
     332    Returns a FormSet class for the given Django model class. This FormSet 
     333    will contain change forms for every instance of the given model as well 
     334    as the number of add forms specified by ``extra``. 
     335     
     336    This is essentially the same as ``formset_for_queryset``, but automatically 
     337    uses the model's default manager to determine the queryset. 
     338    """ 
     339    qs = model._default_manager.all() 
     340    return formset_for_queryset(qs, form, formfield_callback, formset, extra, orderable, deletable, fields) 
    294341 
    295342class InlineFormset(BaseModelFormSet): 
    296343    """A formset for child objects related to a parent.""" 
    297     def __init__(self, instance=None, data=None, files=None): 
     344    def __init__(self, instance, data=None, files=None): 
    298345        from django.db.models.fields.related import RelatedObject 
    299346        self.instance = instance 
    300347        # is there a better way to get the object descriptor? 
    301348        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) 
    303  
    304     def get_inline_objects(self): 
    305         if self.instance is None: 
    306             return [] 
    307         return getattr(self.instance, self.rel_name).all() 
     349        super(InlineFormset, self).__init__(data, files, prefix=self.rel_name) 
     350 
     351    def get_queryset(self): 
     352        """ 
     353        Returns this FormSet's queryset, but restricted to children of  
     354        self.instance 
     355        """ 
     356        kwargs = {self.fk.name: self.instance} 
     357        return self.queryset.filter(**kwargs) 
    308358 
    309359    def save_new(self, form, commit=True):