From babfe78494028415b0e5f74ec2ca9b66506e8d34 Mon Sep 17 00:00:00 2001
From: Gary Wilson Jr <gary.wilson@gmail.com>
Date: Tue, 13 Nov 2007 14:36:29 +0000
Subject: [PATCH] Fixed #4787, #5913 -- Updating the queryset on a
 `ModelChoiceField` or `ModelMultipleChoiceField` now updates its widget's
 choices.  The clean methods for `ModelChoiceField` and
 `ModelMultipleChoiceField` were changed to only allow choices in the
 specified queryset (instead of allowing all choices returned by the queryset
 model's default manager).

git-svn-id: http://code.djangoproject.com/svn/django/trunk@6670 bcc190cf-cafb-0310-a4f2-bffc1f526a37
---
 django/newforms/models.py              | 14 ++++++++---
 tests/modeltests/model_forms/models.py | 32 ++++++++++++++++++++++++++
 2 files changed, 43 insertions(+), 3 deletions(-)

diff --git a/django/newforms/models.py b/django/newforms/models.py
index 269166c5d5..c86b9b3a42 100644
--- a/django/newforms/models.py
+++ b/django/newforms/models.py
@@ -155,14 +155,22 @@ class ModelChoiceField(ChoiceField):
     def __init__(self, queryset, empty_label=u"---------", cache_choices=False,
                  required=True, widget=Select, label=None, initial=None,
                  help_text=None):
-        self.queryset = queryset
         self.empty_label = empty_label
         self.cache_choices = cache_choices
         # Call Field instead of ChoiceField __init__() because we don't need
         # ChoiceField.__init__().
         Field.__init__(self, required, widget, label, initial, help_text)
+        self.queryset = queryset
+
+    def _get_queryset(self):
+        return self._queryset
+
+    def _set_queryset(self, queryset):
+        self._queryset = queryset
         self.widget.choices = self.choices
 
+    queryset = property(_get_queryset, _set_queryset)
+
     def _get_choices(self):
         # If self._choices is set, then somebody must have manually set
         # the property self.choices. In this case, just return self._choices.
@@ -190,7 +198,7 @@ class ModelChoiceField(ChoiceField):
         if value in ('', None):
             return None
         try:
-            value = self.queryset.model._default_manager.get(pk=value)
+            value = self.queryset.get(pk=value)
         except self.queryset.model.DoesNotExist:
             raise ValidationError(ugettext(u'Select a valid choice. That'
                                            u' choice is not one of the'
@@ -217,7 +225,7 @@ class ModelMultipleChoiceField(ModelChoiceField):
         final_values = []
         for val in value:
             try:
-                obj = self.queryset.model._default_manager.get(pk=val)
+                obj = self.queryset.get(pk=val)
             except self.queryset.model.DoesNotExist:
                 raise ValidationError(ugettext(u'Select a valid choice. %s is'
                                                u' not one of the available'
diff --git a/tests/modeltests/model_forms/models.py b/tests/modeltests/model_forms/models.py
index 66865976c8..e4e230c98d 100644
--- a/tests/modeltests/model_forms/models.py
+++ b/tests/modeltests/model_forms/models.py
@@ -440,6 +440,8 @@ the data in the database when the form is instantiated.
 >>> from django.newforms import ModelChoiceField, ModelMultipleChoiceField
 
 >>> f = ModelChoiceField(Category.objects.all())
+>>> list(f.choices)
+[(u'', u'---------'), (1, u'Entertainment'), (2, u"It's a test"), (3, u'Third'), (4, u'Fourth')]
 >>> f.clean('')
 Traceback (most recent call last):
 ...
@@ -485,9 +487,23 @@ Traceback (most recent call last):
 ...
 ValidationError: [u'Select a valid choice. That choice is not one of the available choices.']
 
+# queryset can be changed after the field is created.
+>>> f.queryset = Category.objects.exclude(name='Fourth')
+>>> list(f.choices)
+[(u'', u'---------'), (1, u'Entertainment'), (2, u"It's a test"), (3, u'Third')]
+>>> f.clean(3)
+<Category: Third>
+>>> f.clean(4)
+Traceback (most recent call last):
+...
+ValidationError: [u'Select a valid choice. That choice is not one of the available choices.']
+
+
 # ModelMultipleChoiceField ####################################################
 
 >>> f = ModelMultipleChoiceField(Category.objects.all())
+>>> list(f.choices)
+[(1, u'Entertainment'), (2, u"It's a test"), (3, u'Third'), (4, u'Fourth')]
 >>> f.clean(None)
 Traceback (most recent call last):
 ...
@@ -552,6 +568,22 @@ Traceback (most recent call last):
 ...
 ValidationError: [u'Select a valid choice. 10 is not one of the available choices.']
 
+# queryset can be changed after the field is created.
+>>> f.queryset = Category.objects.exclude(name='Fourth')
+>>> list(f.choices)
+[(1, u'Entertainment'), (2, u"It's a test"), (3, u'Third')]
+>>> f.clean([3])
+[<Category: Third>]
+>>> f.clean([4])
+Traceback (most recent call last):
+...
+ValidationError: [u'Select a valid choice. 4 is not one of the available choices.']
+>>> f.clean(['3', '4'])
+Traceback (most recent call last):
+...
+ValidationError: [u'Select a valid choice. 4 is not one of the available choices.']
+
+
 # PhoneNumberField ############################################################
 
 >>> PhoneNumberForm = form_for_model(PhoneNumber)