Replace all usage of customized json fields with the Django builtin

The event_data field on event models, however, is getting an
overridden version that retains the underlying text data type for the
column, to avoid a heavy data migration on those tables.

Also, certain of the larger tables are getting these fields with the
NOT NULL constraint turned off, to avoid a long migration.

Remove the django.utils.six monkey patch we did at the beginning of
the upgrade.
This commit is contained in:
Jeff Bradberry
2022-02-03 10:29:08 -05:00
parent 65d17fb316
commit 05142a779d
36 changed files with 122 additions and 240 deletions

View File

@@ -6,8 +6,6 @@ import os
import sys import sys
import warnings import warnings
import six
from pkg_resources import get_distribution from pkg_resources import get_distribution
__version__ = get_distribution('awx').version __version__ = get_distribution('awx').version
@@ -37,9 +35,6 @@ else:
from django.db.models import indexes from django.db.models import indexes
from django.db.backends.utils import names_digest from django.db.backends.utils import names_digest
from django.db import connection from django.db import connection
from django import utils
utils.six = six # FIXME: monkey patch to get us through for now
if HAS_DJANGO is True: if HAS_DJANGO is True:

View File

@@ -6,6 +6,7 @@ from uuid import UUID
# Django # Django
from django.core.exceptions import PermissionDenied from django.core.exceptions import PermissionDenied
from django.db.models import JSONField
from django.db.models.fields import PositiveIntegerField, BooleanField from django.db.models.fields import PositiveIntegerField, BooleanField
from django.db.models.fields.related import ForeignKey from django.db.models.fields.related import ForeignKey
from django.http import Http404 from django.http import Http404
@@ -22,7 +23,7 @@ from rest_framework.request import clone_request
# AWX # AWX
from awx.api.fields import ChoiceNullField from awx.api.fields import ChoiceNullField
from awx.main.fields import JSONField, ImplicitRoleField from awx.main.fields import ImplicitRoleField
from awx.main.models import NotificationTemplate from awx.main.models import NotificationTemplate
from awx.main.utils.execution_environments import get_default_pod_spec from awx.main.utils.execution_environments import get_default_pod_spec

View File

@@ -97,7 +97,7 @@ from awx.main.models import (
) )
from awx.main.models.base import VERBOSITY_CHOICES, NEW_JOB_TYPE_CHOICES from awx.main.models.base import VERBOSITY_CHOICES, NEW_JOB_TYPE_CHOICES
from awx.main.models.rbac import get_roles_on_resource, role_summary_fields_generator from awx.main.models.rbac import get_roles_on_resource, role_summary_fields_generator
from awx.main.fields import ImplicitRoleField, JSONBField from awx.main.fields import ImplicitRoleField
from awx.main.utils import ( from awx.main.utils import (
get_type_for_model, get_type_for_model,
get_model_for_type, get_model_for_type,
@@ -1718,7 +1718,7 @@ class InventorySerializer(LabelsListMixin, BaseSerializerWithVariables):
def validate_host_filter(self, host_filter): def validate_host_filter(self, host_filter):
if host_filter: if host_filter:
try: try:
for match in JSONBField.get_lookups().keys(): for match in models.JSONField.get_lookups().keys():
if match == 'exact': if match == 'exact':
# __exact is allowed # __exact is allowed
continue continue

View File

@@ -2,7 +2,6 @@
from __future__ import unicode_literals from __future__ import unicode_literals
from django.db import migrations, models from django.db import migrations, models
import jsonfield.fields
from django.conf import settings from django.conf import settings
@@ -18,7 +17,7 @@ class Migration(migrations.Migration):
('created', models.DateTimeField(default=None, editable=False)), ('created', models.DateTimeField(default=None, editable=False)),
('modified', models.DateTimeField(default=None, editable=False)), ('modified', models.DateTimeField(default=None, editable=False)),
('key', models.CharField(max_length=255)), ('key', models.CharField(max_length=255)),
('value', jsonfield.fields.JSONField(null=True)), ('value', models.JSONField(null=True)),
( (
'user', 'user',
models.ForeignKey(related_name='settings', default=None, editable=False, to=settings.AUTH_USER_MODEL, on_delete=models.CASCADE, null=True), models.ForeignKey(related_name='settings', default=None, editable=False, to=settings.AUTH_USER_MODEL, on_delete=models.CASCADE, null=True),

View File

@@ -1,12 +1,11 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from __future__ import unicode_literals from __future__ import unicode_literals
from django.db import migrations from django.db import migrations, models
import awx.main.fields
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [('conf', '0002_v310_copy_tower_settings')] dependencies = [('conf', '0002_v310_copy_tower_settings')]
operations = [migrations.AlterField(model_name='setting', name='value', field=awx.main.fields.JSONField(null=True))] operations = [migrations.AlterField(model_name='setting', name='value', field=models.JSONField(null=True))]

View File

@@ -9,7 +9,6 @@ from django.db import models
# AWX # AWX
from awx.main.models.base import CreatedModifiedModel, prevent_search from awx.main.models.base import CreatedModifiedModel, prevent_search
from awx.main.fields import JSONField
from awx.main.utils import encrypt_field from awx.main.utils import encrypt_field
from awx.conf import settings_registry from awx.conf import settings_registry
@@ -19,7 +18,7 @@ __all__ = ['Setting']
class Setting(CreatedModifiedModel): class Setting(CreatedModifiedModel):
key = models.CharField(max_length=255) key = models.CharField(max_length=255)
value = JSONField(null=True) value = models.JSONField(null=True)
user = prevent_search(models.ForeignKey('auth.User', related_name='settings', default=None, null=True, editable=False, on_delete=models.CASCADE)) user = prevent_search(models.ForeignKey('auth.User', related_name='settings', default=None, null=True, editable=False, on_delete=models.CASCADE))
def __str__(self): def __str__(self):

View File

@@ -11,7 +11,6 @@ from jinja2 import sandbox, StrictUndefined
from jinja2.exceptions import UndefinedError, TemplateSyntaxError, SecurityError from jinja2.exceptions import UndefinedError, TemplateSyntaxError, SecurityError
# Django # Django
from django.contrib.postgres.fields import JSONField as upstream_JSONBField
from django.core import exceptions as django_exceptions from django.core import exceptions as django_exceptions
from django.core.serializers.json import DjangoJSONEncoder from django.core.serializers.json import DjangoJSONEncoder
from django.db.models.signals import ( from django.db.models.signals import (
@@ -29,6 +28,7 @@ from django.db.models.fields.related_descriptors import (
create_forward_many_to_many_manager, create_forward_many_to_many_manager,
) )
from django.utils.encoding import smart_str from django.utils.encoding import smart_str
from django.db.models import JSONField
from django.utils.functional import cached_property from django.utils.functional import cached_property
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
@@ -36,9 +36,6 @@ from django.utils.translation import gettext_lazy as _
from jsonschema import Draft4Validator, FormatChecker from jsonschema import Draft4Validator, FormatChecker
import jsonschema.exceptions import jsonschema.exceptions
# Django-JSONField
from jsonfield import JSONField as upstream_JSONField
# DRF # DRF
from rest_framework import serializers from rest_framework import serializers
@@ -52,9 +49,9 @@ from awx.main import utils
__all__ = [ __all__ = [
'JSONBlob',
'AutoOneToOneField', 'AutoOneToOneField',
'ImplicitRoleField', 'ImplicitRoleField',
'JSONField',
'SmartFilterField', 'SmartFilterField',
'OrderedManyToManyField', 'OrderedManyToManyField',
'update_role_parentage_for_instance', 'update_role_parentage_for_instance',
@@ -71,40 +68,9 @@ def __enum_validate__(validator, enums, instance, schema):
Draft4Validator.VALIDATORS['enum'] = __enum_validate__ Draft4Validator.VALIDATORS['enum'] = __enum_validate__
class JSONField(upstream_JSONField): class JSONBlob(JSONField):
def __init__(self, *args, **kwargs): def get_internal_type(self):
super().__init__(*args, **kwargs) return "TextField"
self.decoder_kwargs = {'cls': json.JSONDecoder} # FIXME
def db_type(self, connection):
return 'text'
def from_db_value(self, value, expression, connection):
if value in {'', None} and not self.null:
return {}
return super(JSONField, self).from_db_value(value, expression, connection)
class JSONBField(upstream_JSONBField):
def get_prep_lookup(self, lookup_type, value):
if isinstance(value, str) and value == "null":
return 'null'
return super(JSONBField, self).get_prep_lookup(lookup_type, value)
def get_db_prep_value(self, value, connection, prepared=False):
if connection.vendor == 'sqlite':
# sqlite (which we use for tests) does not support jsonb;
if hasattr(value, 'adapted'):
value = value.adapted # FIXME: Django 3.0 uses JsonAdapter, removed in 3.1
return json.dumps(value, cls=DjangoJSONEncoder)
return super(JSONBField, self).get_db_prep_value(value, connection, prepared)
def from_db_value(self, value, expression, connection):
# Work around a bug in django-jsonfield
# https://bitbucket.org/schinckel/django-jsonfield/issues/57/cannot-use-in-the-same-project-as-djangos
if isinstance(value, str):
return json.loads(value)
return value
# Based on AutoOneToOneField from django-annoying: # Based on AutoOneToOneField from django-annoying:
@@ -391,7 +357,7 @@ class SmartFilterField(models.TextField):
return super(SmartFilterField, self).get_prep_value(value) return super(SmartFilterField, self).get_prep_value(value)
class JSONSchemaField(JSONBField): class JSONSchemaField(models.JSONField):
""" """
A JSONB field that self-validates against a defined JSON schema A JSONB field that self-validates against a defined JSON schema
(http://json-schema.org). This base class is intended to be overwritten by (http://json-schema.org). This base class is intended to be overwritten by
@@ -404,8 +370,13 @@ class JSONSchemaField(JSONBField):
# validation # validation
empty_values = (None, '') empty_values = (None, '')
def __init__(self, encoder=None, decoder=None, **options):
if encoder is None:
encoder = DjangoJSONEncoder
super().__init__(encoder=encoder, decoder=decoder, **options)
def get_default(self): def get_default(self):
return copy.deepcopy(super(JSONBField, self).get_default()) return copy.deepcopy(super(models.JSONField, self).get_default())
def schema(self, model_instance): def schema(self, model_instance):
raise NotImplementedError() raise NotImplementedError()

View File

@@ -7,7 +7,6 @@ from __future__ import unicode_literals
from django.db import migrations, models from django.db import migrations, models
import django.utils.timezone import django.utils.timezone
import jsonfield.fields
import django.db.models.deletion import django.db.models.deletion
from django.conf import settings from django.conf import settings
import taggit.managers import taggit.managers
@@ -70,7 +69,7 @@ class Migration(migrations.Migration):
], ],
), ),
), ),
('event_data', jsonfield.fields.JSONField(default=dict, blank=True)), ('event_data', awx.main.fields.JSONBlob(default=dict, blank=True)),
('failed', models.BooleanField(default=False, editable=False)), ('failed', models.BooleanField(default=False, editable=False)),
('changed', models.BooleanField(default=False, editable=False)), ('changed', models.BooleanField(default=False, editable=False)),
('counter', models.PositiveIntegerField(default=0)), ('counter', models.PositiveIntegerField(default=0)),
@@ -433,7 +432,7 @@ class Migration(migrations.Migration):
], ],
), ),
), ),
('event_data', jsonfield.fields.JSONField(default=dict, blank=True)), ('event_data', awx.main.fields.JSONBlob(default=dict, blank=True)),
('failed', models.BooleanField(default=False, editable=False)), ('failed', models.BooleanField(default=False, editable=False)),
('changed', models.BooleanField(default=False, editable=False)), ('changed', models.BooleanField(default=False, editable=False)),
('host_name', models.CharField(default='', max_length=1024, editable=False)), ('host_name', models.CharField(default='', max_length=1024, editable=False)),
@@ -623,7 +622,7 @@ class Migration(migrations.Migration):
('dtend', models.DateTimeField(default=None, null=True, editable=False)), ('dtend', models.DateTimeField(default=None, null=True, editable=False)),
('rrule', models.CharField(max_length=255)), ('rrule', models.CharField(max_length=255)),
('next_run', models.DateTimeField(default=None, null=True, editable=False)), ('next_run', models.DateTimeField(default=None, null=True, editable=False)),
('extra_data', jsonfield.fields.JSONField(default=dict, blank=True)), ('extra_data', models.JSONField(default=dict, null=True, blank=True)),
( (
'created_by', 'created_by',
models.ForeignKey( models.ForeignKey(
@@ -751,7 +750,7 @@ class Migration(migrations.Migration):
('elapsed', models.DecimalField(editable=False, max_digits=12, decimal_places=3)), ('elapsed', models.DecimalField(editable=False, max_digits=12, decimal_places=3)),
('job_args', models.TextField(default='', editable=False, blank=True)), ('job_args', models.TextField(default='', editable=False, blank=True)),
('job_cwd', models.CharField(default='', max_length=1024, editable=False, blank=True)), ('job_cwd', models.CharField(default='', max_length=1024, editable=False, blank=True)),
('job_env', jsonfield.fields.JSONField(default=dict, editable=False, blank=True)), ('job_env', models.JSONField(default=dict, editable=False, null=True, blank=True)),
('job_explanation', models.TextField(default='', editable=False, blank=True)), ('job_explanation', models.TextField(default='', editable=False, blank=True)),
('start_args', models.TextField(default='', editable=False, blank=True)), ('start_args', models.TextField(default='', editable=False, blank=True)),
('result_stdout_text', models.TextField(default='', editable=False, blank=True)), ('result_stdout_text', models.TextField(default='', editable=False, blank=True)),
@@ -1035,7 +1034,7 @@ class Migration(migrations.Migration):
('host_config_key', models.CharField(default='', max_length=1024, blank=True)), ('host_config_key', models.CharField(default='', max_length=1024, blank=True)),
('ask_variables_on_launch', models.BooleanField(default=False)), ('ask_variables_on_launch', models.BooleanField(default=False)),
('survey_enabled', models.BooleanField(default=False)), ('survey_enabled', models.BooleanField(default=False)),
('survey_spec', jsonfield.fields.JSONField(default=dict, blank=True)), ('survey_spec', models.JSONField(default=dict, blank=True)),
], ],
options={ options={
'ordering': ('name',), 'ordering': ('name',),

View File

@@ -12,7 +12,6 @@ import django.db.models.deletion
from django.conf import settings from django.conf import settings
from django.utils.timezone import now from django.utils.timezone import now
import jsonfield.fields
import taggit.managers import taggit.managers
@@ -199,7 +198,7 @@ class Migration(migrations.Migration):
), ),
('recipients', models.TextField(default='', editable=False, blank=True)), ('recipients', models.TextField(default='', editable=False, blank=True)),
('subject', models.TextField(default='', editable=False, blank=True)), ('subject', models.TextField(default='', editable=False, blank=True)),
('body', jsonfield.fields.JSONField(default=dict, blank=True)), ('body', models.JSONField(default=dict, null=True, blank=True)),
], ],
options={ options={
'ordering': ('pk',), 'ordering': ('pk',),
@@ -230,7 +229,7 @@ class Migration(migrations.Migration):
], ],
), ),
), ),
('notification_configuration', jsonfield.fields.JSONField(default=dict)), ('notification_configuration', models.JSONField(default=dict)),
( (
'created_by', 'created_by',
models.ForeignKey( models.ForeignKey(
@@ -324,9 +323,7 @@ class Migration(migrations.Migration):
('module', models.CharField(max_length=128)), ('module', models.CharField(max_length=128)),
( (
'facts', 'facts',
awx.main.fields.JSONBField( models.JSONField(default=dict, help_text='Arbitrary JSON structure of module facts captured at timestamp for a single host.', blank=True),
default=dict, help_text='Arbitrary JSON structure of module facts captured at timestamp for a single host.', blank=True
),
), ),
( (
'host', 'host',

View File

@@ -3,7 +3,6 @@ from __future__ import unicode_literals
from django.db import migrations, models from django.db import migrations, models
import awx.main.models.notifications import awx.main.models.notifications
import jsonfield.fields
import django.db.models.deletion import django.db.models.deletion
import awx.main.models.workflow import awx.main.models.workflow
import awx.main.fields import awx.main.fields
@@ -221,7 +220,7 @@ class Migration(migrations.Migration):
migrations.AddField( migrations.AddField(
model_name='workflowjobnode', model_name='workflowjobnode',
name='char_prompts', name='char_prompts',
field=jsonfield.fields.JSONField(default=dict, blank=True), field=models.JSONField(default=dict, null=True, blank=True),
), ),
migrations.AddField( migrations.AddField(
model_name='workflowjobnode', model_name='workflowjobnode',
@@ -260,7 +259,7 @@ class Migration(migrations.Migration):
migrations.AddField( migrations.AddField(
model_name='workflowjobtemplatenode', model_name='workflowjobtemplatenode',
name='char_prompts', name='char_prompts',
field=jsonfield.fields.JSONField(default=dict, blank=True), field=models.JSONField(default=dict, null=True, blank=True),
), ),
migrations.AddField( migrations.AddField(
model_name='workflowjobtemplatenode', model_name='workflowjobtemplatenode',
@@ -308,12 +307,12 @@ class Migration(migrations.Migration):
migrations.AddField( migrations.AddField(
model_name='job', model_name='job',
name='artifacts', name='artifacts',
field=jsonfield.fields.JSONField(default=dict, editable=False, blank=True), field=models.JSONField(default=dict, editable=False, null=True, blank=True),
), ),
migrations.AddField( migrations.AddField(
model_name='workflowjobnode', model_name='workflowjobnode',
name='ancestor_artifacts', name='ancestor_artifacts',
field=jsonfield.fields.JSONField(default=dict, editable=False, blank=True), field=models.JSONField(default=dict, editable=False, null=True, blank=True),
), ),
# Job timeout settings # Job timeout settings
migrations.AddField( migrations.AddField(
@@ -381,9 +380,7 @@ class Migration(migrations.Migration):
migrations.AddField( migrations.AddField(
model_name='project', model_name='project',
name='playbook_files', name='playbook_files',
field=jsonfield.fields.JSONField( field=models.JSONField(default=list, help_text='List of playbooks found in the project', verbose_name='Playbook Files', editable=False, blank=True),
default=[], help_text='List of playbooks found in the project', verbose_name='Playbook Files', editable=False, blank=True
),
), ),
# Job events to stdout # Job events to stdout
migrations.AddField( migrations.AddField(
@@ -539,7 +536,7 @@ class Migration(migrations.Migration):
migrations.AddField( migrations.AddField(
model_name='workflowjob', model_name='workflowjob',
name='survey_passwords', name='survey_passwords',
field=jsonfield.fields.JSONField(default=dict, editable=False, blank=True), field=models.JSONField(default=dict, editable=False, null=True, blank=True),
), ),
migrations.AddField( migrations.AddField(
model_name='workflowjobtemplate', model_name='workflowjobtemplate',
@@ -549,85 +546,83 @@ class Migration(migrations.Migration):
migrations.AddField( migrations.AddField(
model_name='workflowjobtemplate', model_name='workflowjobtemplate',
name='survey_spec', name='survey_spec',
field=jsonfield.fields.JSONField(default=dict, blank=True), field=models.JSONField(default=dict, blank=True),
), ),
# JSON field changes # JSON field changes
migrations.AlterField( migrations.AlterField(
model_name='adhoccommandevent', model_name='adhoccommandevent',
name='event_data', name='event_data',
field=awx.main.fields.JSONField(default=dict, blank=True), field=awx.main.fields.JSONBlob(default=dict, blank=True),
), ),
migrations.AlterField( migrations.AlterField(
model_name='job', model_name='job',
name='artifacts', name='artifacts',
field=awx.main.fields.JSONField(default=dict, editable=False, blank=True), field=models.JSONField(default=dict, editable=False, null=True, blank=True),
), ),
migrations.AlterField( migrations.AlterField(
model_name='job', model_name='job',
name='survey_passwords', name='survey_passwords',
field=awx.main.fields.JSONField(default=dict, editable=False, blank=True), field=models.JSONField(default=dict, editable=False, null=True, blank=True),
), ),
migrations.AlterField( migrations.AlterField(
model_name='jobevent', model_name='jobevent',
name='event_data', name='event_data',
field=awx.main.fields.JSONField(default=dict, blank=True), field=awx.main.fields.JSONBlob(default=dict, blank=True),
), ),
migrations.AlterField( migrations.AlterField(
model_name='jobtemplate', model_name='jobtemplate',
name='survey_spec', name='survey_spec',
field=awx.main.fields.JSONField(default=dict, blank=True), field=models.JSONField(default=dict, blank=True),
), ),
migrations.AlterField( migrations.AlterField(
model_name='notification', model_name='notification',
name='body', name='body',
field=awx.main.fields.JSONField(default=dict, blank=True), field=models.JSONField(default=dict, null=True, blank=True),
), ),
migrations.AlterField( migrations.AlterField(
model_name='notificationtemplate', model_name='notificationtemplate',
name='notification_configuration', name='notification_configuration',
field=awx.main.fields.JSONField(default=dict), field=models.JSONField(default=dict),
), ),
migrations.AlterField( migrations.AlterField(
model_name='project', model_name='project',
name='playbook_files', name='playbook_files',
field=awx.main.fields.JSONField( field=models.JSONField(default=list, help_text='List of playbooks found in the project', verbose_name='Playbook Files', editable=False, blank=True),
default=[], help_text='List of playbooks found in the project', verbose_name='Playbook Files', editable=False, blank=True
),
), ),
migrations.AlterField( migrations.AlterField(
model_name='schedule', model_name='schedule',
name='extra_data', name='extra_data',
field=awx.main.fields.JSONField(default=dict, blank=True), field=models.JSONField(default=dict, null=True, blank=True),
), ),
migrations.AlterField( migrations.AlterField(
model_name='unifiedjob', model_name='unifiedjob',
name='job_env', name='job_env',
field=awx.main.fields.JSONField(default=dict, editable=False, blank=True), field=models.JSONField(default=dict, editable=False, null=True, blank=True),
), ),
migrations.AlterField( migrations.AlterField(
model_name='workflowjob', model_name='workflowjob',
name='survey_passwords', name='survey_passwords',
field=awx.main.fields.JSONField(default=dict, editable=False, blank=True), field=models.JSONField(default=dict, editable=False, null=True, blank=True),
), ),
migrations.AlterField( migrations.AlterField(
model_name='workflowjobnode', model_name='workflowjobnode',
name='ancestor_artifacts', name='ancestor_artifacts',
field=awx.main.fields.JSONField(default=dict, editable=False, blank=True), field=models.JSONField(default=dict, editable=False, null=True, blank=True),
), ),
migrations.AlterField( migrations.AlterField(
model_name='workflowjobnode', model_name='workflowjobnode',
name='char_prompts', name='char_prompts',
field=awx.main.fields.JSONField(default=dict, blank=True), field=models.JSONField(default=dict, null=True, blank=True),
), ),
migrations.AlterField( migrations.AlterField(
model_name='workflowjobtemplate', model_name='workflowjobtemplate',
name='survey_spec', name='survey_spec',
field=awx.main.fields.JSONField(default=dict, blank=True), field=models.JSONField(default=dict, blank=True),
), ),
migrations.AlterField( migrations.AlterField(
model_name='workflowjobtemplatenode', model_name='workflowjobtemplatenode',
name='char_prompts', name='char_prompts',
field=awx.main.fields.JSONField(default=dict, blank=True), field=models.JSONField(default=dict, null=True, blank=True),
), ),
# Job Project Update # Job Project Update
migrations.AddField( migrations.AddField(

View File

@@ -108,14 +108,12 @@ class Migration(migrations.Migration):
migrations.AlterField( migrations.AlterField(
model_name='fact', model_name='fact',
name='facts', name='facts',
field=awx.main.fields.JSONBField( field=models.JSONField(default=dict, help_text='Arbitrary JSON structure of module facts captured at timestamp for a single host.', blank=True),
default=dict, help_text='Arbitrary JSON structure of module facts captured at timestamp for a single host.', blank=True
),
), ),
migrations.AddField( migrations.AddField(
model_name='host', model_name='host',
name='ansible_facts', name='ansible_facts',
field=awx.main.fields.JSONBField(default=dict, help_text='Arbitrary JSON structure of most recent ansible_facts, per-host.', blank=True), field=models.JSONField(default=dict, help_text='Arbitrary JSON structure of most recent ansible_facts, per-host.', blank=True),
), ),
migrations.AddField( migrations.AddField(
model_name='host', model_name='host',
@@ -177,8 +175,8 @@ class Migration(migrations.Migration):
migrations.AddField( migrations.AddField(
model_name='project', model_name='project',
name='inventory_files', name='inventory_files',
field=awx.main.fields.JSONField( field=models.JSONField(
default=[], default=list,
help_text='Suggested list of content that could be Ansible inventory in the project', help_text='Suggested list of content that could be Ansible inventory in the project',
verbose_name='Inventory Files', verbose_name='Inventory Files',
editable=False, editable=False,

View File

@@ -1,8 +1,7 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from __future__ import unicode_literals from __future__ import unicode_literals
from django.db import migrations from django.db import migrations, models
import awx.main.fields
class Migration(migrations.Migration): class Migration(migrations.Migration):
@@ -15,6 +14,6 @@ class Migration(migrations.Migration):
migrations.AddField( migrations.AddField(
model_name='activitystream', model_name='activitystream',
name='setting', name='setting',
field=awx.main.fields.JSONField(default=dict, blank=True), field=models.JSONField(default=dict, null=True, blank=True),
), ),
] ]

View File

@@ -20,7 +20,7 @@ class Migration(migrations.Migration):
migrations.AddField( migrations.AddField(
model_name='schedule', model_name='schedule',
name='char_prompts', name='char_prompts',
field=awx.main.fields.JSONField(default=dict, blank=True), field=models.JSONField(default=dict, null=True, blank=True),
), ),
migrations.AddField( migrations.AddField(
model_name='schedule', model_name='schedule',
@@ -37,7 +37,7 @@ class Migration(migrations.Migration):
migrations.AddField( migrations.AddField(
model_name='schedule', model_name='schedule',
name='survey_passwords', name='survey_passwords',
field=awx.main.fields.JSONField(default=dict, editable=False, blank=True), field=models.JSONField(default=dict, editable=False, null=True, blank=True),
), ),
migrations.AddField( migrations.AddField(
model_name='workflowjobnode', model_name='workflowjobnode',
@@ -47,12 +47,12 @@ class Migration(migrations.Migration):
migrations.AddField( migrations.AddField(
model_name='workflowjobnode', model_name='workflowjobnode',
name='extra_data', name='extra_data',
field=awx.main.fields.JSONField(default=dict, blank=True), field=models.JSONField(default=dict, null=True, blank=True),
), ),
migrations.AddField( migrations.AddField(
model_name='workflowjobnode', model_name='workflowjobnode',
name='survey_passwords', name='survey_passwords',
field=awx.main.fields.JSONField(default=dict, editable=False, blank=True), field=models.JSONField(default=dict, editable=False, null=True, blank=True),
), ),
migrations.AddField( migrations.AddField(
model_name='workflowjobtemplatenode', model_name='workflowjobtemplatenode',
@@ -62,12 +62,12 @@ class Migration(migrations.Migration):
migrations.AddField( migrations.AddField(
model_name='workflowjobtemplatenode', model_name='workflowjobtemplatenode',
name='extra_data', name='extra_data',
field=awx.main.fields.JSONField(default=dict, blank=True), field=models.JSONField(default=dict, null=True, blank=True),
), ),
migrations.AddField( migrations.AddField(
model_name='workflowjobtemplatenode', model_name='workflowjobtemplatenode',
name='survey_passwords', name='survey_passwords',
field=awx.main.fields.JSONField(default=dict, editable=False, blank=True), field=models.JSONField(default=dict, editable=False, null=True, blank=True),
), ),
# Run data migration before removing the old credential field # Run data migration before removing the old credential field
migrations.RunPython(migration_utils.set_current_apps_for_migrations, migrations.RunPython.noop), migrations.RunPython(migration_utils.set_current_apps_for_migrations, migrations.RunPython.noop),
@@ -85,9 +85,9 @@ class Migration(migrations.Migration):
name='JobLaunchConfig', name='JobLaunchConfig',
fields=[ fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('extra_data', awx.main.fields.JSONField(blank=True, default=dict)), ('extra_data', models.JSONField(blank=True, null=True, default=dict)),
('survey_passwords', awx.main.fields.JSONField(blank=True, default=dict, editable=False)), ('survey_passwords', models.JSONField(blank=True, null=True, default=dict, editable=False)),
('char_prompts', awx.main.fields.JSONField(blank=True, default=dict)), ('char_prompts', models.JSONField(blank=True, null=True, default=dict)),
('credentials', models.ManyToManyField(related_name='joblaunchconfigs', to='main.Credential')), ('credentials', models.ManyToManyField(related_name='joblaunchconfigs', to='main.Credential')),
( (
'inventory', 'inventory',

View File

@@ -2,10 +2,11 @@
# Generated by Django 1.11.7 on 2017-12-14 15:13 # Generated by Django 1.11.7 on 2017-12-14 15:13
from __future__ import unicode_literals from __future__ import unicode_literals
import awx.main.fields
from django.db import migrations, models from django.db import migrations, models
import django.db.models.deletion import django.db.models.deletion
import awx.main.fields
class Migration(migrations.Migration): class Migration(migrations.Migration):
@@ -20,7 +21,7 @@ class Migration(migrations.Migration):
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('created', models.DateTimeField(default=None, editable=False)), ('created', models.DateTimeField(default=None, editable=False)),
('modified', models.DateTimeField(default=None, editable=False)), ('modified', models.DateTimeField(default=None, editable=False)),
('event_data', awx.main.fields.JSONField(blank=True, default=dict)), ('event_data', awx.main.fields.JSONBlob(blank=True, default=dict)),
('uuid', models.CharField(default='', editable=False, max_length=1024)), ('uuid', models.CharField(default='', editable=False, max_length=1024)),
('counter', models.PositiveIntegerField(default=0, editable=False)), ('counter', models.PositiveIntegerField(default=0, editable=False)),
('stdout', models.TextField(default='', editable=False)), ('stdout', models.TextField(default='', editable=False)),
@@ -84,7 +85,7 @@ class Migration(migrations.Migration):
max_length=100, max_length=100,
), ),
), ),
('event_data', awx.main.fields.JSONField(blank=True, default=dict)), ('event_data', awx.main.fields.JSONBlob(blank=True, default=dict)),
('failed', models.BooleanField(default=False, editable=False)), ('failed', models.BooleanField(default=False, editable=False)),
('changed', models.BooleanField(default=False, editable=False)), ('changed', models.BooleanField(default=False, editable=False)),
('uuid', models.CharField(default='', editable=False, max_length=1024)), ('uuid', models.CharField(default='', editable=False, max_length=1024)),
@@ -114,7 +115,7 @@ class Migration(migrations.Migration):
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('created', models.DateTimeField(default=None, editable=False)), ('created', models.DateTimeField(default=None, editable=False)),
('modified', models.DateTimeField(default=None, editable=False)), ('modified', models.DateTimeField(default=None, editable=False)),
('event_data', awx.main.fields.JSONField(blank=True, default=dict)), ('event_data', awx.main.fields.JSONBlob(blank=True, default=dict)),
('uuid', models.CharField(default='', editable=False, max_length=1024)), ('uuid', models.CharField(default='', editable=False, max_length=1024)),
('counter', models.PositiveIntegerField(default=0, editable=False)), ('counter', models.PositiveIntegerField(default=0, editable=False)),
('stdout', models.TextField(default='', editable=False)), ('stdout', models.TextField(default='', editable=False)),

View File

@@ -3,7 +3,6 @@ from __future__ import unicode_literals
from django.db import migrations, models from django.db import migrations, models
from decimal import Decimal from decimal import Decimal
import awx.main.fields
class Migration(migrations.Migration): class Migration(migrations.Migration):
@@ -16,8 +15,8 @@ class Migration(migrations.Migration):
migrations.AddField( migrations.AddField(
model_name='instancegroup', model_name='instancegroup',
name='policy_instance_list', name='policy_instance_list',
field=awx.main.fields.JSONField( field=models.JSONField(
default=[], help_text='List of exact-match Instances that will always be automatically assigned to this group', blank=True default=list, help_text='List of exact-match Instances that will always be automatically assigned to this group', blank=True
), ),
), ),
migrations.AddField( migrations.AddField(

View File

@@ -2,9 +2,7 @@
# Generated by Django 1.11.11 on 2018-05-21 19:51 # Generated by Django 1.11.11 on 2018-05-21 19:51
from __future__ import unicode_literals from __future__ import unicode_literals
import awx.main.fields from django.db import models, migrations
import awx.main.models.activity_stream
from django.db import migrations
class Migration(migrations.Migration): class Migration(migrations.Migration):
@@ -17,6 +15,6 @@ class Migration(migrations.Migration):
migrations.AddField( migrations.AddField(
model_name='activitystream', model_name='activitystream',
name='deleted_actor', name='deleted_actor',
field=awx.main.fields.JSONField(null=True), field=models.JSONField(null=True),
), ),
] ]

View File

@@ -17,7 +17,7 @@ class Migration(migrations.Migration):
migrations.AddField( migrations.AddField(
model_name='workflowjob', model_name='workflowjob',
name='char_prompts', name='char_prompts',
field=awx.main.fields.JSONField(blank=True, default=dict), field=models.JSONField(blank=True, null=True, default=dict),
), ),
migrations.AddField( migrations.AddField(
model_name='workflowjob', model_name='workflowjob',

View File

@@ -4,7 +4,6 @@ from __future__ import unicode_literals
from django.db import migrations, models from django.db import migrations, models
import awx.main.fields
import awx.main.models.notifications import awx.main.models.notifications
@@ -18,7 +17,7 @@ class Migration(migrations.Migration):
migrations.AddField( migrations.AddField(
model_name='notificationtemplate', model_name='notificationtemplate',
name='messages', name='messages',
field=awx.main.fields.JSONField( field=models.JSONField(
default=awx.main.models.notifications.NotificationTemplate.default_messages, default=awx.main.models.notifications.NotificationTemplate.default_messages,
help_text='Optional custom messages for notification template.', help_text='Optional custom messages for notification template.',
null=True, null=True,

View File

@@ -24,7 +24,7 @@ class Migration(migrations.Migration):
migrations.AddField( migrations.AddField(
model_name='workflowjobtemplate', model_name='workflowjobtemplate',
name='char_prompts', name='char_prompts',
field=awx.main.fields.JSONField(blank=True, default=dict), field=models.JSONField(blank=True, null=True, default=dict),
), ),
migrations.AlterField( migrations.AlterField(
model_name='joblaunchconfig', model_name='joblaunchconfig',

View File

@@ -1,7 +1,6 @@
# Generated by Django 2.2.16 on 2021-02-16 20:27 # Generated by Django 2.2.16 on 2021-02-16 20:27
import awx.main.fields from django.db import migrations, models
from django.db import migrations
class Migration(migrations.Migration): class Migration(migrations.Migration):
@@ -14,7 +13,7 @@ class Migration(migrations.Migration):
migrations.AddField( migrations.AddField(
model_name='unifiedjob', model_name='unifiedjob',
name='installed_collections', name='installed_collections',
field=awx.main.fields.JSONBField( field=models.JSONField(
blank=True, default=dict, editable=False, help_text='The Collections names and versions installed in the execution environment.' blank=True, default=dict, editable=False, help_text='The Collections names and versions installed in the execution environment.'
), ),
), ),

View File

@@ -2,7 +2,6 @@ from django.db import (
migrations, migrations,
models, models,
) )
import jsonfield.fields
import awx.main.fields import awx.main.fields
from awx.main.migrations import _save_password_keys from awx.main.migrations import _save_password_keys
@@ -30,7 +29,7 @@ SQUASHED_30 = {
migrations.AddField( migrations.AddField(
model_name='job', model_name='job',
name='survey_passwords', name='survey_passwords',
field=jsonfield.fields.JSONField(default=dict, editable=False, blank=True), field=models.JSONField(default=dict, editable=False, null=True, blank=True),
), ),
], ],
'0031_v302_migrate_survey_passwords': [ '0031_v302_migrate_survey_passwords': [

View File

@@ -3,7 +3,6 @@
# AWX # AWX
from awx.api.versioning import reverse from awx.api.versioning import reverse
from awx.main.fields import JSONField
from awx.main.models.base import accepts_json from awx.main.models.base import accepts_json
# Django # Django
@@ -36,7 +35,7 @@ class ActivityStream(models.Model):
operation = models.CharField(max_length=13, choices=OPERATION_CHOICES) operation = models.CharField(max_length=13, choices=OPERATION_CHOICES)
timestamp = models.DateTimeField(auto_now_add=True) timestamp = models.DateTimeField(auto_now_add=True)
changes = accepts_json(models.TextField(blank=True)) changes = accepts_json(models.TextField(blank=True))
deleted_actor = JSONField(null=True) deleted_actor = models.JSONField(null=True)
action_node = models.CharField( action_node = models.CharField(
blank=True, blank=True,
default='', default='',
@@ -84,7 +83,7 @@ class ActivityStream(models.Model):
o_auth2_application = models.ManyToManyField("OAuth2Application", blank=True) o_auth2_application = models.ManyToManyField("OAuth2Application", blank=True)
o_auth2_access_token = models.ManyToManyField("OAuth2AccessToken", blank=True) o_auth2_access_token = models.ManyToManyField("OAuth2AccessToken", blank=True)
setting = JSONField(blank=True) setting = models.JSONField(default=dict, null=True, blank=True)
def __str__(self): def __str__(self):
operation = self.operation if 'operation' in self.__dict__ else '_delayed_' operation = self.operation if 'operation' in self.__dict__ else '_delayed_'

View File

@@ -15,8 +15,8 @@ from django.utils.encoding import force_str
from awx.api.versioning import reverse from awx.api.versioning import reverse
from awx.main import consumers from awx.main import consumers
from awx.main.fields import JSONBlob
from awx.main.managers import DeferJobCreatedManager from awx.main.managers import DeferJobCreatedManager
from awx.main.fields import JSONField
from awx.main.constants import MINIMAL_EVENTS from awx.main.constants import MINIMAL_EVENTS
from awx.main.models.base import CreatedModifiedModel from awx.main.models.base import CreatedModifiedModel
from awx.main.utils import ignore_inventory_computed_fields, camelcase_to_underscore from awx.main.utils import ignore_inventory_computed_fields, camelcase_to_underscore
@@ -209,10 +209,7 @@ class BasePlaybookEvent(CreatedModifiedModel):
max_length=100, max_length=100,
choices=EVENT_CHOICES, choices=EVENT_CHOICES,
) )
event_data = JSONField( event_data = JSONBlob(default=dict, blank=True)
blank=True,
default=dict,
)
failed = models.BooleanField( failed = models.BooleanField(
default=False, default=False,
editable=False, editable=False,
@@ -648,10 +645,7 @@ class BaseCommandEvent(CreatedModifiedModel):
class Meta: class Meta:
abstract = True abstract = True
event_data = JSONField( event_data = JSONBlob(default=dict, blank=True)
blank=True,
default=dict,
)
uuid = models.CharField( uuid = models.CharField(
max_length=1024, max_length=1024,
default='', default='',

View File

@@ -19,7 +19,6 @@ from solo.models import SingletonModel
from awx import __version__ as awx_application_version from awx import __version__ as awx_application_version
from awx.api.versioning import reverse from awx.api.versioning import reverse
from awx.main.managers import InstanceManager, InstanceGroupManager, UUID_DEFAULT from awx.main.managers import InstanceManager, InstanceGroupManager, UUID_DEFAULT
from awx.main.fields import JSONField
from awx.main.constants import JOB_FOLDER_PREFIX from awx.main.constants import JOB_FOLDER_PREFIX
from awx.main.models.base import BaseModel, HasEditsMixin, prevent_search from awx.main.models.base import BaseModel, HasEditsMixin, prevent_search
from awx.main.models.unified_jobs import UnifiedJob from awx.main.models.unified_jobs import UnifiedJob
@@ -322,8 +321,8 @@ class InstanceGroup(HasPolicyEditsMixin, BaseModel, RelatedJobsMixin):
) )
policy_instance_percentage = models.IntegerField(default=0, help_text=_("Percentage of Instances to automatically assign to this group")) policy_instance_percentage = models.IntegerField(default=0, help_text=_("Percentage of Instances to automatically assign to this group"))
policy_instance_minimum = models.IntegerField(default=0, help_text=_("Static minimum number of Instances to automatically assign to this group")) policy_instance_minimum = models.IntegerField(default=0, help_text=_("Static minimum number of Instances to automatically assign to this group"))
policy_instance_list = JSONField( policy_instance_list = models.JSONField(
default=[], blank=True, help_text=_("List of exact-match Instances that will always be automatically assigned to this group") default=list, blank=True, help_text=_("List of exact-match Instances that will always be automatically assigned to this group")
) )
POLICY_FIELDS = frozenset(('policy_instance_list', 'policy_instance_minimum', 'policy_instance_percentage')) POLICY_FIELDS = frozenset(('policy_instance_list', 'policy_instance_minimum', 'policy_instance_percentage'))

View File

@@ -29,7 +29,6 @@ from awx.main.constants import CLOUD_PROVIDERS
from awx.main.consumers import emit_channel_notification from awx.main.consumers import emit_channel_notification
from awx.main.fields import ( from awx.main.fields import (
ImplicitRoleField, ImplicitRoleField,
JSONBField,
SmartFilterField, SmartFilterField,
OrderedManyToManyField, OrderedManyToManyField,
) )
@@ -488,7 +487,7 @@ class Host(CommonModelNameNotUnique, RelatedJobsMixin):
editable=False, editable=False,
help_text=_('Inventory source(s) that created or modified this host.'), help_text=_('Inventory source(s) that created or modified this host.'),
) )
ansible_facts = JSONBField( ansible_facts = models.JSONField(
blank=True, blank=True,
default=dict, default=dict,
help_text=_('Arbitrary JSON structure of most recent ansible_facts, per-host.'), help_text=_('Arbitrary JSON structure of most recent ansible_facts, per-host.'),

View File

@@ -44,7 +44,7 @@ from awx.main.models.notifications import (
JobNotificationMixin, JobNotificationMixin,
) )
from awx.main.utils import parse_yaml_or_json, getattr_dne, NullablePromptPseudoField from awx.main.utils import parse_yaml_or_json, getattr_dne, NullablePromptPseudoField
from awx.main.fields import ImplicitRoleField, JSONField, AskForField from awx.main.fields import ImplicitRoleField, AskForField
from awx.main.models.mixins import ( from awx.main.models.mixins import (
ResourceMixin, ResourceMixin,
SurveyJobTemplateMixin, SurveyJobTemplateMixin,
@@ -546,9 +546,10 @@ class Job(UnifiedJob, JobOptions, SurveyJobMixin, JobNotificationMixin, TaskMana
editable=False, editable=False,
through='JobHostSummary', through='JobHostSummary',
) )
artifacts = JSONField( artifacts = models.JSONField(
blank=True,
default=dict, default=dict,
null=True,
blank=True,
editable=False, editable=False,
) )
scm_revision = models.CharField( scm_revision = models.CharField(
@@ -885,7 +886,7 @@ class LaunchTimeConfigBase(BaseModel):
) )
# All standard fields are stored in this dictionary field # All standard fields are stored in this dictionary field
# This is a solution to the nullable CharField problem, specific to prompting # This is a solution to the nullable CharField problem, specific to prompting
char_prompts = JSONField(blank=True, default=dict) char_prompts = models.JSONField(default=dict, null=True, blank=True)
def prompts_dict(self, display=False): def prompts_dict(self, display=False):
data = {} data = {}
@@ -938,12 +939,13 @@ class LaunchTimeConfig(LaunchTimeConfigBase):
abstract = True abstract = True
# Special case prompting fields, even more special than the other ones # Special case prompting fields, even more special than the other ones
extra_data = JSONField(blank=True, default=dict) extra_data = models.JSONField(default=dict, null=True, blank=True)
survey_passwords = prevent_search( survey_passwords = prevent_search(
JSONField( models.JSONField(
blank=True,
default=dict, default=dict,
editable=False, editable=False,
null=True,
blank=True,
) )
) )
# Credentials needed for non-unified job / unified JT models # Credentials needed for non-unified job / unified JT models

View File

@@ -24,7 +24,7 @@ from awx.main.utils import parse_yaml_or_json, get_custom_venv_choices, get_lice
from awx.main.utils.execution_environments import get_default_execution_environment from awx.main.utils.execution_environments import get_default_execution_environment
from awx.main.utils.encryption import decrypt_value, get_encryption_key, is_encrypted from awx.main.utils.encryption import decrypt_value, get_encryption_key, is_encrypted
from awx.main.utils.polymorphic import build_polymorphic_ctypes_map from awx.main.utils.polymorphic import build_polymorphic_ctypes_map
from awx.main.fields import JSONField, AskForField from awx.main.fields import AskForField
from awx.main.constants import ACTIVE_STATES from awx.main.constants import ACTIVE_STATES
@@ -103,12 +103,7 @@ class SurveyJobTemplateMixin(models.Model):
survey_enabled = models.BooleanField( survey_enabled = models.BooleanField(
default=False, default=False,
) )
survey_spec = prevent_search( survey_spec = prevent_search(models.JSONField(default=dict, blank=True))
JSONField(
blank=True,
default=dict,
)
)
ask_variables_on_launch = AskForField(blank=True, default=False, allows_field='extra_vars') ask_variables_on_launch = AskForField(blank=True, default=False, allows_field='extra_vars')
def survey_password_variables(self): def survey_password_variables(self):
@@ -370,10 +365,11 @@ class SurveyJobMixin(models.Model):
abstract = True abstract = True
survey_passwords = prevent_search( survey_passwords = prevent_search(
JSONField( models.JSONField(
blank=True,
default=dict, default=dict,
editable=False, editable=False,
null=True,
blank=True,
) )
) )

View File

@@ -28,7 +28,6 @@ from awx.main.notifications.mattermost_backend import MattermostBackend
from awx.main.notifications.grafana_backend import GrafanaBackend from awx.main.notifications.grafana_backend import GrafanaBackend
from awx.main.notifications.rocketchat_backend import RocketChatBackend from awx.main.notifications.rocketchat_backend import RocketChatBackend
from awx.main.notifications.irc_backend import IrcBackend from awx.main.notifications.irc_backend import IrcBackend
from awx.main.fields import JSONField
logger = logging.getLogger('awx.main.models.notifications') logger = logging.getLogger('awx.main.models.notifications')
@@ -70,12 +69,12 @@ class NotificationTemplate(CommonModelNameNotUnique):
choices=NOTIFICATION_TYPE_CHOICES, choices=NOTIFICATION_TYPE_CHOICES,
) )
notification_configuration = prevent_search(JSONField(blank=False)) notification_configuration = prevent_search(models.JSONField(default=dict))
def default_messages(): def default_messages():
return {'started': None, 'success': None, 'error': None, 'workflow_approval': None} return {'started': None, 'success': None, 'error': None, 'workflow_approval': None}
messages = JSONField(null=True, blank=True, default=default_messages, help_text=_('Optional custom messages for notification template.')) messages = models.JSONField(null=True, blank=True, default=default_messages, help_text=_('Optional custom messages for notification template.'))
def has_message(self, condition): def has_message(self, condition):
potential_template = self.messages.get(condition, {}) potential_template = self.messages.get(condition, {})
@@ -237,7 +236,7 @@ class Notification(CreatedModifiedModel):
default='', default='',
editable=False, editable=False,
) )
body = JSONField(blank=True) body = models.JSONField(default=dict, null=True, blank=True)
def get_absolute_url(self, request=None): def get_absolute_url(self, request=None):
return reverse('api:notification_detail', kwargs={'pk': self.pk}, request=request) return reverse('api:notification_detail', kwargs={'pk': self.pk}, request=request)

View File

@@ -38,7 +38,6 @@ from awx.main.models.rbac import (
ROLE_SINGLETON_SYSTEM_ADMINISTRATOR, ROLE_SINGLETON_SYSTEM_ADMINISTRATOR,
ROLE_SINGLETON_SYSTEM_AUDITOR, ROLE_SINGLETON_SYSTEM_AUDITOR,
) )
from awx.main.fields import JSONField
__all__ = ['Project', 'ProjectUpdate'] __all__ = ['Project', 'ProjectUpdate']
@@ -294,17 +293,17 @@ class Project(UnifiedJobTemplate, ProjectOptions, ResourceMixin, CustomVirtualEn
help_text=_('The last revision fetched by a project update'), help_text=_('The last revision fetched by a project update'),
) )
playbook_files = JSONField( playbook_files = models.JSONField(
default=list,
blank=True, blank=True,
default=[],
editable=False, editable=False,
verbose_name=_('Playbook Files'), verbose_name=_('Playbook Files'),
help_text=_('List of playbooks found in the project'), help_text=_('List of playbooks found in the project'),
) )
inventory_files = JSONField( inventory_files = models.JSONField(
default=list,
blank=True, blank=True,
default=[],
editable=False, editable=False,
verbose_name=_('Inventory Files'), verbose_name=_('Inventory Files'),
help_text=_('Suggested list of content that could be Ansible inventory in the project'), help_text=_('Suggested list of content that could be Ansible inventory in the project'),

View File

@@ -54,7 +54,7 @@ from awx.main.utils import polymorphic
from awx.main.constants import ACTIVE_STATES, CAN_CANCEL from awx.main.constants import ACTIVE_STATES, CAN_CANCEL
from awx.main.redact import UriCleaner, REPLACE_STR from awx.main.redact import UriCleaner, REPLACE_STR
from awx.main.consumers import emit_channel_notification from awx.main.consumers import emit_channel_notification
from awx.main.fields import JSONField, JSONBField, AskForField, OrderedManyToManyField from awx.main.fields import AskForField, OrderedManyToManyField
__all__ = ['UnifiedJobTemplate', 'UnifiedJob', 'StdoutMaxBytesExceeded'] __all__ = ['UnifiedJobTemplate', 'UnifiedJob', 'StdoutMaxBytesExceeded']
@@ -653,9 +653,10 @@ class UnifiedJob(
editable=False, editable=False,
) )
job_env = prevent_search( job_env = prevent_search(
JSONField( models.JSONField(
blank=True,
default=dict, default=dict,
null=True,
blank=True,
editable=False, editable=False,
) )
) )
@@ -704,7 +705,7 @@ class UnifiedJob(
'Credential', 'Credential',
related_name='%(class)ss', related_name='%(class)ss',
) )
installed_collections = JSONBField( installed_collections = models.JSONField(
blank=True, blank=True,
default=dict, default=dict,
editable=False, editable=False,

View File

@@ -40,7 +40,6 @@ from awx.main.models.mixins import (
from awx.main.models.jobs import LaunchTimeConfigBase, LaunchTimeConfig, JobTemplate from awx.main.models.jobs import LaunchTimeConfigBase, LaunchTimeConfig, JobTemplate
from awx.main.models.credential import Credential from awx.main.models.credential import Credential
from awx.main.redact import REPLACE_STR from awx.main.redact import REPLACE_STR
from awx.main.fields import JSONField
from awx.main.utils import schedule_task_manager from awx.main.utils import schedule_task_manager
@@ -232,9 +231,10 @@ class WorkflowJobNode(WorkflowNodeBase):
default=None, default=None,
on_delete=models.CASCADE, on_delete=models.CASCADE,
) )
ancestor_artifacts = JSONField( ancestor_artifacts = models.JSONField(
blank=True,
default=dict, default=dict,
null=True,
blank=True,
editable=False, editable=False,
) )
do_not_run = models.BooleanField( do_not_run = models.BooleanField(

View File

@@ -180,8 +180,8 @@ def mk_job_template(
jt.project = project jt.project = project
jt.survey_spec = spec if spec is not None:
if jt.survey_spec is not None: jt.survey_spec = spec
jt.survey_enabled = True jt.survey_enabled = True
if persisted: if persisted:
@@ -212,8 +212,8 @@ def mk_workflow_job_template(name, extra_vars='', spec=None, organization=None,
wfjt = WorkflowJobTemplate(name=name, extra_vars=extra_vars, organization=organization, webhook_service=webhook_service) wfjt = WorkflowJobTemplate(name=name, extra_vars=extra_vars, organization=organization, webhook_service=webhook_service)
wfjt.survey_spec = spec if spec:
if wfjt.survey_spec: wfjt.survey_spec = spec
wfjt.survey_enabled = True wfjt.survey_enabled = True
if persisted: if persisted:

View File

@@ -15,7 +15,6 @@ from django.core.serializers.json import DjangoJSONEncoder
from django.db.backends.sqlite3.base import SQLiteCursorWrapper from django.db.backends.sqlite3.base import SQLiteCursorWrapper
# AWX # AWX
from awx.main.fields import JSONBField
from awx.main.models.projects import Project from awx.main.models.projects import Project
from awx.main.models.ha import Instance from awx.main.models.ha import Instance
@@ -755,11 +754,6 @@ def get_db_prep_save(self, value, connection, **kwargs):
return value return value
@pytest.fixture
def monkeypatch_jsonbfield_get_db_prep_save(mocker):
JSONBField.get_db_prep_save = get_db_prep_save
@pytest.fixture @pytest.fixture
def oauth_application(admin): def oauth_application(admin):
return Application.objects.create(name='test app', user=admin, client_type='confidential', authorization_grant_type='password') return Application.objects.create(name='test app', user=admin, client_type='confidential', authorization_grant_type='password')

View File

@@ -4,7 +4,6 @@ from unittest import mock
# AWX # AWX
from awx.main.utils.filters import SmartFilter, ExternalLoggerEnabled from awx.main.utils.filters import SmartFilter, ExternalLoggerEnabled
from awx.main.models import Host
# Django # Django
from django.db.models import Q from django.db.models import Q
@@ -219,39 +218,6 @@ class TestSmartFilterQueryFromString:
assert str(q) == str(q_expected) assert str(q) == str(q_expected)
class TestSmartFilterQueryFromStringNoDB:
@pytest.mark.parametrize(
"filter_string,q_expected",
[
(
'ansible_facts__a="true" and ansible_facts__b="true" and ansible_facts__c="true"',
(
Q(**{u"ansible_facts__contains": {u"a": u"true"}})
& Q(**{u"ansible_facts__contains": {u"b": u"true"}})
& Q(**{u"ansible_facts__contains": {u"c": u"true"}})
),
),
(
'ansible_facts__a="true" or ansible_facts__b="true" or ansible_facts__c="true"',
(
Q(**{u"ansible_facts__contains": {u"a": u"true"}})
| Q(**{u"ansible_facts__contains": {u"b": u"true"}})
| Q(**{u"ansible_facts__contains": {u"c": u"true"}})
),
),
('search=foo', Q(Q(**{u"description__icontains": u"foo"}) | Q(**{u"name__icontains": u"foo"}))),
(
'search=foo and ansible_facts__a="null"',
Q(Q(**{u"description__icontains": u"foo"}) | Q(**{u"name__icontains": u"foo"})) & Q(**{u"ansible_facts__contains": {u"a": u"\"null\""}}),
),
('name=foo or name=bar and name=foobar', Q(name="foo") | Q(name="bar") & Q(name="foobar")),
],
)
def test_does_not_invoke_db(self, filter_string, q_expected):
q = SmartFilter.query_from_string(filter_string)
assert str(q.query) == str(Host.objects.filter(q_expected).query)
''' '''
#('"facts__quoted_val"="f\"oo"', 1), #('"facts__quoted_val"="f\"oo"', 1),
#('facts__facts__arr[]="foo"', 1), #('facts__facts__arr[]="foo"', 1),

View File

@@ -188,13 +188,11 @@ class SmartFilter(object):
''' '''
def _json_path_to_contains(self, k, v): def _json_path_to_contains(self, k, v):
from awx.main.fields import JSONBField # avoid a circular import
if not k.startswith(SmartFilter.SEARCHABLE_RELATIONSHIP): if not k.startswith(SmartFilter.SEARCHABLE_RELATIONSHIP):
v = self.strip_quotes_traditional_logic(v) v = self.strip_quotes_traditional_logic(v)
return (k, v) return (k, v)
for match in JSONBField.get_lookups().keys(): for match in models.JSONField.get_lookups().keys():
match = '__{}'.format(match) match = '__{}'.format(match)
if k.endswith(match): if k.endswith(match):
if match == '__exact': if match == '__exact':

View File

@@ -105,17 +105,6 @@ Upgrading to 4.0.0 causes error because imports changed.
ImportError: cannot import name 'KeyVaultClient' ImportError: cannot import name 'KeyVaultClient'
``` ```
### django-jsonfield
Instead of calling a `loads()` operation, the returned value is casted into
a string in some cases, introduced in the change:
https://github.com/adamchainz/django-jsonfield/pull/14
This breaks a very large amount of AWX code that assumes these fields
are returned as dicts. Upgrading this library will require a refactor
to accommodate this change.
### pip and setuptools ### pip and setuptools
The offline installer needs to have functionality confirmed before upgrading these. The offline installer needs to have functionality confirmed before upgrading these.