﻿id	summary	reporter	owner	description	type	status	component	version	severity	resolution	keywords	cc	stage	has_patch	needs_docs	needs_tests	needs_better_patch	easy	ui_ux
18272	ModelChoiceField's queryset attribute persists between two form instances	Baptiste Mispelon	Spark23	"== 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)."	Bug	closed	Forms	dev	Normal	wontfix	modelchoicefield deepcopy		Design decision needed	0	0	0	0	0	0
