Ticket #9976: 9976.generic-foreign-key-widget.diff
File 9976.generic-foreign-key-widget.diff, 11.2 KB (added by , 13 years ago) |
---|
-
django/contrib/admin/options.py
diff --git a/django/contrib/admin/options.py b/django/contrib/admin/options.py index f05b5cb..1ac9d11 100644
a b class BaseModelAdmin(object): 72 72 formfield_overrides = {} 73 73 readonly_fields = () 74 74 ordering = None 75 generic_fields = () 75 76 76 77 def __init__(self): 77 78 overrides = FORMFIELD_FOR_DBFIELD_DEFAULTS.copy() … … class BaseModelAdmin(object): 122 123 123 124 return formfield 124 125 126 # For generic foreign keys marked as generic_fields we use a special widget 127 elif db_field.name in [f.fk_field for f 128 in self.model._meta.virtual_fields 129 if f.name in self.generic_fields]: 130 for gfk in self.model._meta.virtual_fields: 131 if gfk.fk_field == db_field.name: 132 break 133 return db_field.formfield( 134 label=capfirst(gfk.name.replace('_', ' ')), 135 widget=widgets.GenericForeignKeyRawIdWidget( 136 gfk.ct_field, 137 self.admin_site._registry.keys())) 138 125 139 # If we've got overrides for the formfield defined, use 'em. **kwargs 126 140 # passed to formfield_for_dbfield override the defaults. 127 141 for klass in db_field.__class__.mro(): -
django/contrib/admin/validation.py
diff --git a/django/contrib/admin/validation.py b/django/contrib/admin/validation.py index 733f89d..0812407 100644
a b def validate(cls, model): 171 171 raise ImproperlyConfigured("'%s.%s' should be a boolean." 172 172 % (cls.__name__, attr)) 173 173 174 if hasattr(cls, 'generic_fields'): 175 check_isseq(cls, 'generic_fields', cls.generic_fields) 176 for i, field in enumerate(cls.generic_fields): 177 if field not in [f.name for f in model._meta.virtual_fields]: 178 raise ImproperlyConfigured( 179 "Item number %d in %s.generic_fields" 180 "is not a GenericForeignKey on %s" 181 % (i, cls.__name__, model.__name__)) 174 182 175 183 # inlines = [] 176 184 if hasattr(cls, 'inlines'): -
django/contrib/admin/widgets.py
diff --git a/django/contrib/admin/widgets.py b/django/contrib/admin/widgets.py index 0d1f2a9..b87c366 100644
a b Form Widget classes specific to the Django admin site. 5 5 import copy 6 6 from django import forms 7 7 from django.contrib.admin.templatetags.admin_static import static 8 from django.contrib.contenttypes.models import ContentType 8 9 from django.core.urlresolvers import reverse 9 10 from django.forms.widgets import RadioFieldRenderer 10 11 from django.forms.util import flatatt … … class AdminCommaSeparatedIntegerFieldWidget(forms.TextInput): 309 310 if attrs is not None: 310 311 final_attrs.update(attrs) 311 312 super(AdminCommaSeparatedIntegerFieldWidget, self).__init__(attrs=final_attrs) 313 314 class GenericForeignKeyRawIdWidget(ForeignKeyRawIdWidget): 315 def __init__(self, ct_field, cts=[], attrs=None): 316 self.ct_field = ct_field 317 self.cts = cts 318 forms.TextInput.__init__(self, attrs) 319 320 def render(self, name, value, attrs=None): 321 if attrs is None: 322 attrs = {} 323 related_url = '../../../' 324 params = self.url_parameters() 325 if params: 326 url = '?' + '&'.join(['%s=%s' % (k, v) for k, v in params.iteritems()]) 327 else: 328 url = '' 329 if 'class' not in attrs: 330 attrs['class'] = 'vForeignKeyRawIdAdminField' 331 output = [ 332 forms.TextInput.render(self, name, value, attrs), 333 "<a href=\"%(related)s%(url)s\" class=\"related-lookup\" " 334 "id=\"lookup_id_%(name)s\" onclick=\"return " 335 "showGenericRelatedObjectLookupPopup(document.getElementById(" 336 "'id_%(ct_field)s'), this, '%(related)s%(url)s');\"> " 337 % {'related': related_url, 'url': url, 'name': name, 338 'ct_field': self.ct_field}, 339 "<img src=\"%s\" width=\"16\" height=\"16\" alt=\"%s\" /></a>" 340 % (static('admin/img/selector-search.gif'), _('Lookup'))] 341 if value: 342 output.append(self.label_for_value(value)) 343 344 content_types = """ 345 <script type="text/javascript"> 346 var content_types = new Array(); 347 %s 348 </script>""" % ('\n'.join(["content_types[%s] = '%s/%s/';" 349 % (ContentType.objects.get_for_model(ct).id, 350 ct._meta.app_label, 351 ct._meta.object_name.lower()) 352 for ct in self.cts])) 353 return mark_safe(u''.join(output) + content_types) 354 355 def url_parameters(self): 356 return {} -
tests/regressiontests/admin_widgets/models.py
diff --git a/tests/regressiontests/admin_widgets/models.py b/tests/regressiontests/admin_widgets/models.py index c4489e0..8e16641 100644
a b 1 1 from django.db import models 2 2 from django.contrib.auth.models import User 3 3 from django.contrib.contenttypes import generic 4 from django.contrib.contenttypes.models import ContentType 4 5 5 6 class MyFileField(models.FileField): 6 7 pass … … class Advisor(models.Model): 99 100 """ 100 101 name = models.CharField(max_length=20) 101 102 companies = models.ManyToManyField(Company) 103 104 class Image(models.Model): 105 image = models.ImageField(upload_to='images') 106 description = models.CharField(max_length=200) 107 108 content_type = models.ForeignKey(ContentType, related_name='admin_widgets_image_set') 109 object_id = models.PositiveIntegerField() 110 object = generic.GenericForeignKey('content_type', 'object_id') 111 112 def __unicode__(self): 113 return u"%s - %s" % (self.image.name, self.description[:100]) 114 No newline at end of file -
tests/regressiontests/admin_widgets/tests.py
diff --git a/tests/regressiontests/admin_widgets/tests.py b/tests/regressiontests/admin_widgets/tests.py index a7bfe55..0c03916 100644
a b from django import forms 7 7 from django.conf import settings 8 8 from django.contrib import admin 9 9 from django.contrib.admin import widgets 10 from django.contrib.contenttypes.models import ContentType 10 11 from django.core.files.storage import default_storage 11 12 from django.core.files.uploadedfile import SimpleUploadedFile 12 13 from django.db.models import DateField … … from django.test import TestCase as DjangoTestCase 14 15 from django.utils import translation 15 16 from django.utils.html import conditional_escape 16 17 from django.utils.unittest import TestCase 18 from django.contrib.admin.widgets import GenericForeignKeyRawIdWidget 17 19 18 20 import models 19 21 from widgetadmin import site as widget_admin_site … … class AdminForeignKeyWidgetChangeList(DjangoTestCase): 148 150 self.assertTrue('%s/auth/user/add/' % self.admin_root in response.content) 149 151 150 152 153 class AdminGenericForeignKeyRawIdWidget(DjangoTestCase): 154 fixtures = ["admin-widgets-users.xml"] 155 admin_root = '/widget_admin' 156 157 def test_gfk_raw_id_field(self): 158 self.assertEqual( 159 GenericForeignKeyRawIdWidget(models.Image._meta.virtual_fields[0].ct_field).render('test', '', {}), 160 """<input type="text" name="test" class="vForeignKeyRawIdAdminField" /><a href="../../../" class="related-lookup" id="lookup_id_test" onclick="return showGenericRelatedObjectLookupPopup(document.getElementById('id_content_type'), this, '../../../');"> <img src="/static/admin/img/selector-search.gif" width="16" height="16" alt="Lookup" /></a> 161 <script type="text/javascript"> 162 var content_types = new Array(); 163 164 </script>""" 165 ) 166 167 cts = [ct.model_class() for ct in ContentType.objects.filter(pk__lt=3)] 168 self.assertEqual( 169 GenericForeignKeyRawIdWidget(models.Image._meta.virtual_fields[0].ct_field, cts).render('test', '', {}), 170 """<input type="text" name="test" class="vForeignKeyRawIdAdminField" /><a href="../../../" class="related-lookup" id="lookup_id_test" onclick="return showGenericRelatedObjectLookupPopup(document.getElementById('id_content_type'), this, '../../../');"> <img src="/static/admin/img/selector-search.gif" width="16" height="16" alt="Lookup" /></a> 171 <script type="text/javascript"> 172 var content_types = new Array(); 173 content_types[1] = '%s/%s/'; 174 content_types[2] = '%s/%s/'; 175 </script>""" % (cts[0]._meta.app_label, cts[0]._meta.object_name.lower(), 176 cts[1]._meta.app_label, cts[1]._meta.object_name.lower(),) 177 ) 178 151 179 class AdminForeignKeyRawIdWidget(DjangoTestCase): 152 180 fixtures = ["admin-widgets-users.xml"] 153 181 admin_root = '/widget_admin' -
tests/regressiontests/modeladmin/models.py
diff --git a/tests/regressiontests/modeladmin/models.py b/tests/regressiontests/modeladmin/models.py index ae180a4..e296169 100644
a b 1 1 # coding: utf-8 2 2 from django.db import models 3 3 from django.contrib.auth.models import User 4 from django.contrib.contenttypes import generic 5 from django.contrib.contenttypes.models import ContentType 4 6 5 7 class Band(models.Model): 6 8 name = models.CharField(max_length=100) … … class ValidationTestModel(models.Model): 39 41 40 42 class ValidationTestInlineModel(models.Model): 41 43 parent = models.ForeignKey(ValidationTestModel) 44 45 class Image(models.Model): 46 image = models.ImageField(upload_to='pictures') 47 description = models.TextField() 48 49 content_type = models.ForeignKey(ContentType, related_name='modeladmin_image_set') 50 object_id = models.PositiveIntegerField() 51 object = generic.GenericForeignKey('content_type', 'object_id') 52 No newline at end of file -
tests/regressiontests/modeladmin/tests.py
diff --git a/tests/regressiontests/modeladmin/tests.py b/tests/regressiontests/modeladmin/tests.py index 872fb0c..819a779 100644
a b from django.test import TestCase 16 16 from django.utils import unittest 17 17 18 18 from models import (Band, Concert, ValidationTestModel, 19 ValidationTestInlineModel )19 ValidationTestInlineModel, Image) 20 20 21 21 22 22 # None of the following tests really depend on the content of the request, … … class ValidationTests(unittest.TestCase): 1486 1486 inlines = [ValidationTestInline] 1487 1487 1488 1488 validate(ValidationTestModelAdmin, ValidationTestModel) 1489 1490 def test_generic_fields_validation(self): 1491 class GenericModelAdmin(ModelAdmin): 1492 generic_fields = None 1493 1494 self.assertRaisesRegexp( 1495 ImproperlyConfigured, 1496 "'GenericModelAdmin.generic_fields' must be a list or tuple.", 1497 validate, 1498 GenericModelAdmin, 1499 Image 1500 ) 1501 1502 class GenericModelAdmin(ModelAdmin): 1503 generic_fields = ["abc"] 1504 1505 self.assertRaisesRegexp( 1506 ImproperlyConfigured, 1507 "Item number 0 in GenericModelAdmin.generic_fieldsis not a GenericForeignKey on Image", 1508 validate, 1509 GenericModelAdmin, 1510 Image 1511 ) 1512 1513 class GenericModelAdmin(ModelAdmin): 1514 generic_fields = ["object"] 1515 1516 validate(GenericModelAdmin, Image) 1517 No newline at end of file