Иногда приходятся сталкиваться с задачей хранения JSON данных в моделях Django. В этом нам очень хорошо помогает сама Django, так как она поддерживает тип данных БД на основе JSON. JSONField() может быть назначен атрибутам models для хранения данных на основе JSON. Это очень простой путь решения подобных задач, но у него есть одна большая побочная проблема. При большом использование полей JSONField в проекте ломается консистентность данных, и возникает проблема контролирования структуры всех данных в БД. Если на это не обращать внимание через некоторое время в БД может быть записано все что угодно и это значительно усложнит работу с проектом.
К счастью, есть отличный инструмент для проверки данных JSON. Он называется json-schema и имеет реализации на нескольких языках, включая Python. Он может проверять типы данных, убедиться, что строки соответствуют перечисленным атрибутам и разрешать/запрещать дополнительные свойства, которые могут не требовать какой-либо проверки. Вы также можете описывать разрешенные типы данных, например, как простые int, string, так и составные массивы, объекты, где каждый элемент имеет заданное количество полей с разными типами данных.
Создадим пример структуры данных используемой для хранения SEO данных веб страницы на двух языках:
{ "en": { "image": { "src": None, }, "title": None, "description": None, "keywords": None, }, "ru": { "image": { "src": None, }, "title": None, "description": None, "keywords": None, }, }
Вот пример схемы json-schema, по которой мы можем проверить наши данные.
schema': 'http://json-schema.org/draft-07/schema#', 'type': 'object', 'properties': { 'ru': { 'type': ["object"], 'properties': { 'image': { 'type': ["object"], 'properties': { 'src': { 'type': ["string", "null"], }, }, 'required': ['src', ], 'additionalProperties': False, }, 'title': { 'type': ["string", "null"] }, 'keywords': { 'type': ["string", "null"] }, 'description': { 'type': ["string", "null"] }, }, 'required': ['image', 'title', 'keywords', 'description'], 'additionalProperties': False, }, 'en': { 'type': ["object"], 'properties': { 'image': { 'type': ["object"], 'properties': { 'src': { 'type': ["string", "null"], }, }, 'required': ['src', ], 'additionalProperties': False, }, 'title': { 'type': ["string", "null"] }, 'keywords': { 'type': ["string", "null"] }, 'description': { 'type': ["string", "null"] }, }, 'required': ['image', 'title', 'keywords', 'description'], 'additionalProperties': False, }, }, 'required': ['ru', 'en'], 'additionalProperties': False,
Из имен атрибутов можно догадаться о их предназначение. За более подробным описание обратитесь к официальной документации, там все просто.
Далее нам нужно связать нашу схему с полем в котором будут храниться данные. Мы рассмотрим два варианта.
В этом варианте, мы расширим JSONField (), предоставляемый Django, и добавим в него функцию проверки перед сохранением.
from jsonschema import validate, exceptions as jsonschema_exceptions from django.core import exceptions from django.contrib.postgres.fields import JSONField class JSONSchemaField(JSONField): def __init__(self, *args, **kwargs): self.schema = kwargs.pop('schema', None) super().__init__(*args, **kwargs) @property def _schema_data(self): model_file = inspect.getfile(self.model) dirname = os.path.dirname(model_file) # schema file related to model.py path p = os.path.join(dirname, self.schema) with open(p, 'r') as file: return json.loads(file.read()) def _validate_schema(self, value): # Disable validation when migrations are faked if self.model.__module__ == '__fake__': return True try: status = validate(value, self._schema_data) except jsonschema_exceptions.ValidationError as e: raise exceptions.ValidationError(e.message, code='invalid') return status def validate(self, value, model_instance): super().validate(value, model_instance) self._validate_schema(value) def pre_save(self, model_instance, add): value = super().pre_save(model_instance, add) if value and not self.null: self._validate_schema(value) return value
Здесь мы используем реализацию jsonschema, которая проверяет наши данные по схеме, такой как определенная выше. В конструкторе мы ожидаем путь к файлу json, который затем загружаем в память как свойство в методе _schema_data. Метод pre_save обеспечивает выполнение проверки перед сохранением экземпляра модели.
Использование этого поля довольно просто. Нам нужно будет определить схему и сохранить ее, прежде чем указывать относительный путь к ней в качестве аргумента:
class Page(models.Model): title = models.CharField(max_length=256) content = models.TextField() seo = JSONSchemaField( schema='schemas/jsonschema.example.json', default=dict, blank=True)
Теперь, если мы попытаемся сохранить json, который не соответствует схеме, определенной выше, будет сгенерирована ошибка ValidationError.
Для него создадим класс валидатора
import django from django.core.validators import BaseValidator import jsonschema class JSONSchemaValidator(BaseValidator): def compare(self, input_value, schema): try: jsonschema.validate(input_value, schema) except jsonschema.exceptions.ValidationError: raise django.core.exceptions.ValidationError( '%(value)s failed JSON schema check', params={'value': input_value})
Далее используем его при определение поля
from common.validators import ( JSONSchemaValidator, SEO_JSON_FIELD_SCHEMA, ) class Page(models.Model): title = models.CharField(max_length=256) content = models.TextField() seo = JSONSchemaField( default=dict, blank=True, validators=[JSONSchemaValidator(limit_value=SEO_JSON_FIELD_SCHEMA)], )
Этот вариант содержит значительно меньше кода, но у него и меньше гибкости.
Использование jsonschema отличный способ автоматизировать проверку json вместо того, чтобы делать это в ручную в сериализаторах или где-либо еще. Мы можем пойти дальше и создать настраиваемое свойство схемы, которое может отличается для каждого экземпляра модели. Для этого нам нужно будет передать схему в поле при сохранении экземпляра и вручную вызвать наш метод проверки.
Краткий перевод: https://vuejs.org/guide/components/v-model.html Основное использование v-model используется для реализации двусторонней привязки в компоненте. Начиная с Vue…
Сегодня мы рады объявить о выпуске Vue 3.4 «🏀 Slam Dunk»! Этот выпуск включает в…
Vue.js — это универсальный и адаптируемый фреймворк. Благодаря своей отличительной архитектуре и системе реактивности Vue…
Недавно, у меня истек сертификат и пришлось заказывать новый и затем устанавливать на хостинг с…
Каким бы ни было ваше мнение о JavaScript, но всем известно, что работа с датами…
Все, кто следит за последними событиями в мире адаптивного дизайна, согласятся, что введение контейнерных запросов…
View Comments
Мне для json больше нравится pydantic.