Projet

Général

Profil

0001-natural_key-fix-get_by_natural_key_json-for-objects-.patch

Benjamin Dauvergne, 17 mai 2018 15:26

Télécharger (6,87 ko)

Voir les différences:

Subject: [PATCH] natural_key: fix get_by_natural_key_json for objects with
 partial unique indexes (fixes #23857)

Unicity on NULL column must be explicitely stated in the list of natural
keys.
 src/authentic2/a2_rbac/models.py | 13 ++++++--
 src/authentic2/natural_key.py    | 57 ++++++++++++++++++++------------
 tests/test_natural_key.py        | 18 +++++++---
 3 files changed, 61 insertions(+), 27 deletions(-)
src/authentic2/a2_rbac/models.py
115 115
                                   object_id_field='admin_scope_id')
116 116

  
117 117

  
118
Permission._meta.natural_key = ['operation', 'ou', 'target']
118
Permission._meta.natural_key = [
119
    ['operation', 'ou', 'target'],
120
    ['operation', 'ou__isnull', 'target'],
121
]
119 122

  
120 123

  
121 124
class Role(RoleAbstractBase):
......
247 250

  
248 251

  
249 252
Role._meta.natural_key = [
250
    ['uuid'], ['slug', 'ou'], ['name', 'ou'], ['slug', 'service'], ['name', 'service']
253
    ['uuid'],
254
    ['slug', 'ou__isnull', 'service__isnull'],
255
    ['name', 'ou__isnull', 'service__isnull'],
256
    ['slug', 'ou', 'service'],
257
    ['name', 'ou', 'service'],
258
    ['slug', 'ou', 'service__isnull'],
259
    ['name', 'ou', 'service__isnull'],
251 260
]
252 261

  
253 262

  
src/authentic2/natural_key.py
24 24
            names.add(key)
25 25

  
26 26
    for name in names:
27
        if name.endswith('__isnull'):
28
            name = name.split('__isnull')[0]
27 29
        field = self._meta.get_field(name)
28 30
        if not (field.concrete or isinstance(field, GenericForeignKey)):
29 31
            raise ValueError('field %s is not concrete' % name)
......
49 51
    for natural_key in natural_keys:
50 52
        get_kwargs = {}
51 53
        for name in natural_key:
54
            isnull = name.endswith('__isnull')
55
            if isnull:
56
                name = name.split('__isnull')[0]
52 57
            field = model._meta.get_field(name)
53 58
            if not (field.concrete or isinstance(field, GenericForeignKey)):
54 59
                raise ValueError('field %s is not concrete' % name)
......
57 62
            try:
58 63
                value = d[name]
59 64
            except KeyError:
60
                break
61
            if isinstance(field, GenericForeignKey):
62
                try:
63
                    ct_nk = d[field.ct_field]
64
                except KeyError:
65
                    break
66
                try:
67
                    ct = ContentType.objects.get_by_natural_key_json(ct_nk)
68
                except ContentType.DoesNotExist:
65
                if not isnull:
69 66
                    break
70
                related_model = ct.model_class()
71
                try:
72
                    value = related_model._default_manager.get_by_natural_key_json(value)
73
                except related_model.DoesNotExist:
67
                value = None
68
            if isinstance(field, GenericForeignKey):
69
                if isnull and value:
74 70
                    break
75
                get_kwargs[field.ct_field] = ct
76
                name = field.fk_field
77
                value = value.pk
71
                elif not isnull:
72
                    if not value:
73
                        break
74
                    try:
75
                        ct_nk = d[field.ct_field]
76
                    except KeyError:
77
                        break
78
                    try:
79
                        ct = ContentType.objects.get_by_natural_key_json(ct_nk)
80
                    except ContentType.DoesNotExist:
81
                        break
82
                    related_model = ct.model_class()
83
                    try:
84
                        value = related_model._default_manager.get_by_natural_key_json(value)
85
                    except related_model.DoesNotExist:
86
                        break
87
                    name = field.fk_field
88
                    value = value.pk
78 89
            elif field.is_relation:
79
                if value is None:
80
                    name = '%s__isnull' % name
81
                    value = True
82
                else:
90
                if isnull and value:
91
                    break
92
                elif not isnull:
93
                    if not value:
94
                        break
83 95
                    try:
84 96
                        value = field.related_model._default_manager.get_by_natural_key_json(value)
85 97
                    except field.related_model.DoesNotExist:
86 98
                        break
87
            get_kwargs[name] = value
99
            if isnull:
100
                get_kwargs[name + '__isnull'] = True
101
            else:
102
                get_kwargs[name] = value
88 103
        else:
89 104
            try:
90 105
                return self.get(**get_kwargs)
tests/test_natural_key.py
1
import pytest
2

  
1 3
from django.contrib.contenttypes.models import ContentType
2 4
from authentic2.a2_rbac.models import Role, OrganizationalUnit as OU, Permission
3 5

  
......
29 31
        }
30 32
        assert role == Role.objects.get_by_natural_key_json(nk)
31 33
        assert role == Role.objects.get_by_natural_key_json({'uuid': role.uuid})
32
        assert role == Role.objects.get_by_natural_key_json({'slug': role.slug, 'ou': ou_nk})
33
        assert role == Role.objects.get_by_natural_key_json({'name': role.name, 'ou': ou_nk})
34
        if service_nk:
35
            with pytest.raises(Role.DoesNotExist):
36
                Role.objects.get_by_natural_key_json({'slug': role.slug, 'ou': ou_nk})
37
        else:
38
            assert Role.objects.get_by_natural_key_json({'slug': role.slug, 'ou': ou_nk}) == role
39
        if service_nk:
40
            with pytest.raises(Role.DoesNotExist):
41
                assert Role.objects.get_by_natural_key_json({'name': role.name, 'ou': ou_nk})
42
        else:
43
            assert Role.objects.get_by_natural_key_json({'name': role.name, 'ou': ou_nk}) == role
34 44
        assert role == Role.objects.get_by_natural_key_json(
35
            {'slug': role.slug, 'service': service_nk})
45
            {'slug': role.slug, 'ou': ou_nk, 'service': service_nk})
36 46
        assert role == Role.objects.get_by_natural_key_json(
37
            {'name': role.name, 'service': service_nk})
47
            {'name': role.name, 'ou': ou_nk, 'service': service_nk})
38 48

  
39 49
    for permission in Permission.objects.all():
40 50
        ou_nk = permission.ou and permission.ou.natural_key_json()
41
-