Different initial data for each form in a Django formset

前端 未结 4 1265
灰色年华
灰色年华 2020-12-07 17:37

Is it possible to prepopulate a formset with different data for each row? I\'d like to put some information in hidden fields from a previous view.

According to the d

相关标签:
4条回答
  • 2020-12-07 18:18

    I had this problem and I made a new widget:

    from django.forms.widgets import Select
    from django.utils.safestring import mark_safe
    class PrepolutatedSelect(Select):
        def render(self, name, value, attrs=None, choices=()):
            if value is None: value = ''
            if value == '':
                value = int(name.split('-')[1])+1
            final_attrs = self.build_attrs(attrs, name=name)
            output = [u'<select%s>' % flatatt(final_attrs)]
            options = self.render_options(choices, [value])
            if options:
                output.append(options)
            output.append('</select>')
            return mark_safe(u'\n'.join(output))
    

    Maybe this will work for you too.

    0 讨论(0)
  • 2020-12-07 18:28

    Building on Antony Vennard's answer, I am not sure what version of python/django he is using but I could not get the generator to work in the curry method either. I am currently on python2.7.3 and django1.5.1. Instead of using a custom Generator, I ended up using the built-in iter() on a list of things to create an iterator and passing the iterator itself in the curry method and calling next() on it in the Form __init__(). Here is my solution:

    # Build the Formset:
    my_iterator = iter(my_list_of_things) # Each list item will correspond to a form.
    Formset = formset_factory(MyForm, extra=len(my_list_of_things))
    Formset.form = staticmethod(curry(MyForm, item_iterator=my_iterator))
    

    And in the form:

    # forms.py
    class MyForm(forms.Form):
        def __init__(self, *args, **kwargs):
            # Calling next() on the iterator/generator here:
            list_item = kwargs.pop('item_iterator').next()
    
            # Now you can assign whatever you passed in to an attribute
            # on one of the form elements.
            self.fields['my_field'].initial = list_item
    

    Some Key things I found were that you need to either specify an 'extra' value in the formset_factory or use the initial kwarg on the formset to specify a list that corresponds to the list you pass to the iterator (In above example I pass the len() of the my_list_of_things list to 'extra' kwarg to formset_factory). This is necessary to actually create a number of forms in the formset.

    0 讨论(0)
  • 2020-12-07 18:30

    You need to use the technique described in this post in order to be able to pass parameters in. Credit to that author for an excellent post. You achieve this in several parts:

    A form aware it is going to pick up additional parameters

    Example from the linked question:

    def __init__(self, *args, **kwargs):
        someeobject = kwargs.pop('someobject')
        super(ServiceForm, self).__init__(*args, **kwargs)
        self.fields["somefield"].queryset = ServiceOption.objects.filter(
                                                               somem2mrel=someobject)
    

    Or you can replace the latter code with

        self.fields["somefield"].initial = someobject
    

    Directly, and it works.

    A curried form initialisation setup:

    formset = formset_factory(Someform, extra=3)
    formset.form = staticmethod(curry(someform, somem2mrel=someobject))
    

    That gets you to passing custom form parameters. Now what you need is:

    A generator to acquire your different initial parameters

    I'm using this:

    def ItemGenerator(Item):
        i = 0
        while i < len(Item):
            yield Item[i]
            i += 1
    

    Now, I can do this:

    iterdefs = ItemGenerator(ListofItems) # pass the different parameters 
                                          # as an object here
    formset.form = staticmethod(curry(someform, somem2mrel=iterdefs.next()))
    

    Hey presto. Each evaluation of the form method is being evaluated in parts passing in an iterated parameter. We can iterate over what we like, so I'm using that fact to iterate over a set of objects and pass the value of each one in as a different initial parameter.

    0 讨论(0)
  • 2020-12-07 18:35

    If you made the same mistake as me, you've slightly mistaken the documentation.

    When I first saw this example...

    formset = ArticleFormSet(initial=[
     {'title': 'Django is now open source',
      'pub_date': datetime.date.today(),}
    ])
    

    I assumed that each form is given the same set of initial data based on a dictionary.

    However, if you look carefully you'll see that the formset is actually being passed a list of dictionaries.

    In order to set different initial values for each form in a formset then, you just need to pass a list of dictionaries containing the different data.

    Formset = formset_factory(SomeForm, extra=len(some_objects)
    some_formset = FormSet(initial=[{'id': x.id} for x in some_objects])
    
    0 讨论(0)
提交回复
热议问题