#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.

Reply via email to