Project

General

Profile

0001-Revert-add-compatibility-layer-for-support-of-Django.patch

Paul Marillonnet, 21 Nov 2019 05:13 PM

Download (10.1 KB)

View differences:

Subject: [PATCH 1/3] Revert "add compatibility layer for support of Django
 native JSONField (fixes #29193)"

This reverts commit d730dba525215d615fa2b974d44183d16909f4d2.
 src/authentic2/apps.py                        |  51 +-------
 src/authentic2/compat.py                      | 116 +++---------------
 .../migrations/0007_auto_20181219_0005.py     |  22 ----
 src/authentic2_auth_oidc/models.py            |   5 +-
 tox.ini                                       |   1 -
 5 files changed, 17 insertions(+), 178 deletions(-)
 delete mode 100644 src/authentic2_auth_oidc/migrations/0007_auto_20181219_0005.py
src/authentic2/apps.py
18 18
from django.apps import AppConfig
19 19
from django.views import debug
20 20

  
21
from django.db import connection, router
22
from django.db.models.signals import post_migrate
23

  
24
from . import plugins, compat
25

  
21
from . import plugins
26 22

  
27 23
class Authentic2Config(AppConfig):
28 24
    name = 'authentic2'
29 25
    verbose_name = 'Authentic2'
30 26

  
31
    def post_migrate_update_json_column(self, sender, using, **kwargs):
32
        # adapted from https://github.com/kbussell/django-jsonfield-compat/blob/4f6ac4bfaea2224559b174b6d16d846b93d125c6/jsonfield_compat/convert.py
33
        # MIT License, kbussel
34
        if connection.vendor != 'postgresql':
35
            return
36

  
37
        if compat.has_postgresql_support():
38
            expected_type = 'JSONB'
39
        else:
40
            expected_type = 'TEXT'
41

  
42
        def convert_column_to_json(model, column_name):
43
            table_name = model._meta.db_table
44

  
45
            with connection.cursor() as cursor:
46
                cursor.execute(
47
                    "select data_type from information_schema.columns "
48
                    "where table_name = %s and column_name = %s;",
49
                    [table_name, column_name])
50

  
51
                current_type = cursor.fetchone()[0].upper()
52

  
53
                if current_type != expected_type:
54
                    print("{app}: Converting {col} to use native {type} field".format(
55
                        app=model._meta.app_label, col=column_name, type=expected_type))
56

  
57
                    cursor.execute(
58
                        "ALTER TABLE {table} ALTER COLUMN {col} "
59
                        "TYPE {type} USING {col}::{type};".format(
60
                            table=table_name, col=column_name, type=expected_type
61
                        )
62
                    )
63

  
64
        def convert_model_json_fields(model):
65
            json_fields = [f for f in model._meta.fields if f.__class__ == compat.JSONField]
66
            for field in json_fields:
67
                _, column_name = field.get_attname_column()
68
                convert_column_to_json(model, column_name)
69

  
70
        for model in list(sender.get_models()):
71
            if not router.allow_migrate(using, model):
72
                continue
73
            convert_model_json_fields(model)
74

  
75 27
    def ready(self):
76 28
        plugins.init()
77 29
        debug.HIDDEN_SETTINGS = re.compile(
78 30
            'API|TOKEN|KEY|SECRET|PASS|PROFANITIES_LIST|SIGNATURE|LDAP')
79
        post_migrate.connect(self.post_migrate_update_json_column)
src/authentic2/compat.py
14 14
# You should have received a copy of the GNU Affero General Public License
15 15
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
16 16

  
17
import inspect
18

  
19
import django
20
from django.db import connection
21
from django.db.utils import OperationalError
22
from django.core.exceptions import ImproperlyConfigured
17
from datetime import datetime
23 18

  
19
from django.conf import settings
24 20
from django.contrib.auth.tokens import PasswordResetTokenGenerator
25 21

  
26

  
27
default_token_generator = PasswordResetTokenGenerator()
28

  
29

  
30
def has_postgresql_support():
31
    try:
32
        return connection.vendor == 'postgresql' and connection.pg_version > 90400
33
    except (ImproperlyConfigured, OperationalError):
34
        # database is not initialized, be conservative
35
        return False
36

  
37

  
38
def use_django_native_field():
39
    return has_postgresql_support() and django.VERSION >= (1, 11)
40

  
41

  
42
class JSONField(object):
43
    __dj11_field = None
44
    __jsonfield_field = None
45
    __name = None
46

  
47
    def __init__(self, *args, **kwargs):
48
        self.__args = args
49
        self.__kwargs = kwargs
50
        if django.VERSION >= (1, 11):
51
            try:
52
                from django.contrib.postgres.fields import JSONField
53
                self.__dj11_field = JSONField(*args, **kwargs)
54
            except ImportError:
55
                pass
56
        try:
57
            from jsonfield.fields import JSONField
58
            self.__jsonfield_field = JSONField(*args, **kwargs)
59
        except ImportError:
60
            pass
61

  
62
    def __real_field__(self):
63
        if use_django_native_field():
64
            assert self.__dj11_field
65
            return self.__dj11_field
66
        assert self.__jsonfield_field
67
        return self.__jsonfield_field
68

  
69
    def __getattr__(self, key):
70
        return getattr(self.__real_field__(), key)
71

  
72
    def __setattr__(self, key, value):
73
        if key.startswith('_JSONField__'):
74
            super(JSONField, self).__setattr__(key, value)
75
        else:
76
            setattr(self.__real__field(), key, value)
77

  
78
    # we need to implement contribute_to_class so that the direct
79
    # implementation from the two sub-fields is not used directly
80
    def contribute_to_class(self, cls, name, private_only=False, virtual_only=False, **kwargs):
81
        assert not virtual_only and not private_only, 'virtual_only / private_only are not supported'
82
        assert not kwargs, 'new arguments to contribute_to_class not supported'
83
        self.__name = name
84
        if self.__dj11_field:
85
            self.__dj11_field.set_attributes_from_name(name)
86
            self.__dj11_field.model = cls
87
        if self.__jsonfield_field:
88
            self.__jsonfield_field.set_attributes_from_name(name)
89
            self.__jsonfield_field.model = cls
90
        cls._meta.add_field(self)
91

  
92
    # the next two methods are useful for compatibilit with the migration engine
93
    # inspect is used because migration autodetector cannot recognize this class
94
    # as a subclass of models.Field.
95
    def deconstruct(self):
96
        d = (self.__name, 'authentic2.compat.JSONField', self.__args, self.__kwargs)
97
        previous_frame = inspect.currentframe().f_back
98
        if inspect.getframeinfo(previous_frame)[2] in ('serialize', 'deep_deconstruct'):
99
            d = d[1:]
100
        return d
101

  
102
    def clone(self):
103
        from copy import copy
104
        new = copy(self)
105
        if self.__dj11_field:
106
            new.__dj11_field = new.__dj11_field.clone()
107
        if self.__jsonfield_field:
108
            new.__jsonfield_field = new.__jsonfield_field.clone()
109
        return new
110

  
22
try:
23
    from django.contrib.auth import get_user_model
24
except ImportError:
25
    from django.contrib.auth.models import User
26
    get_user_model = lambda: User
111 27

  
112 28
try:
113
    import jsonfield.fields
29
    from django.db.transaction import atomic
30
    commit_on_success = atomic
114 31
except ImportError:
115
    pass
116
else:
117
    # prevent django-jsonfield from modifying postgresql connection when we are
118
    # not using it
119
    if hasattr(jsonfield.fields, 'connection_created'):
120
        def configure_database_connection(connection, **kwargs):
121
            if django.VERSION < (1, 11):
122
                jsonfield.fields.configure_database_connection(connection, **kwargs)
123
        jsonfield.fields.connection_created.disconnect(jsonfield.fields.configure_database_connection)
124
        jsonfield.fields.connection_created.connect(configure_database_connection)
32
    from django.db.transaction import commit_on_success
33

  
34
user_model_label = getattr(settings, 'AUTH_USER_MODEL', 'auth.User')
35

  
36
default_token_generator = PasswordResetTokenGenerator()
src/authentic2_auth_oidc/migrations/0007_auto_20181219_0005.py
1
# -*- coding: utf-8 -*-
2
# Generated by Django 1.11.17 on 2018-12-18 23:05
3
from __future__ import unicode_literals
4

  
5
import authentic2.compat
6
import authentic2_auth_oidc.models
7
from django.db import migrations
8

  
9

  
10
class Migration(migrations.Migration):
11

  
12
    dependencies = [
13
        ('authentic2_auth_oidc', '0006_oidcprovider_claims_parameter_supported'),
14
    ]
15

  
16
    operations = [
17
        migrations.AlterField(
18
            model_name='oidcprovider',
19
            name='jwkset_json',
20
            field=authentic2.compat.JSONField(blank=True, null=True, validators=[authentic2_auth_oidc.models.validate_jwkset], verbose_name='JSON WebKey set'),
21
        ),
22
    ]
src/authentic2_auth_oidc/models.py
23 23
from django.conf import settings
24 24
from django.core.exceptions import ValidationError
25 25

  
26
from jsonfield import JSONField
26 27

  
27 28
from jwcrypto.jwk import JWKSet, InvalidJWKValue, JWK
28 29

  
29 30
from django_rbac.utils import get_ou_model_name
30 31

  
31
from authentic2 import compat
32

  
33 32
from . import managers
34 33

  
35 34

  
......
108 107
        max_length=128,
109 108
        blank=True,
110 109
        verbose_name=_('scopes'))
111
    jwkset_json = compat.JSONField(
110
    jwkset_json = JSONField(
112 111
        verbose_name=_('JSON WebKey set'),
113 112
        null=True,
114 113
        blank=True,
tox.ini
42 42
  dj111: django<2.0
43 43
  dj111: django-tables<2.0
44 44
  pg: psycopg2-binary
45
  dj111: psycopg2-binary
46 45
  coverage
47 46
  pytest-cov
48 47
  pytest-django<3.4.6
49
-