Django: Initializing a FormSet of custom forms with instances


Given the following models:

class Graph(models.Model):
    owner = models.ForeignKey(User)

    def __unicode__(self):
        return u'%d' %

class Point(models.Model):
    graph = models.ForeignKey(Graph) 
    date  = models.DateField(primary_key = True)
    abs   = models.FloatField(null = True)
    avg   = models.FloatField(null = True)

    def __unicode__(self):
        return u'%s' %

I am trying to create a form for editing lists of Points. The HTML input tags require additional attributes to be set, so I am using the following custom form:

class PointForm(forms.ModelForm):
    graph = forms.ModelChoiceField(queryset = Graph.objects.all(),
                                   widget   = forms.HiddenInput())
    date  = forms.DateField(widget = forms.HiddenInput(), label = 'date')
    abs   = forms.FloatField(widget = forms.TextInput(
                                      attrs = {'class': 'abs-field'}),
                            required = False)

    class Meta:
        model  = Point
        fields = ('graph', 'date', 'abs')  # Other fields are not edited.

    def pretty_date(self):

At this point I do not know how to pass instances of the Point class to a FormSet:

def edit(request):
    PointFormSet = forms.formsets.formset_factory(PointForm, extra = 0)
    if request.method == 'POST':

    # Receive 3 points to edit from the database.
    graph, res = Graph.objects.get_or_create(id = 1)
    one_day    = datetime.timedelta(days = 1)
    today      =
    do_edit    = []
    for date in [today - (x * one_day) for x in range(3)]:
        point, res = Point.objects.get_or_create(graph = graph, date = date)

    formset = PointFormSet(????) # How is this initialized with the points?

I found a hack that somewhat works, but it leads to errors later on when trying to process the resulting POST data:

do_edit = []
for date in [today - (x * one_day) for x in range(3)]:
    point, res    = Point.objects.get_or_create(graph = graph, date = date)
    data          = point.__dict__.copy()
    data['graph'] = graph

formset = PointFormSet(initial = do_edit)

How is this done correctly?

For the reference, my template looks like this:

<form action="" method="post">
{{ formset.management_form }}
    {% for form in formset.forms %}
            <td>{{ form.graph }} {{ }} {{ form.pretty_date }}:</td>
            <td width="100%">{{ form.abs }}</td>
    {% endfor %}

Answer 1:

The trick is to use a "ModelFormset" instead of just a formset since they allow initialization with a queryset. The docs are here, what you do is provide a form=* when creating the model formset and queryset=* when your instantiating the formset. The form=* arguement is not well documented (had to dig around in the code a little to make sure it is actually there).

def edit(request):
    PointFormSet = modelformset_factory(Point, form = PointForm)
    qset = Point.objects.all() #or however your getting your Points to modify
    formset = PointFormset(queryset = qset)
    if request.method == 'POST':
        #deal with posting the data
        formset = PointFormset(request.POST)
        if formset.is_valid():
            #if it is not valid then the "errors" will fall through and be returned
        return #to your redirect

    context_dict = {'formset':formset,
                    #other context info

    return render_to_response('your_template.html', context_dict)

So the code walks through easily. If the request is a GET then the instantiated form is returned to the user. If the request is a POST and the form is not .is_valid() then the errors "fall through" and are returned in the same template. If the request is a POST and the data is valid then the formset is saved.

Hope that helps.


Answer 2:

If you only have one possible value which you want to set, or perhaps a closed of values, it is possible to set them after the user POSTS the data to your server using commit=False

Please consider the following code:

class UserReferralView(View):
    ReferralFormSet = modelformset_factory(ReferralCode,
                                           form=ReferralTokenForm, extra=1)

    def get(self, request):

    def post(self, request):
        referral_formset = UserUpdateView.ReferralFormSet(request.POST)

        if referral_formset.is_valid():
            instances =
            for instance in instances:
                instance.user = request.user
            return redirect(reverse('referrals.success_view'))
            return redirect(reverse('referrals.failure_view'))


码神部落- 版权声明 1、本主题所有言论和图片纯属会员个人意见,与码神部落立场无关。

最新回复 (0)
    • 码神部落
        立即登录 立即注册 GitHub登录