5

I am using inlineformset_factory to create fields for a many to many relationship between Clients and Sessions, with an intermediary Attendance model.

I have the following in my views file:

AttendanceFormset = inlineformset_factory(
    Session,
    Attendance,
    formset=BaseAttendanceFormSet,
    exclude=('user'),
    extra=1,
    max_num=10,
    )

session = Session(user=request.user)
formset = AttendanceFormset(request.POST, instance=session)

And, as I needed to override one of the form fields, I added the following to the formset base class:

class BaseAttendanceFormSet(BaseFormSet):

    def add_fields(self, form, index):
        super(BaseAttendanceFormSet, self).add_fields(form, index)
        form.fields['client'] = forms.ModelChoiceField(
                queryset=Client.objects.filter(user=2))

Now, the form works correctly, but I need to pass a value into the formset so that I can filter the clients displayed based the current user rather than just using the id 2.

Can anyone help?

Any advice appreciated.

Thanks.

EDIT

For anyone reading, this is what worked for me:

def get_field_qs(field, **kwargs):
        if field.name == 'client':
            return forms.ModelChoiceField(queryset=Client.objects.filter(user=request.user))
        return field.formfield(**kwargs)

1 Answer 1

8

How about utilizing the inlineformset_factory's formfield_callback param instead of providing a formset ? Provide a callable which in turns returns the field which should be used in the form.

Form fields callback gets as 1st parameter the field, and **kwargs for optional params (e.g: widget).

For example (using request.user for the filter, replace with another if needed:

def my_view(request):
    #some setup code here

    def get_field_qs(field, **kwargs):
        formfield = field.formfield(**kwargs)
        if field.name == 'client':
            formfield.queryset = formfield.queryset.filter(user=request.user)
        return formfield

    AttendanceFormset = inlineformset_factory(
        ...
        formfield_callback=get_field_qs
        ...
    )

    formset = AttendanceFormset(request.POST, instance=session)

To better understand it, see the usage of formfield_callback in Django's FormSet code.

Sign up to request clarification or add additional context in comments.

4 Comments

Hi, thanks for your reply. I have tried adding what you have put above (although I had to return field.formfield(**kwargs) instead of just field) but it is now returning all of the client objects. I've read the query in the django toolbar and the filter is not being applied. I tried outputting to console from within the if statement so I know that is being hit. Any idea why it might be ignoring it? Thanks again.
Can you update your question with your 2nd attempt ? Guess you can also return forms.ModelChoiceField(queryset=Client.objects.filter(user=request.user)) if the field is named client
Fixed example above, using more general implementation of @mkriheli solution in the comments
Yes great solution. Even possible to use this in a CBFormView by overriding the get_form() and the get_form_kwargs()

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.