Ticket #17122: patch_with_tests.diff
File patch_with_tests.diff, 20.9 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_inlines/admin.py
108 108 class SottoCapoInline(admin.TabularInline): 109 109 model = SottoCapo 110 110 111 class FAdminInline(admin.TabularInline): 112 model = F 113 fk_name = 'fk1' 114 extra = 0 111 115 116 class EAdmin(admin.ModelAdmin): 117 inlines = (FAdminInline,) 118 119 class GAdmin(admin.ModelAdmin): 120 raw_id_fields = ("relation",) 121 122 class HAdmin(admin.ModelAdmin): 123 raw_id_fields = ("relation",) 124 125 admin.site.register(A) 126 admin.site.register(B) 127 admin.site.register(C) 128 admin.site.register(D) 129 admin.site.register(E,EAdmin) 130 admin.site.register(G,GAdmin) 131 admin.site.register(H,HAdmin) 132 133 112 134 site.register(TitleCollection, inlines=[TitleInline]) 113 135 # Test bug #12561 and #12778 114 136 # only ModelAdmin media -
tests/regressiontests/admin_inlines/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) 140 No newline at end of file -
tests/regressiontests/admin_inlines/models.py
5 5 from django.db import models 6 6 from django.contrib.contenttypes.models import ContentType 7 7 from django.contrib.contenttypes import generic 8 from django.core.urlresolvers import reverse 9 from .fields import UUIDField 8 10 9 11 10 12 class Parent(models.Model): … … 136 138 class SottoCapo(models.Model): 137 139 name = models.CharField(max_length=100) 138 140 capo_famiglia = models.ForeignKey(CapoFamiglia, related_name='+') 141 142 class Base(models.Model): 143 class Meta(): 144 abstract = True 145 146 binary_id = UUIDField(primary_key=True) 147 148 def __unicode__(self): 149 return u"%s.__unicode__() resulting pk: %s" % (self.__class__.__name__, self.pk) 150 151 def get_change_url(self): 152 # {{ app_label }}_{{ model_name }}_change object_id 153 viewname = "admin:%s_%s_change" % (self._meta.app_label,self.__class__.__name__) 154 return reverse(viewname.lower(), args=(self.pk,)) 155 156 class A(Base): 157 pass 158 159 class B(Base): 160 relation = models.ForeignKey(A) 161 162 class C(Base): 163 relation = models.OneToOneField(A) 164 165 class D(Base): 166 relation = models.ManyToManyField(A) 167 168 class E(Base): 169 relation = models.ManyToManyField(A, through='F') 170 171 class F(Base): 172 fk1 = models.ForeignKey(E) 173 fk2 = models.ForeignKey(A,related_name="fa_fk2") 174 m2m1 = models.ManyToManyField(A,related_name="fa_m2m1") 175 one1 = models.OneToOneField(B) 176 177 class G(Base): 178 relation = models.ForeignKey(A) 179 180 class H(Base): 181 relation = models.ManyToManyField(A) 182 No newline at end of file -
tests/regressiontests/admin_inlines/tests.py
1 1 from __future__ import absolute_import 2 from re import search,DOTALL 2 3 3 4 from django.contrib.admin.helpers import InlineAdminForm 4 5 from django.contrib.auth.models import User, Permission 5 6 from django.contrib.contenttypes.models import ContentType 7 from django.http import HttpResponse 6 8 from django.test import TestCase 9 from django.utils.encoding import force_unicode 7 10 11 8 12 # local test models 9 13 from .admin import InnerInline 10 14 from .models import (Holder, Inner, Holder2, Inner2, Holder3, Inner3, Person, 11 15 OutfitItem, Fashionista, Teacher, Parent, Child, Author, Book) 12 16 17 from .models import A,B,C,D,E,F,G,H 13 18 19 14 20 class TestInline(TestCase): 15 21 urls = "regressiontests.admin_inlines.urls" 16 22 fixtures = ['admin-views-users.xml'] … … 380 386 self.assertContains(response, 'value="4" id="id_inner2_set-TOTAL_FORMS"') 381 387 self.assertContains(response, '<input type="hidden" name="inner2_set-0-id" value="%i"' % self.inner2_id) 382 388 self.assertContains(response, 'id="id_inner2_set-0-DELETE"') 389 390 class TestsFor17122(TestCase): 391 def setUp(self): 392 # create the admin user 393 user = User() 394 user.username = "admin" 395 user.set_password("admin") 396 user.is_staff = True 397 user.is_superuser = True 398 user.save() 399 400 # log in 401 result = self.client.login(username="admin", password="admin") 402 self.assertEqual(result, True) 403 404 # create instances to work with 405 a1 = A() 406 a2 = A() 407 a3 = A() 408 a1.save() 409 a2.save() 410 a3.save() 411 self.a1 = a1 412 self.a2 = a2 413 self.a3 = a3 414 415 # create foreign keys to test 416 b1 = B(relation=a1) 417 b2 = B(relation=a2) 418 b3 = B(relation=a3) 419 b1.save() 420 b2.save() 421 b3.save() 422 self.b1 = b1 423 self.b2 = b2 424 self.b3 = b3 425 426 # create one to ones for testing 427 c1 = C(relation=a1) 428 c1.save() 429 self.c1 = c1 430 431 # create many to manys for testing 432 d1 = D() 433 d1.save() 434 d1.relation.add(a1) 435 d1.relation.add(a2) 436 d1.save() 437 self.d1 = d1 438 439 # create relation for inline 440 e1 = E() 441 e1.save() 442 self.e1 = e1 443 444 # create inline 445 f1 = F() 446 f1.fk1 = self.e1 447 f1.fk2 = self.a1 448 f1.one1 = self.b1 449 f1.save() 450 f1.m2m1.add(self.a1) 451 f1.m2m1.add(self.a2) 452 f1.save() 453 self.f1 = f1 454 455 # query the admin 456 response = self.client.get(self.e1.get_change_url()) 457 458 # If the response supports deferred rendering and hasn't been rendered 459 # yet, then ensure that it does get rendered before proceeding further. 460 if (hasattr(response, 'render') and callable(response.render) 461 and not response.is_rendered): 462 response.render() 463 content = response.content 464 465 # create a dict for the inline parse 466 results = {} 467 468 # parse inline response content 469 for field_name in ['fk2','one1','m2m1']: 470 result = search( 471 r'<select.*?name="f_set-0-%s".*?>(?P<%s>.*?)</select>'%( 472 field_name, 473 field_name 474 ), 475 content, 476 DOTALL 477 ) 478 if result is not None and len(result.groups(field_name)) == 1: 479 results[field_name] = result.groups(field_name)[0] 480 else: 481 results[field_name] = "" 482 483 # store for tests 484 self.inline_results = results 485 self.inline_content = content 486 self.inline_response = response 487 488 # create foreign keys to test raw id widget 489 g1 = G(relation=a1) 490 g1.save() 491 self.g1 = g1 492 493 # create m2m to test raw id widget 494 h1 = H() 495 h1.save() 496 h1.relation.add(a1) 497 h1.relation.add(a2) 498 h1.save() 499 self.h1 = h1 500 501 def tearDown(self): 502 self.client.logout() 503 504 def test_ForeignKey_render(self): 505 response = self.client.get(self.b1.get_change_url()) 506 self.assertContains(response, 507 '<option value="%s" selected="selected">%s</option>' % ( 508 self.b1.relation.pk, 509 self.b1.relation 510 ) 511 ) 512 for a in A.objects.all().exclude(pk=self.b1.relation.pk): 513 self.assertContains(response, 514 '<option value="%s">%s</option>' % ( 515 a.pk, 516 a 517 ) 518 ) 519 520 def test_OneToOneField_render(self): 521 response = self.client.get(self.c1.get_change_url()) 522 self.assertContains(response, 523 '<option value="%s" selected="selected">%s</option>' % ( 524 self.c1.relation.pk, 525 self.c1.relation 526 ) 527 ) 528 for a in A.objects.all().exclude(pk=self.c1.relation.pk): 529 self.assertContains(response, 530 '<option value="%s">%s</option>' % ( 531 a.pk, 532 a 533 ) 534 ) 535 536 def test_ManyToManyField_render(self): 537 response = self.client.get(self.d1.get_change_url()) 538 others = A.objects.all() 539 for a in self.d1.relation.all(): 540 self.assertContains(response, 541 '<option value="%s" selected="selected">%s</option>' % ( 542 a.pk, 543 a 544 ) 545 ) 546 others = others.exclude(pk=a.pk) 547 for a in others: 548 self.assertContains(response, 549 '<option value="%s">%s</option>' % ( 550 a.pk, 551 a 552 ) 553 ) 554 555 def test_inline_hidden_input(self): 556 text = '<input type="hidden" name="f_set-__prefix__-fk1" value="%s" id="id_f_set-__prefix__-fk1" />' % self.f1.fk1.pk 557 self.assertContains(self.inline_response, text) 558 559 def test_inline_ForeignKey_render(self): 560 response = HttpResponse(self.inline_results["fk2"]) 561 self.assertContains(response, 562 '<option value="%s" selected="selected">%s</option>' % ( 563 self.f1.fk2.pk, 564 self.f1.fk2 565 ) 566 ) 567 for a in A.objects.all().exclude(pk=self.f1.fk2.pk): 568 self.assertContains(response, 569 '<option value="%s">%s</option>' % ( 570 a.pk, 571 a 572 ) 573 ) 574 575 def test_inline_OneToOneField_render(self): 576 response = HttpResponse(self.inline_results["one1"]) 577 self.assertContains(response, 578 '<option value="%s" selected="selected">%s</option>' % ( 579 self.f1.one1.pk, 580 self.f1.one1 581 ) 582 ) 583 for b in B.objects.all().exclude(pk=self.f1.one1.pk): 584 self.assertContains(response, 585 '<option value="%s">%s</option>' % ( 586 b.pk, 587 b 588 ) 589 ) 590 591 def test_inline_ManyToManyField_render(self): 592 response = HttpResponse(self.inline_results["m2m1"]) 593 others = A.objects.all() 594 for a in self.f1.m2m1.all(): 595 self.assertContains(response, 596 '<option value="%s" selected="selected">%s</option>' % ( 597 a.pk, 598 a 599 ) 600 ) 601 others = others.exclude(pk=a.pk) 602 for a in others: 603 self.assertContains(response, 604 '<option value="%s">%s</option>' % ( 605 a.pk, 606 a 607 ) 608 ) 609 610 def test_ForeignKeyRawIdWidget_render(self): 611 response = self.client.get(self.g1.get_change_url()) 612 result = search( 613 r'<input.*?(name="relation".*?value="%s"|value="%s".*?name="relation").*?>'%( 614 self.g1.relation.pk, 615 self.g1.relation.pk, 616 ), 617 str(response), 618 DOTALL 619 ) 620 self.assertTrue(result,"ForeignKeyRawIdWidget failed with non-unicode pk.") 621 622 def test_ManyToManyRawIdWidget_render(self): 623 response = self.client.get(self.h1.get_change_url()) 624 result = search( 625 r'<input.*?(?:name="relation".*?value="(?P<value1>[a-zA-Z0-9,-]*?)"|value="(?P<value2>[a-zA-Z0-9,-]*?)".*?name="relation").*?>', 626 str(response), 627 DOTALL 628 ) 629 if result.group("value1"): 630 value = result.group("value1") 631 elif result.group("value2"): 632 value = result.group("value2") 633 else: 634 value = "" 635 observed_pks = set([force_unicode(pk) for pk in value.split(",")]) 636 relation_pks = set([force_unicode(h.pk) for h in self.h1.relation.all()]) 637 msg = "ManyToManyRawIdWidget did not render properly." 638 if hasattr(self,"longMessage") and not self.longMessage: 639 msg = "%s Enable longMessage to see the difference between rendered pks and stored pks." % msg 640 if hasattr(self,"assertSetEqual"): 641 self.assertSetEqual(observed_pks, relation_pks, msg) 642 else: 643 diff1 = observed_pks.difference(relation_pks) 644 diff2 = relation_pks.difference(observed_pks) 645 if diff1 or diff2: 646 self.fail(msg) 647 No newline at end of file