Code

Opened 2 years ago

Closed 2 years ago

#17740 closed Bug (needsinfo)

BaseModelFormSet should use its queryset when initializing the ModelForm's pk_field's ModelChoiceField.

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

Description

BaseModelFormSet uses the queryset of the default model manager when creating the ModelChoiceField for the model's pk_field.

Line 656:

if isinstance(pk, OneToOneField) or isinstance(pk, ForeignKey):
    qs = pk.rel.to._default_manager.get_query_set()
else:
    qs = self.model._default_manager.get_query_set()
qs = qs.using(form.instance._state.db)
form.fields[self._pk_field.name] = ModelChoiceField(qs, initial=pk_value, required=False, widget=HiddenInput)

The ModelFormSet will not validate if it is initialized with a queryset containing objects that are not in the model's default manager's queryset.

Attachments (0)

Change History (2)

comment:1 Changed 2 years ago by larsent

  • Needs documentation unset
  • Needs tests unset
  • Patch needs improvement unset

Code to reproduce this:

#create a ModelFormset with a queryset containing objects not in the default queryset 
MyFormSet = modelformset_factory(ModelA, form=ModelAForm, extra=0)
qs = ModelA.custom_manager_objects.all() #not the default manager. contains objects not in the default manager's queryset
fs = MyFormSet(request.POST, request.FILES, queryset=qs) #fs won't validate 

comment:2 Changed 2 years ago by aaugustin

  • Resolution set to needsinfo
  • Status changed from new to closed

Let me elaborate a bit on the bug report; it's terse and it took me a while to figure out what's going on.

BaseModelFormSet.add_fields(self, form, index) adds a field that contains the primary key to form. This field is invisible to the end user -- it uses a HiddenInput widget -- but it's necessary for Django to know which instance of the model form deals with, when it received the contents of form in the next request. Django uses a ModelChoiceField for this extra field: this makes sense for designating which instance the form applies to, among all existing instances of the model.

"All existing instances of the model" is qs = self.model._default_manager.get_query_set(). By definition -- and unless I missed something -- this queryset always contains all instances of the model. Could you explain how it's possible for your custom_manager to contain an object that isn't in _default_manager?

Add Comment

Modify Ticket

Change Properties
<Author field>
Action
as closed
as The resolution will be set. Next status will be 'closed'
The resolution will be deleted. Next status will be 'new'
Author


E-mail address and user name can be saved in the Preferences.

 
Note: See TracTickets for help on using tickets.