Ticket #17122: patch_with_tests.diff

File patch_with_tests.diff, 20.9 KB (added by nickname123, 12 years ago)

merged tests and patch to same diff. Run with python ./tests/runtests.py --settings=test_sqlite admin_inlines.TestInline

  • 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_inlines/admin.py

     
    108108class SottoCapoInline(admin.TabularInline):
    109109    model = SottoCapo
    110110
     111class FAdminInline(admin.TabularInline):
     112    model = F
     113    fk_name = 'fk1'
     114    extra = 0
    111115
     116class EAdmin(admin.ModelAdmin):
     117    inlines = (FAdminInline,)
     118   
     119class GAdmin(admin.ModelAdmin):
     120    raw_id_fields = ("relation",)
     121   
     122class HAdmin(admin.ModelAdmin):
     123    raw_id_fields = ("relation",)
     124
     125admin.site.register(A)
     126admin.site.register(B)
     127admin.site.register(C)
     128admin.site.register(D)
     129admin.site.register(E,EAdmin)
     130admin.site.register(G,GAdmin)
     131admin.site.register(H,HAdmin)
     132
     133
    112134site.register(TitleCollection, inlines=[TitleInline])
    113135# Test bug #12561 and #12778
    114136# only ModelAdmin media
  • tests/regressiontests/admin_inlines/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_inlines/models.py

     
    55from django.db import models
    66from django.contrib.contenttypes.models import ContentType
    77from django.contrib.contenttypes import generic
     8from django.core.urlresolvers import reverse
     9from .fields import UUIDField
    810
    911
    1012class Parent(models.Model):
     
    136138class SottoCapo(models.Model):
    137139    name = models.CharField(max_length=100)
    138140    capo_famiglia = models.ForeignKey(CapoFamiglia, related_name='+')
     141
     142class 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   
     156class A(Base):
     157    pass
     158
     159class B(Base):
     160    relation = models.ForeignKey(A)
     161
     162class C(Base):
     163    relation = models.OneToOneField(A)
     164   
     165class D(Base):
     166    relation = models.ManyToManyField(A)
     167
     168class E(Base):
     169    relation = models.ManyToManyField(A, through='F')
     170
     171class 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
     177class G(Base):
     178    relation = models.ForeignKey(A)
     179   
     180class H(Base):
     181    relation = models.ManyToManyField(A)
     182 No newline at end of file
  • tests/regressiontests/admin_inlines/tests.py

     
    11from __future__ import absolute_import
     2from re import search,DOTALL
    23
    34from django.contrib.admin.helpers import InlineAdminForm
    45from django.contrib.auth.models import User, Permission
    56from django.contrib.contenttypes.models import ContentType
     7from django.http import HttpResponse
    68from django.test import TestCase
     9from django.utils.encoding import force_unicode
    710
     11
    812# local test models
    913from .admin import InnerInline
    1014from .models import (Holder, Inner, Holder2, Inner2, Holder3, Inner3, Person,
    1115    OutfitItem, Fashionista, Teacher, Parent, Child, Author, Book)
    1216
     17from .models import A,B,C,D,E,F,G,H
    1318
     19
    1420class TestInline(TestCase):
    1521    urls = "regressiontests.admin_inlines.urls"
    1622    fixtures = ['admin-views-users.xml']
     
    380386        self.assertContains(response, 'value="4" id="id_inner2_set-TOTAL_FORMS"')
    381387        self.assertContains(response, '<input type="hidden" name="inner2_set-0-id" value="%i"' % self.inner2_id)
    382388        self.assertContains(response, 'id="id_inner2_set-0-DELETE"')
     389
     390class 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
Back to Top