Different initial data for each form in a Django formset

泄露秘密 提交于 2019-12-29 02:51: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 docs you can only set initial across the board.


回答1:


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])



回答2:


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.




回答3:


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.




回答4:


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.



来源:https://stackoverflow.com/questions/1897756/different-initial-data-for-each-form-in-a-django-formset

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!