Opened 4 years ago

Closed 4 years ago

Last modified 4 years ago

#17137 closed Bug (invalid)

ModelForm is_valid() accepts options outside choices

Reported by: hayrynen@… Owned by: poirier
Component: Forms Version: 1.3
Severity: Normal Keywords:
Cc: Triage Stage: Accepted
Has patch: no Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description (last modified by aaugustin)

If form is made using ModelForm and choices of ManyToMany field are limited like this:

# models.py

class Product(models.Model):
   name = models.CharField(max_length=60, verbose_name="Name")
   visibility = models.ManyToManyField(Group, verbose_name="Groups")

# views.py

class ProductForm(forms.ModelForm):
   def __init__(self, group_choices, *args, **kwargs):
       super(ProductForm, self).__init__(*args, **kwargs)
       self.fields['visibility'].choices = group_choices
   class Meta:
       model = Product

@login_required
def products_view(request):
   userprofile = UserProfile.objects.get(user = request.user)
   groups = [ (i.id, i.group) for i in Membership.objects.filter(userprofile=userprofile) ]

   if request.method == "POST":
       form = ProductForm(groups, request.POST)
       if form.is_valid():
           .. do stuff with visibility trusting form.is_valid()

is_valid() allows values for ManyToMany selections outside what is specified in the form as long as there exists corresponding ID in the database. Obviously the submitted form needs to be modified outside standard browser UI.

is_valid() should compare selections against .choices, not against what exists in the database.

Change History (4)

comment:1 Changed 4 years ago by aaugustin

  • Description modified (diff)
  • Needs documentation unset
  • Needs tests unset
  • Patch needs improvement unset

Fixed formatting -- please use wiki syntax and preview.

comment:2 Changed 4 years ago by poirier

  • Owner changed from nobody to poirier
  • Status changed from new to assigned

comment:3 Changed 4 years ago by poirier

  • Resolution set to invalid
  • Status changed from assigned to closed
  • Triage Stage changed from Unreviewed to Accepted

From https://docs.djangoproject.com/en/dev/topics/forms/modelforms/:

  ManyToManyField is represented by django.forms.ModelMultipleChoiceField, which is a MultipleChoiceField whose choices are a model QuerySet.

Validation of a ModelMultipleChoiceField is controlled by the queryset value, not choices.

comment:4 Changed 4 years ago by charettes

@hayrynen I think you should try using queryset attribute instead.

class ProductForm(forms.ModelForm):
   def __init__(self, visibility_qs, *args, **kwargs):
       super(ProductForm, self).__init__(*args, **kwargs)
       self.fields['visibility'].queryset = visibility_qs
   class Meta:
       model = Product

@login_required
def products_view(request):
   userprofile = UserProfile.objects.get(user = request.user)
   # assuming Membership.group's related_name is unset
   visibility_qs = Groups.objects.filter(membership_set__userprofile=userprofile)

   if request.method == "POST":
       form = ProductForm(visibility_qs, request.POST)
       if form.is_valid():
           .. do stuff with visibility trusting form.is_valid()
Note: See TracTickets for help on using tickets.
Back to Top