Ticket #17122: TRUNK_patch_with_widget_tests_to_render_and_update.diff
File TRUNK_patch_with_widget_tests_to_render_and_update.diff, 18.4 KB (added by , 13 years ago) |
---|
-
django/contrib/admin/widgets.py
130 130 131 131 def render(self, name, value, attrs=None): 132 132 rel_to = self.rel.to 133 # custom fields may need the to_python 134 # conversion in order to facilitate 135 # unicode conversion 136 try: 137 value = self.rel.to._meta.get_field(self.rel.field_name).to_python(value) 138 except AttributeError: 139 pass# 'ManyToManyRel' object has no attribute 'field_name' 133 140 if attrs is None: 134 141 attrs = {} 135 142 extra = [] … … 139 146 (rel_to._meta.app_label, 140 147 rel_to._meta.module_name), 141 148 current_app=self.admin_site.name) 142 149 143 150 params = self.url_parameters() 144 151 if params: 145 152 url = u'?' + u'&'.join([u'%s=%s' % (k, v) for k, v in params.items()]) … … 248 255 249 256 def render(self, name, value, *args, **kwargs): 250 257 rel_to = self.rel.to 258 # custom fields may need the to_python 259 # conversion in order to facilitate 260 # unicode conversion 261 try: 262 value = self.rel.to._meta.get_field(self.rel.field_name).to_python(value) 263 except AttributeError: 264 pass# 'ManyToManyRel' object has no attribute 'field_name' 251 265 info = (rel_to._meta.app_label, rel_to._meta.object_name.lower()) 252 266 self.widget.choices = self.choices 253 267 output = [self.widget.render(name, value, *args, **kwargs)] -
django/forms/forms.py
341 341 hidden_widget = field.hidden_widget() 342 342 initial_value = hidden_widget.value_from_datadict( 343 343 self.data, self.files, initial_prefixed_name) 344 # custom fields may need the to_python 345 # conversion in order to facilitate 346 # unicode conversion 347 if isinstance(initial_value,list): 348 # ManyToManyField uses a list 349 initial_value = [field.to_python(v) for v in initial_value] 350 else: 351 initial_value = field.to_python(initial_value) 344 352 if field.widget._has_changed(initial_value, data_value): 345 353 self._changed_data.append(name) 346 354 return self._changed_data -
tests/regressiontests/admin_widgets/fields.py
1 from django.db import models 2 import uuid 3 from sqlite3 import Binary 4 5 class UUIDVersionError(Exception): 6 pass 7 8 class UUIDField(models.Field): 9 10 __metaclass__ = models.SubfieldBase 11 empty_strings_allowed = False 12 13 def __init__(self, primary_key=False, verbose_name=None, name=None, auto=True, version=1, node=None, clock_seq=None, namespace=None, **kwargs): 14 if primary_key and not auto: 15 auto = True 16 if auto: 17 kwargs['blank'] = True 18 kwargs.setdefault('editable', False) 19 self.auto = auto 20 self.version = version 21 if version == 1: 22 self.node, self.clock_seq = node, clock_seq 23 elif version == 3 or version == 5: 24 self.namespace, self.name = namespace, name 25 kwargs['max_length'] = 36 26 super(UUIDField,self).__init__(verbose_name, name, primary_key, **kwargs) 27 28 def contribute_to_class(self, cls, name): 29 if self.primary_key: 30 assert not cls._meta.has_auto_field, \ 31 "A model can't have more than one AutoField: %s %s %s; have %s" % \ 32 (self, cls, name, cls._meta.auto_field) 33 super(UUIDField, self).contribute_to_class(cls, name) 34 cls._meta.has_auto_field = True 35 cls._meta.auto_field = self 36 else: 37 super(UUIDField, self).contribute_to_class(cls, name) 38 39 def create_uuid(self): 40 if not self.version or self.version == 4: 41 return uuid.uuid4() 42 elif self.version == 1: 43 return uuid.uuid1(self.node, self.clock_seq) 44 elif self.version == 2: 45 raise UUIDVersionError("UUID version 2 is not supported.") 46 elif self.version == 3: 47 return uuid.uuid3(self.namespace, self.name) 48 elif self.version == 5: 49 return uuid.uuid5(self.namespace, self.name) 50 else: 51 raise UUIDVersionError("UUID version %s is not valid." % self.version) 52 53 def db_type(self, connection): 54 """ 55 Returns the database column data type for the Field, 56 taking into account the connection object, and the 57 settings associated with it. 58 """ 59 return 'Binary(16)' 60 61 def to_python(self, value): 62 """ 63 Converts a value as returned by your database 64 (or a serializer) to a Python object. 65 """ 66 if isinstance(value,models.Model): 67 # This happens with related fields 68 # when the relation uses a UUIDField 69 # as the primary key. 70 value = value.pk 71 if isinstance(value,buffer): 72 value = "%s" % value 73 if value is None: 74 value = '' 75 if isinstance(value,basestring) and not value: 76 pass 77 elif not isinstance(value,uuid.UUID): 78 try: 79 value = uuid.UUID(value) 80 except ValueError: 81 value = uuid.UUID(bytes=value) 82 return unicode(value) 83 84 def get_prep_value(self, value): 85 """ 86 New in Django 1.2 87 This is the reverse of to_python() when working with 88 the database backends (as opposed to serialization). 89 """ 90 if isinstance(value,buffer): 91 value = "%s" % value 92 if value is None: 93 value = '' 94 if isinstance(value,basestring) and not value: 95 pass 96 elif not isinstance(value,uuid.UUID): 97 try: 98 value = uuid.UUID(value).bytes 99 except ValueError: 100 value = uuid.UUID(bytes=value).bytes 101 return value 102 103 def get_db_prep_value(self, value, connection, prepared=False): 104 value = super(UUIDField,self).get_db_prep_value(value, connection=connection, prepared=prepared) 105 if connection.settings_dict['ENGINE'] == 'django.db.backends.sqlite3': 106 value = Binary(value) 107 return value 108 109 def pre_save(self, model_instance, add): 110 """ 111 This method is called just prior to get_db_prep_save() 112 and should return the value of the appropriate attribute 113 from model_instance for this field. If the model is 114 being saved to the database for the first time, the add 115 parameter will be True, otherwise it will be False. 116 """ 117 if self.auto and add: 118 value = unicode(self.create_uuid()) 119 setattr(model_instance, self.attname, value) 120 else: 121 value = super(UUIDField, self).pre_save(model_instance, add) 122 if self.auto and not value: 123 value = unicode(self.create_uuid()) 124 setattr(model_instance, self.attname, value) 125 return value 126 127 def formfield(self, **kwargs): 128 if self.primary_key: 129 return None 130 else: 131 return super(UUIDField,self).formfield(**kwargs) 132 133 def value_to_string(self, obj): 134 """ 135 This method is used by the serializers to convert the 136 field into a string for output. 137 """ 138 value = self._get_val_from_obj(obj) 139 return self.to_python(value) -
tests/regressiontests/admin_widgets/models.py
1 1 from django.db import models 2 2 from django.contrib.auth.models import User 3 from django.core.urlresolvers import reverse 4 from fields import UUIDField 3 5 4 6 5 7 class MyFileField(models.FileField): … … 99 101 """ 100 102 name = models.CharField(max_length=20) 101 103 companies = models.ManyToManyField(Company) 104 105 class Base(models.Model): 106 class Meta(): 107 abstract = True 108 109 binary_id = UUIDField(primary_key=True) 110 111 def __unicode__(self): 112 return u"%s.__unicode__() resulting pk: %s" % (self.__class__.__name__, self.pk) 113 114 def get_change_url(self): 115 return "/widget_admin/admin_widgets/%s/%s/" % ( 116 self.__class__.__name__.lower(), 117 self.pk 118 ) 119 120 class A(Base): 121 pass 122 123 class B(Base): 124 relation = models.ForeignKey(A) 125 126 class C(Base): 127 relation = models.OneToOneField(A) 128 129 class D(Base): 130 relation = models.ManyToManyField(A) 131 132 class G(Base): 133 relation = models.ForeignKey(A) 134 135 class H(Base): 136 relation = models.ManyToManyField(A) -
tests/regressiontests/admin_widgets/tests.py
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.auth.models import User 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 14 from django.http import HttpResponse 13 15 from django.test import TestCase as DjangoTestCase 14 16 from django.utils import translation 17 from django.utils.encoding import force_unicode 15 18 from django.utils.html import conditional_escape 16 19 from django.utils.unittest import TestCase 17 20 18 21 from . import models 19 22 from .widgetadmin import site as widget_admin_site 20 23 24 from re import search,DOTALL 21 25 26 from .models import A,B,C,D,G,H 27 28 22 29 admin_media_prefix = lambda: { 23 30 'ADMIN_MEDIA_PREFIX': "%sadmin/" % settings.STATIC_URL, 24 31 } … … 372 379 # Used to fail with a name error. 373 380 w = widgets.RelatedFieldWidgetWrapper(w, rel, widget_admin_site) 374 381 self.assertFalse(w.can_add_related) 382 383 class TestsFor17122(DjangoTestCase): 384 def setUp(self): 385 # create the admin user 386 user = User() 387 user.username = "admin" 388 user.set_password("admin") 389 user.is_staff = True 390 user.is_superuser = True 391 user.save() 392 393 # log in 394 result = self.client.login(username="admin", password="admin") 395 self.assertEqual(result, True) 396 397 # create instances to work with 398 a1 = A() 399 a2 = A() 400 a3 = A() 401 a1.save() 402 a2.save() 403 a3.save() 404 self.a1 = a1 405 self.a2 = a2 406 self.a3 = a3 407 408 # create foreign keys to test 409 b1 = B(relation=a1) 410 b2 = B(relation=a2) 411 b3 = B(relation=a3) 412 b1.save() 413 b2.save() 414 b3.save() 415 self.b1 = b1 416 self.b2 = b2 417 self.b3 = b3 418 419 # create one to ones for testing 420 c1 = C(relation=a1) 421 c1.save() 422 self.c1 = c1 423 424 # create many to manys for testing 425 d1 = D() 426 d1.save() 427 d1.relation.add(a1) 428 d1.relation.add(a2) 429 d1.save() 430 self.d1 = d1 431 432 # create foreign keys to test raw id widget 433 g1 = G(relation=a1) 434 g1.save() 435 self.g1 = g1 436 437 # create m2m to test raw id widget 438 h1 = H() 439 h1.save() 440 h1.relation.add(a1) 441 h1.relation.add(a2) 442 h1.save() 443 self.h1 = h1 444 445 def tearDown(self): 446 self.client.logout() 447 448 def test_ForeignKey_render(self): 449 response = self.client.get(self.b1.get_change_url()) 450 self.assertContains(response, 451 '<option value="%s" selected="selected">%s</option>' % ( 452 self.b1.relation.pk, 453 self.b1.relation 454 ) 455 ) 456 for a in A.objects.all().exclude(pk=self.b1.relation.pk): 457 self.assertContains(response, 458 '<option value="%s">%s</option>' % ( 459 a.pk, 460 a 461 ) 462 ) 463 464 def test_OneToOneField_render(self): 465 response = self.client.get(self.c1.get_change_url()) 466 self.assertContains(response, 467 '<option value="%s" selected="selected">%s</option>' % ( 468 self.c1.relation.pk, 469 self.c1.relation 470 ) 471 ) 472 for a in A.objects.all().exclude(pk=self.c1.relation.pk): 473 self.assertContains(response, 474 '<option value="%s">%s</option>' % ( 475 a.pk, 476 a 477 ) 478 ) 479 480 def test_ManyToManyField_render(self): 481 response = self.client.get(self.d1.get_change_url()) 482 others = A.objects.all() 483 for a in self.d1.relation.all(): 484 self.assertContains(response, 485 '<option value="%s" selected="selected">%s</option>' % ( 486 a.pk, 487 a 488 ) 489 ) 490 others = others.exclude(pk=a.pk) 491 for a in others: 492 self.assertContains(response, 493 '<option value="%s">%s</option>' % ( 494 a.pk, 495 a 496 ) 497 ) 498 499 def test_ForeignKeyRawIdWidget_render(self): 500 response = self.client.get(self.g1.get_change_url()) 501 result = search( 502 r'<input.*?(name="relation".*?value="%s"|value="%s".*?name="relation").*?>'%( 503 self.g1.relation.pk, 504 self.g1.relation.pk, 505 ), 506 str(response), 507 DOTALL 508 ) 509 self.assertTrue(result,"ForeignKeyRawIdWidget failed with non-unicode pk.") 510 511 def test_ManyToManyRawIdWidget_render(self): 512 response = self.client.get(self.h1.get_change_url()) 513 result = search( 514 r'<input.*?(?:name="relation".*?value="(?P<value1>[a-zA-Z0-9,-]*?)"|value="(?P<value2>[a-zA-Z0-9,-]*?)".*?name="relation").*?>', 515 str(response), 516 DOTALL 517 ) 518 if result.group("value1"): 519 value = result.group("value1") 520 elif result.group("value2"): 521 value = result.group("value2") 522 else: 523 value = "" 524 observed_pks = set([force_unicode(pk) for pk in value.split(",")]) 525 relation_pks = set([force_unicode(h.pk) for h in self.h1.relation.all()]) 526 msg = "ManyToManyRawIdWidget did not render properly." 527 if hasattr(self,"longMessage") and not self.longMessage: 528 msg = "%s Enable longMessage to see the difference between rendered pks and stored pks." % msg 529 if hasattr(self,"assertSetEqual"): 530 self.assertSetEqual(observed_pks, relation_pks, msg) 531 else: 532 diff1 = observed_pks.difference(relation_pks) 533 diff2 = relation_pks.difference(observed_pks) 534 if diff1 or diff2: 535 self.fail(msg) 536 537 def test_ForeignKey_update(self): 538 b = B() 539 b.relation = self.a1 540 b.save() 541 data = {} 542 data['relation'] = self.a3.pk 543 response = self.client.post(b.get_change_url(),data) 544 self.assertRedirects(response, "/widget_admin/admin_widgets/b/") 545 546 def test_OneToOneField_update(self): 547 a1 = A() 548 a1.save() 549 a2 = A() 550 a2.save() 551 c = C() 552 c.relation = a1 553 c.save() 554 data = {} 555 data['relation'] = a2.pk 556 response = self.client.post(c.get_change_url(),data) 557 self.assertRedirects(response, "/widget_admin/admin_widgets/c/") 558 559 def test_ManyToManyField_update(self): 560 d = D() 561 d.save() 562 d.relation.add(self.a1) 563 d.relation.add(self.a2) 564 d.save() 565 data = {} 566 data['relation'] = self.a2.pk 567 response = self.client.post(d.get_change_url(),data) 568 self.assertRedirects(response, "/widget_admin/admin_widgets/d/") 569 570 def test_ForeignKeyRawIdWidget_update(self): 571 g = G() 572 g.relation = self.a1 573 g.save() 574 data = {} 575 data['relation'] = self.a3.pk 576 response = self.client.post(g.get_change_url(),data) 577 self.assertRedirects(response, "/widget_admin/admin_widgets/g/") 578 579 def test_ManyToManyRawIdWidget_update(self): 580 h = H() 581 h.save() 582 h.relation.add(self.a1) 583 h.relation.add(self.a2) 584 h.save() 585 data = {} 586 data['relation'] = self.a2.pk 587 response = self.client.post(h.get_change_url(),data) 588 self.assertRedirects(response, "/widget_admin/admin_widgets/h/") 589 No newline at end of file -
tests/regressiontests/admin_widgets/widgetadmin.py
25 25 class EventAdmin(admin.ModelAdmin): 26 26 raw_id_fields = ['band'] 27 27 28 class GAdmin(admin.ModelAdmin): 29 raw_id_fields = ("relation",) 30 31 class HAdmin(admin.ModelAdmin): 32 raw_id_fields = ("relation",) 33 34 28 35 site = WidgetAdmin(name='widget-admin') 29 36 30 37 site.register(models.User) … … 41 48 site.register(models.Bee) 42 49 43 50 site.register(models.Advisor) 51 52 53 site.register(models.A) 54 site.register(models.B) 55 site.register(models.C) 56 site.register(models.D) 57 site.register(models.G,GAdmin) 58 site.register(models.H,HAdmin)