Projet

Général

Profil

0001-management-add-command-to-ensure-all-JSONField-field.patch

Voir les différences:

Subject: [PATCH] management: add command to ensure all JSONField fields have
 correct db type (#43501)

 .../base/management/commands/ensure_jsonb.py  | 50 +++++++++++++++++
 tests/test_ensure_jsonbfields.py              | 56 +++++++++++++++++++
 2 files changed, 106 insertions(+)
 create mode 100644 passerelle/base/management/commands/ensure_jsonb.py
 create mode 100644 tests/test_ensure_jsonbfields.py
passerelle/base/management/commands/ensure_jsonb.py
1
# passerelle - uniform access to multiple data sources and services
2
# Copyright (C) 2017  Entr'ouvert
3
#
4
# This program is free software: you can redistribute it and/or modify it
5
# under the terms of the GNU Affero General Public License as published
6
# by the Free Software Foundation, either version 3 of the License, or
7
# (at your option) any later version.
8
#
9
# This program is distributed in the hope that it will be useful,
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
# GNU Affero General Public License for more details.
13
#
14
# You should have received a copy of the GNU Affero General Public License
15
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
16

  
17
from django.db import connection
18
from django.apps import apps
19

  
20
from django.core.management.base import BaseCommand, CommandError
21
from django.contrib.postgres.fields import JSONField
22

  
23

  
24

  
25

  
26
class Command(BaseCommand):
27
    help = 'Ensure all JSON fields are of type jsonb'
28

  
29
    def handle(self, **options):
30
        for app in apps.get_models():
31
            for field in app._meta.get_fields():
32
                if isinstance(field, JSONField):
33
                    table_name = app._meta.db_table
34
                    column_name = app._meta.get_field(field.name).column
35
                    with connection.cursor() as cursor:
36
                        query = 'SELECT table_schema, data_type FROM information_schema.columns WHERE table_name=%s AND column_name=%s'
37
                        cursor.execute(query, [table_name, column_name])
38
                        for schema_name, db_type in cursor.fetchall():
39
                            if db_type == 'jsonb':
40
                                continue
41
                            alter = 'ALTER TABLE "%(schema_name)s"."%(table_name)s" ALTER COLUMN "%(column_name)s" TYPE jsonb USING "%(column_name)s"::jsonb'
42
                            params = {
43
                                "schema_name": schema_name,
44
                                'table_name': table_name,
45
                                'column_name': column_name
46
                            }
47
                            try:
48
                                cursor.execute(alter % params)
49
                            except Exception as e:
50
                                raise CommandError(e)
tests/test_ensure_jsonbfields.py
1
# -*- coding: utf-8 -*-
2
import pytest
3

  
4

  
5
from django.core.files import File
6
from django.utils.six import BytesIO
7

  
8
from django.db import connection
9

  
10
from django.core.management import call_command
11

  
12
from passerelle.apps.csvdatasource.models import CsvDataSource
13
from passerelle.contrib.teamnet_axel.models import TeamnetAxel
14

  
15
pytestmark = pytest.mark.django_db
16

  
17

  
18
@pytest.fixture
19
def setup():
20

  
21
    def maker(columns_keynames='fam,id,lname,fname,sex', filename='data.csv', sheet_name='Feuille2',
22
              data=b''):
23
        csv = CsvDataSource.objects.create(csv_file=File(BytesIO(data), filename),
24
                                           sheet_name=sheet_name, columns_keynames=columns_keynames,
25
                                           slug='test', title='a title',
26
                                           description='a description')
27
        teamnet = TeamnetAxel.objects.create(slug='test', billing_regies={},
28
                                             wsdl_url='http://example.net/AXEL_WS/AxelWS.php?wsdl')
29
        return csv, teamnet
30

  
31
    return maker
32

  
33

  
34

  
35
def test_create_reference_column(setup):
36
    with connection.cursor() as cursor:
37
        query = "SELECT table_name, column_name, data_type FROM information_schema.columns WHERE column_name IN ('_dialect_options', 'billing_regies')"
38
        cursor.execute(query)
39

  
40
        # make sure the data_type is correct
41
        for line in cursor.fetchall():
42
            assert line[2] == 'jsonb'
43

  
44
        # alter columns
45
        cursor.execute('ALTER TABLE csvdatasource_csvdatasource ALTER COLUMN _dialect_options TYPE text USING _dialect_options::text')
46
        cursor.execute('ALTER TABLE teamnet_axel_teamnetaxel ALTER COLUMN billing_regies TYPE text USING billing_regies::text')
47

  
48
    call_command('ensure_jsonb')
49

  
50
    with connection.cursor() as cursor:
51
        query = "SELECT table_name, column_name, data_type FROM information_schema.columns WHERE column_name IN ('_dialect_options', 'billing_regies')"
52
        cursor.execute(query)
53

  
54
        # check the data_type is correct
55
        for line in cursor.fetchall():
56
            assert line[2] == 'jsonb'
0
-