Question

Having several ForeignKeys in a ModelForm that I want to use in a form wizard with modelformset_factory (not 100% sure about the formset) I'm wondering how to limit the choices of the dropdown field for I need to do it dynamically. I wanted to try it by writing my own modelformset factory but here on stackoverflow I read about other approaches but unfortunatelly I don't understand them.

That's how far I came:

models.py

#...
class Attendee(models.Model):
    """Event specific attendee details."""
    # event is set by URL.
    event = models.ForeignKey(Event)
    attendee = models.ForeignKey(Person) # Contact details, should be limited to user
    accommodation = models.ForeignKey(Accommodation, blank=True) # *
    workshop = models.ForeignKey(Workshop, blank=True) # *
    volunteer = models.ForeignKey(Volunteer, blank=True) # *
    # *= should be limited to event but I think I will be able to handle that.
    #...

class AttendeeForm(forms.ModelForm):
    class Meta:
        model = Attendee

    def __init__(self, *args, **kwargs):
#    def __init__(self, user, *args, **kwargs): I tried this but I didn't get 
# it to work.
        super(AttendeeForm, self).__init__(*args, **kwargs)
#         self.fields['attendee'].queryset = Person.objects.filter(owner=user)
        self.fields['workshop'].required = True
        #...

# This is from https://stackoverflow.com/a/4858044/2704544 but I don't understand it. ##
def form_setup(**kwargs):
    def make_form(data, prefix=None, initial=None):
        form = (data, prefix, initial)
        for k, v in kwargs.items():
            if k == 'some_list':
                form.fields['some_list'].choices = v # What does that mean?
            ...
        return form
    return make_form
#######################################################################################

(link to source)

views.py

# This is from https://stackoverflow.com/a/623198/2704544 but I don't understand it. ###
class Callback(object):
    def __init__(self, field_name, aff):
        self._field_name = field_name
        self._aff = aff # What is this aff?
    def cb(self, field, **kwargs):
        nf = field.formfield(**kwargs)
        if field.name == self._field_name:  # this is 'options' field
            nf.queryset = ServiceOption.objects.filter(affiliate=self._aff)
        return nf
#######################################################################################

reg_wiz_forms = (
    ('attendees', modelformset_factory(Attendee, form=AttendeeForm, exclude='event', 
# formfield_callback=Callback('option', affiliate).cb Just copied. Why can you call 
# cb without arguments?
)),)
class RegWizard(SessionWizardView):
    def get_form_instance(self, step):
        instance = None

        if step == '0':
            instance = Attendees.objects.filter(owner__owner=self.request.user)
        #...

(link to source)

urls.py

#...
url(r'^register/$', views.RegWizard.as_view(views.reg_wiz_forms), name='register'),
#...

I also read about curry and other stuff and tried to override get_initkwargs and other methods of WizardView but I couldn't find any more docu or hints on that topic. Maybe someone can help me out.

Update

It works partly with curry now. Partly because it doesn't work with a manage function:

views.py

def manage_wizard(request, event):
    AttendeeFormSet = modelformset_factory(Attendee, form=AttendeeForm, exclude='event')
    AttendeeFormSet.form = staticmethod(curry(AttendeeForm, user=request.user))
    wiz = RegWizard
    wiz.event = event
    return wiz.as_view([('attendees', AttendeeFormSet)])

I get an AttributeError: "'function' object has no attribute 'base_fields'" This sounds like the same issue here.

But when I override WizardView's get_form_list and call it in url.py directly it works:

reg_wiz_forms = ('attendees', modelformset_factory(Attendee, form=AttendeeForm, exclude='event'))

class RegWizard(SessionWizardView):
    def get_form_list(self):
        self.form_list['attendees'].form = staticmethod(curry(AttendeeForm, user=self.request.user))
        return super(RegWizard, self).get_form_list()

Now I'm wondering if there is a solution without overriding the method.

Was it helpful?

Solution

You simply want your foreign key queryset for the attendee attribute on your Model to be a filtered one on your ModelForm. You are on the right lines here:

self.fields['attendee'].queryset = Person.objects.filter(owner=user)

This is assuming an attribute 'owner' exists on the Person class.

This won't work however, as where or what is your user arg? One solution is to curry the forms init method, as you mention, to include the correct user object:

    form = staticmethod(curry(AttendeeForm, user=<the-user-obj>))

Now you pop your user arg from the kwargs in your init method:

user = kwargs.pop('user')

Now your filtered queryset will only display the Person you filtered for.

def __init__(self, user, *args, **kwargs): I tried this but I didn't get 
it to work.

The line above won't work for a number of reasons, the primary being that it will never be called from anywhere, you are making a new function there, not overriding an existing one, which is what we are doing overring the init method.

Just some possibly helpful advice on design - You've got many threads here all providing many different ideas, complicating things a lot. Try to filter your problem to basic concepts. Here, it's a presentation of data issue, so think about starting in the Form, that's what it's there for. :-)

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top