2

I want to limit the choices of a ManyToManyField to those matching a ForeignKey. The form displays properly, but upon saving results in an error Select a valid choice. <choice> is not one of the available choices.

Before I was trying to limit the queryset by passing a parameter in the view to the form, and then using that parameter to filter the queryset.

Models:

class VenueEventTimeslot(models.Model):
    venue = models.ForeignKey(Venue)
    name = models.CharField(max_length=255)

class VenueEvent(models.Model):
    venue = models.ForeignKey(Venue)
    event_timeslots = models.ManyToManyField(VenueEventTimeslot)    

class VenueEventForm(ModelForm):
    event_timeslots = ModelMultipleChoiceField(queryset=None, widget=CheckboxSelectMultiple())

    def __init__(self, *args, **kwargs): # limit timeslots to those of the venue only
        venue_obj = kwargs.pop('venue_obj',None)
        super(VenueEventForm, self).__init__(*args,**kwargs)
        self.fields['event_timeslots'].queryset=VenueEventTimeslot.objects.filter(venue=venue_obj)

    class Meta:
        model = VenueEvent
        fields = ['event_timeslots']

Views:

@login_required
def calendar(request, pk):
    venue = Venue.objects.get(pk = pk)

    if request.method == "POST":
        form = VenueEventForm(request.POST)
        if form.is_valid():
            # form stuff
        else:
            form = VenueEventForm(venue_obj = venue)

    context = {'venue':venue, 'form':form}
    return render(request, ... , context)

However, if I pass the queryset from the view, it works perfectly.

Models:

class VenueEventTimeslot(models.Model):
    # same as above

class VenueEvent(models.Model):
    # same as above 

class VenueEventForm(ModelForm):
    class Meta:
    model = VenueEvent
    fields = ['date','client_name','event_timeslots']
    widgets = {
        'date': SelectDateWidget(),
        'event_timeslots': CheckboxSelectMultiple(),
    }

Views:

@login_required
def calendar(request, pk):
    venue = Venue.objects.get(pk = pk)

    if request.method == "POST":
        form = VenueEventForm(request.POST)
        if form.is_valid():
            # form stuff
        else:
            form = VenueEventForm()
    form.fields['event_timeslots'].queryset=VenueEventTimeslot.objects.filter(venue=venue)

    context = {'venue':venue, 'form':form}
    return render(request, ..., context)

Would anyone be able to shed some light on this?

LokiRagnarok
  • 220
  • 1
  • 10
wasabigeek
  • 2,923
  • 1
  • 21
  • 30

1 Answers1

1

I just solved a problem similar to this yesterday which is right here, How To Exclude A Value In A ModelMultipleChoiceField?, but I think the issue with your init function is the way it is formatted. Instead of venue=venue_obj, you need to change it to pk=venue_obj because it appear you are getting the pk of venue in the view instead of the venue attribute of VenueEvent , and I reformatted your form a bit to make it look cleaner.

forms.py

class VenueEventForm(ModelForm):

    def __init__(self, *args, **kwargs): # limit timeslots to those of the venue only
        venue_obj = kwargs.pop('venue_obj')
        super(VenueEventForm, self).__init__(*args,**kwargs)
        self.fields['event_timeslots'] = forms.ModelMultipleChoiceField(widget=forms.CheckboxSelectMultiple(), queryset=VenueEventTimeslot.objects.filter(pk=venue_obj))

    class Meta:
        model = VenueEvent
        fields = ['event_timeslots']

views.py

@login_required
def calendar(request, pk):
    venue = Venue.objects.get(pk = pk)
    if request.method == "POST":
        form = VenueEventForm(request.POST, venue_obj=venue)
        if form.is_valid():
            # form stuff
        else:
            print VenueEventForm.errors

    else:
        form = VenueEventForm(venue_obj=venue)
        context = {'venue':venue, 'form':form}
        return render(request, ... , context)
Community
  • 1
  • 1
  • Sorry, where do you see venue = venue? – wasabigeek Aug 14 '15 at 19:53
  • I see where you had it now, I looked at the view by mistake, let me see if I can edit my answer here. –  Aug 14 '15 at 19:58
  • what exactly are you trying to pop in __inti__ function what are you trying to take out of the list exactly. –  Aug 14 '15 at 20:07
  • I'm filtering VenueEventTimeslots that match the current Venue. Hence passing the venue from view into init. I want to stress again that the form displays fine. It gives an error on submit only. – wasabigeek Aug 14 '15 at 20:11
  • Okay I edited my answer maybe this will work, if it does not I tried because I have nothing else. –  Aug 14 '15 at 20:25