#18272: ModelChoiceField's queryset attribute persists between two form
instances
----------------------------+---------------------------------------
Reporter: bmispelon | Owner: nobody
Type: Bug | Status: new
Component: Forms | Version: master
Severity: Normal | Keywords: modelchoicefield deepcopy
Triage Stage: Unreviewed | Has patch: 0
Easy pickings: 0 | UI/UX: 0
----------------------------+---------------------------------------
== Setup ==
Using the models from the tutorial:
{{{#!python
from django.db import models
class Poll(models.Model):
question = models.CharField(max_length=200)
pub_date = models.DateTimeField('date published')
class Choice(models.Model):
poll = models.ForeignKey(Poll)
choice = models.CharField(max_length=200)
votes = models.IntegerField()
}}}
And the following form:
{{{#!python
from django import forms
from polls.models import Poll
class ChoiceForm(forms.Form):
poll = forms.ModelChoiceField(queryset=Poll.objects.all())
def __init__(self, *args, **kwargs):
"""Change the choices attribute of the field `poll`."""
super(ChoiceForm, self).__init__(*args, **kwargs)
queryset = self.fields['poll'].queryset
choices = [(poll.pk, poll.pk) for poll in queryset]
self.fields['poll'].choices = choices
}}}
== Bug ==
When doing so, the queryset gets "stuck" on what it was the first time the
form was instanciated: added Poll objects don't show up in it, and deleted
polls are still present.
{{{#!python
from datetime import datetime
from polls.forms import ChoiceForm
from polls.models import Poll
Poll.objects.all().delete() # Just making sure.
f = ChoiceForm()
print f['poll'].as_widget()
# '<select name="poll" id="id_poll">\n</select>'
Poll.objects.create(question="What is your favourite colour?",
pub_date=datetime.now())
f = ChoiceForm()
print f['poll'].as_widget()
# Expected: '<select name="poll" id="id_poll">\n<option
value="1">1</option>\n</select>'
# Result: '<select name="poll" id="id_poll">\n</select>'
}}}
== Workaround ==
One workaround is to copy the queryset before iterating over it:
{{{#!python
queryset = self.fields['poll'].queryset.all()
}}}
== Analysis ==
I believe the issue lies in {{{ModelChoiceField.__deepcopy__()}}}, in
particular the line:
{{{#!python
result.queryset = result.queryset
}}}
{{{queryset}}} being a property, this ends up doing (among other things
which I think aren't relevant here):
{{{#!python
result._queryset = result._queryset
}}}
And since {{{_queryset}}} has been iterated over, its cache is filled and
it gets attached onto the deepcopied instance.
== Fix ==
My naive fix would be to make a copy of result.queryset in the
{{{__deepcopy__}}} method, like so:
{{{#!python
result.queryset = result.queryset.all()
}}}
But my understanding of the issue is limited and I probably don't know the
ins and outs of this (especially considering the lengthy discussion around
ticket #11183).
--
Ticket URL: <https://code.djangoproject.com/ticket/18272>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.
--
You received this message because you are subscribed to the Google Groups
"Django updates" group.
To post to this group, send email to [email protected].
To unsubscribe from this group, send email to
[email protected].
For more options, visit this group at
http://groups.google.com/group/django-updates?hl=en.