Ticket #8648: 8648.3.diff

File 8648.3.diff, 10.2 KB (added by Chris Beaven, 16 years ago)
  • django/db/models/fields/related.py

     
    686686        setattr(cls, related.get_accessor_name(), ForeignRelatedObjectsDescriptor(related))
    687687       
    688688    def formfield(self, **kwargs):
    689         defaults = {'form_class': forms.ModelChoiceField, 'queryset': self.rel.to._default_manager.complex_filter(self.rel.limit_choices_to)}
     689        defaults = {
     690            'form_class': forms.ModelChoiceField,
     691            'queryset': self.rel.to._default_manager.complex_filter(
     692                                                    self.rel.limit_choices_to),
     693            'to_field_name': self.rel.field_name,
     694        }
    690695        defaults.update(kwargs)
    691696        return super(ForeignKey, self).formfield(**defaults)
    692697
  • django/forms/models.py

     
    460460        if self.field.cache_choices:
    461461            if self.field.choice_cache is None:
    462462                self.field.choice_cache = [
    463                     (obj.pk, self.field.label_from_instance(obj))
    464                     for obj in self.queryset.all()
     463                    self.choice(obj) for obj in self.queryset.all()
    465464                ]
    466465            for choice in self.field.choice_cache:
    467466                yield choice
    468467        else:
    469468            for obj in self.queryset.all():
    470                 yield (obj.pk, self.field.label_from_instance(obj))
     469                yield self.choice(obj)
    471470
     471    def choice(self, obj):
     472        if self.field.to_field_name:
     473            key = getattr(obj, self.field.to_field_name)
     474        else:
     475            key = obj.pk
     476        return (key, self.field.label_from_instance(obj))
     477
     478
    472479class ModelChoiceField(ChoiceField):
    473480    """A ChoiceField whose choices are a model QuerySet."""
    474481    # This class is a subclass of ChoiceField for purity, but it doesn't
     
    480487
    481488    def __init__(self, queryset, empty_label=u"---------", cache_choices=False,
    482489                 required=True, widget=None, label=None, initial=None,
    483                  help_text=None, *args, **kwargs):
     490                 help_text=None, to_field_name=None, *args, **kwargs):
    484491        self.empty_label = empty_label
    485492        self.cache_choices = cache_choices
    486493
     
    490497                       *args, **kwargs)
    491498        self.queryset = queryset
    492499        self.choice_cache = None
     500        self.to_field_name = to_field_name
    493501
    494502    def _get_queryset(self):
    495503        return self._queryset
     
    532540        if value in EMPTY_VALUES:
    533541            return None
    534542        try:
    535             value = self.queryset.get(pk=value)
     543            key = self.to_field_name or 'pk'
     544            value = self.queryset.get(**{key: value})
    536545        except self.queryset.model.DoesNotExist:
    537546            raise ValidationError(self.error_messages['invalid_choice'])
    538547        return value
  • tests/modeltests/model_forms/models.py

     
    7878class WriterProfile(models.Model):
    7979    writer = models.OneToOneField(Writer, primary_key=True)
    8080    age = models.PositiveIntegerField()
    81    
     81
    8282    def __unicode__(self):
    8383        return "%s is %s" % (self.writer, self.age)
    8484
     
    120120class ArticleStatus(models.Model):
    121121    status = models.CharField(max_length=2, choices=ARTICLE_STATUS_CHAR, blank=True, null=True)
    122122
     123class Inventory(models.Model):
     124   barcode = models.PositiveIntegerField(unique=True)
     125   parent = models.ForeignKey('self', to_field='barcode', blank=True, null=True)
     126   name = models.CharField(blank=False, max_length=20)
     127
     128   def __unicode__(self):
     129      return self.name
     130
    123131__test__ = {'API_TESTS': """
    124132>>> from django import forms
    125133>>> from django.forms.models import ModelForm, model_to_dict
     
    11171125Traceback (most recent call last):
    11181126...
    11191127ValidationError: [u'Enter only digits separated by commas.']
    1120 >>> f.clean(',,,,') 
     1128>>> f.clean(',,,,')
    11211129u',,,,'
    11221130>>> f.clean('1.2')
    11231131Traceback (most recent call last):
     
    11521160...
    11531161ValidationError: [u'Select a valid choice. z is not one of the available choices.']
    11541162
     1163# Foreign keys which use to_field #############################################
     1164
     1165>>> apple = Inventory.objects.create(barcode=86, name='Apple')
     1166>>> pear = Inventory.objects.create(barcode=22, name='Pear')
     1167>>> core = Inventory.objects.create(barcode=87, name='Core', parent=apple)
     1168
     1169>>> field = ModelChoiceField(Inventory.objects.all(), to_field_name='barcode')
     1170>>> for choice in field.choices:
     1171...     print choice
     1172(u'', u'---------')
     1173(86, u'Apple')
     1174(22, u'Pear')
     1175(87, u'Core')
     1176
     1177>>> class InventoryForm(ModelForm):
     1178...     class Meta:
     1179...         model = Inventory
     1180>>> form = InventoryForm(instance=core)
     1181>>> print form['parent']
     1182<select name="parent" id="id_parent">
     1183<option value="">---------</option>
     1184<option value="86" selected="selected">Apple</option>
     1185<option value="22">Pear</option>
     1186<option value="87">Core</option>
     1187</select>
     1188
     1189>>> data = model_to_dict(core)
     1190>>> data['parent'] = '22'
     1191>>> form = InventoryForm(data=data, instance=core)
     1192>>> core = form.save()
     1193>>> core.parent
     1194<Inventory: Pear>
    11551195"""}
  • tests/regressiontests/admin_widgets/models.py

     
    55
    66class Member(models.Model):
    77    name = models.CharField(max_length=100)
    8    
     8
    99    def __unicode__(self):
    1010        return self.name
    1111
    1212class Band(models.Model):
    1313    name = models.CharField(max_length=100)
    1414    members = models.ManyToManyField(Member)
    15    
     15
    1616    def __unicode__(self):
    1717        return self.name
    1818
     
    2020    band = models.ForeignKey(Band)
    2121    name = models.CharField(max_length=100)
    2222    cover_art = models.FileField(upload_to='albums')
    23    
     23
    2424    def __unicode__(self):
    2525        return self.name
    2626
     27class Inventory(models.Model):
     28   barcode = models.PositiveIntegerField(unique=True)
     29   parent = models.ForeignKey('self', to_field='barcode', blank=True, null=True)
     30   name = models.CharField(blank=False, max_length=20)
     31
     32   def __unicode__(self):
     33      return self.name
     34
    2735__test__ = {'WIDGETS_TESTS': """
    2836>>> from datetime import datetime
    2937>>> from django.utils.html import escape, conditional_escape
     
    8492>>> w._has_changed([1, 2], [u'1', u'3'])
    8593True
    8694
     95# Check that ForeignKeyRawIdWidget works with fields which aren't related to
     96# the model's primary key.
     97>>> apple = Inventory.objects.create(barcode=86, name='Apple')
     98>>> pear = Inventory.objects.create(barcode=22, name='Pear')
     99>>> core = Inventory.objects.create(barcode=87, name='Core', parent=apple)
     100>>> rel = Inventory._meta.get_field('parent').rel
     101>>> w = ForeignKeyRawIdWidget(rel)
     102>>> print w.render('test', core.parent_id, attrs={})
     103<input type="text" name="test" value="86" class="vForeignKeyRawIdAdminField" /><a href="../../../admin_widgets/inventory/" class="related-lookup" id="lookup_id_test" onclick="return showRelatedObjectLookupPopup(this);"> <img src="/admin_media/img/admin/selector-search.gif" width="16" height="16" alt="Lookup" /></a>&nbsp;<strong>Apple</strong>
    87104""" % {
    88105    'ADMIN_MEDIA_PREFIX': settings.ADMIN_MEDIA_PREFIX,
    89106    'STORAGE_URL': default_storage.url(''),
  • django/contrib/admin/widgets.py

     
    4141
    4242class AdminDateWidget(forms.TextInput):
    4343    class Media:
    44         js = (settings.ADMIN_MEDIA_PREFIX + "js/calendar.js", 
     44        js = (settings.ADMIN_MEDIA_PREFIX + "js/calendar.js",
    4545              settings.ADMIN_MEDIA_PREFIX + "js/admin/DateTimeShortcuts.js")
    46        
     46
    4747    def __init__(self, attrs={}):
    4848        super(AdminDateWidget, self).__init__(attrs={'class': 'vDateField', 'size': '10'})
    4949
    5050class AdminTimeWidget(forms.TextInput):
    5151    class Media:
    52         js = (settings.ADMIN_MEDIA_PREFIX + "js/calendar.js", 
     52        js = (settings.ADMIN_MEDIA_PREFIX + "js/calendar.js",
    5353              settings.ADMIN_MEDIA_PREFIX + "js/admin/DateTimeShortcuts.js")
    5454
    5555    def __init__(self, attrs={}):
    5656        super(AdminTimeWidget, self).__init__(attrs={'class': 'vTimeField', 'size': '8'})
    57    
     57
    5858class AdminSplitDateTime(forms.SplitDateTimeWidget):
    5959    """
    6060    A SplitDateTime Widget that has some admin-specific styling.
     
    8686    """
    8787    def __init__(self, attrs={}):
    8888        super(AdminFileWidget, self).__init__(attrs)
    89        
     89
    9090    def render(self, name, value, attrs=None):
    9191        output = []
    9292        if value and hasattr(value, "url"):
     
    121121        if value:
    122122            output.append(self.label_for_value(value))
    123123        return mark_safe(u''.join(output))
    124    
     124
    125125    def label_for_value(self, value):
    126         return '&nbsp;<strong>%s</strong>' % \
    127             truncate_words(self.rel.to.objects.get(pk=value), 14)
    128            
     126        key = self.rel.get_related_field().name
     127        obj = self.rel.to.objects.get(**{key: value})
     128        return '&nbsp;<strong>%s</strong>' % truncate_words(obj, 14)
     129
    129130class ManyToManyRawIdWidget(ForeignKeyRawIdWidget):
    130131    """
    131132    A Widget for displaying ManyToMany ids in the "raw_id" interface rather than
     
    133134    """
    134135    def __init__(self, rel, attrs=None):
    135136        super(ManyToManyRawIdWidget, self).__init__(rel, attrs)
    136    
     137
    137138    def render(self, name, value, attrs=None):
    138139        attrs['class'] = 'vManyToManyRawIdAdminField'
    139140        if value:
     
    141142        else:
    142143            value = ''
    143144        return super(ManyToManyRawIdWidget, self).render(name, value, attrs)
    144    
     145
    145146    def label_for_value(self, value):
    146147        return ''
    147148
     
    152153        if value:
    153154            return [value]
    154155        return None
    155    
     156
    156157    def _has_changed(self, initial, data):
    157158        if initial is None:
    158159            initial = []
Back to Top