Ticket #11707: limit_ForeignKey.trunk.patch

File limit_ForeignKey.trunk.patch, 5.2 KB (added by Chris Wesseling, 13 years ago)

Refactored less complex against trunk

  • django/forms/models.py

    diff -r 38c339d6093d django/forms/models.py
    a b  
    879879            yield (u"", self.field.empty_label)
    880880        if self.field.cache_choices:
    881881            if self.field.choice_cache is None:
    882                 self.field.choice_cache = [
    883                     self.choice(obj) for obj in self.queryset.all()
    884                 ]
     882                self.field.choice_cache = list(self.distinct_choices())
    885883            for choice in self.field.choice_cache:
    886884                yield choice
    887885        else:
    888             for obj in self.queryset.all():
     886            for choice in self.distinct_choices():
     887                yield choice
     888
     889    def distinct_choices(self):
     890        """Yields a choice for each distinct object in the queryset.
     891
     892        If you use limit_choices_to you could have more than one results for a
     893        single Instance. DISTINCT in the db won't work as expected so we do
     894        the check here."""
     895        seen_choices = set()
     896        for obj in self.queryset.all():
     897            if obj.pk not in seen_choices:
     898                seen_choices.add(obj.pk)
    889899                yield self.choice(obj)
    890900
    891901    def __len__(self):
     
    975985            return None
    976986        try:
    977987            key = self.to_field_name or 'pk'
    978             value = self.queryset.get(**{key: value})
     988            values = self.queryset.filter(**{key: value})
     989            # If you use limit_choices_to you could have more than one results
     990            # for a single Instance. DISTINCT in the db won't work as expected
     991            # so we do the check here.
     992            if not values:
     993                raise self.queryset.model.DoesNotExist
     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.MultipleObjectsReturned
    9791002        except (ValueError, self.queryset.model.DoesNotExist):
    9801003            raise ValidationError(self.error_messages['invalid_choice'])
    9811004        return value
  • tests/regressiontests/model_fields/models.py

    diff -r 38c339d6093d 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 38c339d6093d 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, RenamedField
     12from models import Foo, Bar, Baz, Whiz, BigD, BigS, Image, BigInt, Post, NullBooleanModel, BooleanModel, Document, RenamedField
    1213
    1314# If PIL available, do these tests.
    1415if Image:
     
    109110        # This should not crash. That counts as a win for our purposes.
    110111        Foo.objects.filter(d__gte=100000000000)
    111112
     113
     114class BazForm(forms.ModelForm):
     115    class Meta:
     116        model = Baz
     117
     118
    112119class ForeignKeyTests(test.TestCase):
    113120    def test_callable_default(self):
    114121        """Test the use of a lazy callable for ForeignKey.default"""
     
    116123        b = Bar.objects.create(b="bcd")
    117124        self.assertEqual(b.a, a)
    118125
     126    def test_distinct_choice_limit(self):
     127        """Doesn't make sense to offer the same ForeignKey multiple times in a form"""
     128        a = Foo.objects.create(a='a', d=Decimal("-1"))
     129        b = Foo.objects.create(a='b', d=Decimal("1"))
     130        Bar.objects.create(b='ah', a=a)
     131        Bar.objects.create(b='aha', a=a)
     132        Bar.objects.create(b='bla', a=b)
     133        form = BazForm()
     134        fk_field = str(form['foo'])
     135        self.assertEqual(len(re.findall(r'value="%s"' % b.pk, fk_field)), 0)
     136        self.assertEqual(len(re.findall(r'value="%s"' % a.pk, fk_field)), 1)
     137
     138    def test_distinct_choice_limit_save(self):
     139        """Doesn't make sense to offer the same ForeignKey multiple times in a form"""
     140        a = Foo.objects.create(a='a', d=Decimal("-1"))
     141        b = Foo.objects.create(a='b', d=Decimal("1"))
     142        Bar.objects.create(b='ah', a=a)
     143        Bar.objects.create(b='aha', a=a)
     144        Bar.objects.create(b='bla', a=b)
     145        form = BazForm({'foo': a.pk, 'a': 'a'})
     146        self.assertTrue(form.is_valid())
     147        obj = form.save()
     148        self.assertEqual(obj.foo.pk, a.pk)
     149
    119150class DateTimeFieldTests(unittest.TestCase):
    120151    def test_datetimefield_to_python_usecs(self):
    121152        """DateTimeField.to_python should support usecs"""
Back to Top