Ticket #17122: TRUNK_patch_with_widget_tests.diff

File TRUNK_patch_with_widget_tests.diff, 16.8 KB (added by nickname123, 3 years ago)

Run with python ./tests/runtests.py --settings=test_sqlite admin_widgets.TestsFor17122

  • django/contrib/admin/widgets.py

     
    130130
    131131    def render(self, name, value, attrs=None):
    132132        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'
    133140        if attrs is None:
    134141            attrs = {}
    135142        extra = []
     
    139146                                    (rel_to._meta.app_label,
    140147                                    rel_to._meta.module_name),
    141148                                    current_app=self.admin_site.name)
    142 
     149       
    143150            params = self.url_parameters()
    144151            if params:
    145152                url = u'?' + u'&'.join([u'%s=%s' % (k, v) for k, v in params.items()])
     
    248255
    249256    def render(self, name, value, *args, **kwargs):
    250257        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'
    251265        info = (rel_to._meta.app_label, rel_to._meta.object_name.lower())
    252266        self.widget.choices = self.choices
    253267        output = [self.widget.render(name, value, *args, **kwargs)]
  • django/forms/forms.py

     
    341341                    hidden_widget = field.hidden_widget()
    342342                    initial_value = hidden_widget.value_from_datadict(
    343343                        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)
    344352                if field.widget._has_changed(initial_value, data_value):
    345353                    self._changed_data.append(name)
    346354        return self._changed_data
  • tests/regressiontests/admin_widgets/fields.py

     
     1from django.db import models
     2import uuid
     3from sqlite3 import Binary
     4
     5class UUIDVersionError(Exception):
     6    pass
     7
     8class 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_widgets/models.py

     
    11from django.db import models
    22from django.contrib.auth.models import User
     3from django.core.urlresolvers import reverse
     4from fields import UUIDField
    35
    46
    57class MyFileField(models.FileField):
     
    99101    """
    100102    name = models.CharField(max_length=20)
    101103    companies = models.ManyToManyField(Company)
     104
     105class 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   
     120class A(Base):
     121    pass
     122
     123class B(Base):
     124    relation = models.ForeignKey(A)
     125
     126class C(Base):
     127    relation = models.OneToOneField(A)
     128   
     129class D(Base):
     130    relation = models.ManyToManyField(A)
     131
     132class G(Base):
     133    relation = models.ForeignKey(A)
     134   
     135class H(Base):
     136    relation = models.ManyToManyField(A)
     137 No newline at end of file
  • tests/regressiontests/admin_widgets/tests.py

     
    77from django.conf import settings
    88from django.contrib import admin
    99from django.contrib.admin import widgets
     10from django.contrib.auth.models import User
    1011from django.core.files.storage import default_storage
    1112from django.core.files.uploadedfile import SimpleUploadedFile
    1213from django.db.models import DateField
     14from django.http import HttpResponse
    1315from django.test import TestCase as DjangoTestCase
    1416from django.utils import translation
     17from django.utils.encoding import force_unicode
    1518from django.utils.html import conditional_escape
    1619from django.utils.unittest import TestCase
    1720
    1821from . import models
    1922from .widgetadmin import site as widget_admin_site
    2023
     24from re import search,DOTALL
    2125
     26from .models import A,B,C,D,G,H
     27
     28
    2229admin_media_prefix = lambda: {
    2330    'ADMIN_MEDIA_PREFIX': "%sadmin/" % settings.STATIC_URL,
    2431}
     
    372379        # Used to fail with a name error.
    373380        w = widgets.RelatedFieldWidgetWrapper(w, rel, widget_admin_site)
    374381        self.assertFalse(w.can_add_related)
     382
     383class 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)
  • tests/regressiontests/admin_widgets/widgetadmin.py

     
    2525class EventAdmin(admin.ModelAdmin):
    2626    raw_id_fields = ['band']
    2727
     28class GAdmin(admin.ModelAdmin):
     29    raw_id_fields = ("relation",)
     30   
     31class HAdmin(admin.ModelAdmin):
     32    raw_id_fields = ("relation",)
     33
     34
    2835site = WidgetAdmin(name='widget-admin')
    2936
    3037site.register(models.User)
     
    4148site.register(models.Bee)
    4249
    4350site.register(models.Advisor)
     51
     52
     53site.register(models.A)
     54site.register(models.B)
     55site.register(models.C)
     56site.register(models.D)
     57site.register(models.G,GAdmin)
     58site.register(models.H,HAdmin)
     59 No newline at end of file
Back to Top