general: ensure jsonfields are using jsonb columns (#52915)

This commit is contained in:
Frédéric Péters 2021-04-11 18:03:34 +02:00
parent 4021e5d3b0
commit 21976d8f20
9 changed files with 267 additions and 0 deletions

View File

@ -0,0 +1,17 @@
# Generated by Django 2.2.12 on 2021-04-11 15:54
from django.db import migrations
from combo.utils.db import EnsureJsonbType
class Migration(migrations.Migration):
dependencies = [
('dataviz', '0016_auto_20201215_1624'),
]
operations = [
EnsureJsonbType(model_name='Statistic', field_name='filters'),
EnsureJsonbType(model_name='ChartNgCell', field_name='filter_params'),
]

View File

@ -0,0 +1,20 @@
# Generated by Django 2.2.12 on 2021-04-11 15:54
from django.db import migrations
from combo.utils.db import EnsureJsonbType
class Migration(migrations.Migration):
dependencies = [
('lingo', '0041_regie_can_pay_only_one_basket_item'),
]
operations = [
EnsureJsonbType(model_name='PaymentBackend', field_name='service_options'),
EnsureJsonbType(model_name='Regie', field_name='transaction_options'),
EnsureJsonbType(model_name='BasketItem', field_name='request_data'),
EnsureJsonbType(model_name='Transaction', field_name='bank_data'),
EnsureJsonbType(model_name='TransactionOperation', field_name='bank_result'),
]

View File

@ -0,0 +1,17 @@
# Generated by Django 2.2.12 on 2021-04-11 15:54
from django.db import migrations
from combo.utils.db import EnsureJsonbType
class Migration(migrations.Migration):
dependencies = [
('pwa', '0006_auto_20190628_1042'),
]
operations = [
EnsureJsonbType(model_name='PwaSettings', field_name='push_notifications_infos'),
EnsureJsonbType(model_name='PushSubscription', field_name='subscription_info'),
]

View File

@ -0,0 +1,16 @@
# Generated by Django 2.2.12 on 2021-04-11 15:54
from django.db import migrations
from combo.utils.db import EnsureJsonbType
class Migration(migrations.Migration):
dependencies = [
('search', '0008_searchcell_input_placeholder'),
]
operations = [
EnsureJsonbType(model_name='SearchCell', field_name='_search_services'),
]

View File

@ -0,0 +1,21 @@
# Generated by Django 2.2.12 on 2021-04-11 15:54
from django.db import migrations
from combo.db.utils import EnsureJsonbType
class Migration(migrations.Migration):
dependencies = [
('wcs', '0025_submission_categories'),
]
operations = [
EnsureJsonbType(model_name='WcsFormCell', field_name='cached_json'),
EnsureJsonbType(model_name='WcsCurrentFormsCell', field_name='categories'),
EnsureJsonbType(model_name='WcsCurrentDraftsCell', field_name='categories'),
EnsureJsonbType(model_name='WcsFormsOfCategoryCell', field_name='manual_order'),
EnsureJsonbType(model_name='WcsCardInfosCell', field_name='cached_json'),
EnsureJsonbType(model_name='BackofficeSubmissionCell', field_name='categories'),
]

View File

@ -0,0 +1,50 @@
# combo - content management system
# Copyright (C) 2015-2021 Entr'ouvert
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU Affero General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from django.apps import apps
from django.core.management.base import BaseCommand, CommandError
from django.db import connection
from jsonfield import JSONField
class Command(BaseCommand):
help = 'Ensure all JSON fields are of type jsonb'
def handle(self, **options):
for app in apps.get_models():
for field in app._meta.get_fields():
if isinstance(field, JSONField):
table_name = app._meta.db_table
column_name = app._meta.get_field(field.name).column
with connection.cursor() as cursor:
query = '''SELECT table_schema
FROM information_schema.columns
WHERE table_name = %s AND column_name = %s AND data_type != %s'''
cursor.execute(query, [table_name, column_name, 'jsonb'])
for line in cursor.fetchall():
alter_query = '''ALTER TABLE "%(schema_name)s"."%(table_name)s"
ALTER COLUMN "%(column_name)s"
TYPE jsonb USING "%(column_name)s"::jsonb'''
params = {
'schema_name': line[0],
'table_name': table_name,
'column_name': column_name,
}
try:
cursor.execute(alter_query % params)
except Exception as e:
raise CommandError(e)

View File

@ -0,0 +1,18 @@
# Generated by Django 2.2.12 on 2021-04-11 15:54
from django.db import migrations
from combo.utils.db import EnsureJsonbType
class Migration(migrations.Migration):
dependencies = [
('data', '0045_link_list_limit'),
]
operations = [
EnsureJsonbType(model_name='Page', field_name='related_cells'),
EnsureJsonbType(model_name='PageSnapshot', field_name='serialization'),
EnsureJsonbType(model_name='ConfigJsonCell', field_name='parameters'),
]

47
combo/utils/db.py Normal file
View File

@ -0,0 +1,47 @@
# combo - content management system
# Copyright (C) 2015-2021 Entr'ouvert
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU Affero General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from django.db.migrations.operations.base import Operation
class EnsureJsonbType(Operation):
reversible = True
def __init__(self, model_name, field_name):
self.model_name = model_name
self.field_name = field_name
def state_forwards(self, app_label, state):
pass
def database_forwards(self, app_label, schema_editor, from_state, to_state):
model = from_state.apps.get_model(app_label, self.model_name)
table_name = model._meta.db_table
field = model._meta.get_field(self.field_name)
_, column_name = field.get_attname_column()
with schema_editor.connection.cursor() as cursor:
cursor.execute(
'ALTER TABLE {table} ALTER COLUMN {col} TYPE jsonb USING {col}::jsonb;'.format(
table=table_name, col=column_name
)
)
def database_backwards(self, app_label, schema_editor, from_state, to_state):
pass
def describe(self):
return "Migrate to postgres jsonb type"

View File

@ -0,0 +1,61 @@
# -*- coding: utf-8 -*-
import pytest
from django.core.management import call_command
from django.db import connection
pytestmark = pytest.mark.django_db
@pytest.mark.skipif(connection.vendor != 'postgresql', reason='only postgresql is supported')
def test_ensure_jsonb_fields():
json_fields = (
'bank_data',
'bank_result',
'cached_json',
'cached_json',
'categories',
'categories',
'categories',
'filter_params',
'filters',
'manual_order',
'parameters',
'push_notifications_infos',
'related_cells',
'request_data',
'_search_services',
'serialization',
'service_options',
'subscription_info',
'transaction_options',
)
with connection.cursor() as cursor:
query = '''SELECT table_name, column_name, data_type
FROM information_schema.columns
WHERE column_name IN %(json_fields)s'''
cursor.execute(query, {'json_fields': json_fields})
# make sure the data_type is correct
for line in cursor.fetchall():
assert line[2] == 'jsonb'
# alter columns
cursor.execute(
'''ALTER TABLE wcs_wcsformsofcategorycell
ALTER COLUMN manual_order TYPE text USING manual_order::text'''
)
call_command('ensure_jsonb')
with connection.cursor() as cursor:
query = '''SELECT table_name, column_name, data_type
FROM information_schema.columns
WHERE column_name IN %(json_fields)s'''
cursor.execute(query, {'json_fields': json_fields})
# check the data_type is correct
for line in cursor.fetchall():
assert line[2] == 'jsonb'