Ticket #11707: limit_ForeignKey.4.patch

File limit_ForeignKey.4.patch, 5.6 KB (added by Chris Wesseling, 13 years ago)

just removed a the previous fix from the comments

  • django/forms/models.py

    diff -r 06744e116e42 django/forms/models.py
    a b  
    873873        self.queryset = field.queryset
    874874
    875875    def __iter__(self):
     876        """on the memo dictionary:
     877        If you use limit_choices_to you could have more than one results
     878        for a single Instance. DISTINCT in the db won't work as expected
     879        so we do the check here. If SQL gets fixed remove this.
     880        """
    876881        if self.field.empty_label is not None:
    877882            yield (u"", self.field.empty_label)
    878883        if self.field.cache_choices:
    879884            if self.field.choice_cache is None:
    880                 self.field.choice_cache = [
    881                     self.choice(obj) for obj in self.queryset.all()
    882                 ]
    883             for choice in self.field.choice_cache:
    884                 yield choice
     885                self.field.choice_cache = []
     886                memo = {}
     887                for obj in self.queryset.all():
     888                    if obj.pk not in memo:
     889                        memo[obj.pk] = 0
     890                        choice = self.choice(obj)
     891                        self.field_choice_cache.append(choice)
     892                        yield choice
     893            else:
     894                for choice in self.field.choice_cache:
     895                    yield choice
    885896        else:
     897            memo = {}
    886898            for obj in self.queryset.all():
    887                 yield self.choice(obj)
     899                if obj.pk not in memo:
     900                    memo[obj.pk] = 0
     901                    yield self.choice(obj)
    888902
    889903    def __len__(self):
    890904        return len(self.queryset)
     
    973987            return None
    974988        try:
    975989            key = self.to_field_name or 'pk'
    976             value = self.queryset.get(**{key: value})
     990            values = self.queryset.filter(**{key: value})
     991            # If you use limit_choices_to you could have more than one results
     992            # for a single Instance. DISTINCT in the db won't work as expected
     993            # so we do the check here. If SQL gets fixed remove this.
     994            if len(values) == 1:
     995                value = values[0]
     996            else:
     997                pks = set(value.pk for value in values)
     998                if len(pks) == 1:
     999                    value = values[0]
     1000                else:
     1001                    raise self.queryset.model.MultipleObjectReturned
    9771002        except (ValueError, self.queryset.model.DoesNotExist):
    9781003            raise ValidationError(self.error_messages['invalid_choice'])
    9791004        return value
  • tests/regressiontests/model_fields/models.py

    diff -r 06744e116e42 tests/regressiontests/model_fields/models.py
    a b  
    2929    b = models.CharField(max_length=10)
    3030    a = models.ForeignKey(Foo, default=get_foo)
    3131
     32class Baz(models.Model):
     33    a = models.CharField(max_length=5)
     34    #Only Foos related to Bars starting with 'a'
     35    foo = models.ForeignKey(Foo, limit_choices_to=models.Q(bar__b__startswith='a'))
     36
    3237class Whiz(models.Model):
    3338    CHOICES = (
    3439        ('Group 1', (
  • tests/regressiontests/model_fields/tests.py

    diff -r 06744e116e42 tests/regressiontests/model_fields/tests.py
    a b  
    11import datetime
    22from decimal import Decimal
     3import re
    34
    45from django import test
    56from django import forms
     
    89from django.db.models.fields.files import FieldFile
    910from django.utils import unittest
    1011
    11 from models import Foo, Bar, Whiz, BigD, BigS, Image, BigInt, Post, NullBooleanModel, BooleanModel, Document
     12from models import Foo, Bar, Baz, Whiz, BigD, BigS, Image, BigInt, Post, NullBooleanModel, BooleanModel, Document
    1213
    1314# If PIL available, do these tests.
    1415if Image:
     
    104105        # This should not crash. That counts as a win for our purposes.
    105106        Foo.objects.filter(d__gte=100000000000)
    106107
     108class BazForm(forms.ModelForm):
     109    class Meta:
     110        model = Baz
     111
    107112class ForeignKeyTests(test.TestCase):
    108113    def test_callable_default(self):
    109114        """Test the use of a lazy callable for ForeignKey.default"""
     
    111116        b = Bar.objects.create(b="bcd")
    112117        self.assertEqual(b.a, a)
    113118
     119    def test_distinct_choice_limit(self):
     120        """Doesn't make sense to offer the same ForeignKey multiple times in a form"""
     121        a = Foo.objects.create(a='a', d=Decimal("-1"))
     122        b = Foo.objects.create(a='b', d=Decimal("1"))
     123        Bar.objects.create(b='ah', a=a)
     124        Bar.objects.create(b='aha', a=a)
     125        Bar.objects.create(b='bla', a=b)
     126        form = BazForm()
     127        fk_field = str(form['foo'])
     128        self.assertEqual(len(re.findall(r'value="%s"' % b.pk, fk_field)), 0)
     129        self.assertEqual(len(re.findall(r'value="%s"' % a.pk, fk_field)), 1)
     130
     131    def test_distinct_choice_limit_save(self):
     132        """Doesn't make sense to offer the same ForeignKey multiple times in a form"""
     133        a = Foo.objects.create(a='a', d=Decimal("-1"))
     134        b = Foo.objects.create(a='b', d=Decimal("1"))
     135        Bar.objects.create(b='ah', a=a)
     136        Bar.objects.create(b='aha', a=a)
     137        Bar.objects.create(b='bla', a=b)
     138        form = BazForm({'foo': a.pk, 'a': 'a'})
     139        self.assertTrue(form.is_valid())
     140        obj = form.save()
     141        self.assertEqual(obj.foo.pk, a.pk)
     142
    114143class DateTimeFieldTests(unittest.TestCase):
    115144    def test_datetimefield_to_python_usecs(self):
    116145        """DateTimeField.to_python should support usecs"""
Back to Top