Как сохранить порядок ключей в JSONField Django?
Проблема в том, что в дальнейшем при чтении данного json не сохраняется первоначальный порядок ключей, а в данном случае это очень важно.
Попробовал переопределить save в модели таким образом, не помогло:
def set_ordered_json(self, data): ordered_data = OrderedDict(data) self.raw_json = ordered_data def save(self, *args, **kwargs): if self.raw_json: self.set_ordered_json(self.raw_json) super(Mymodelname, self).save(*args, **kwargs)
Может кто-то сталкивался с подобным?
Версия Django 4.2.6
БД postgres:12.0-alpine
- Вопрос задан 08 нояб. 2023
- 117 просмотров
Комментировать
Решения вопроса 0
Ответы на вопрос 1
Everything_is_bad @Everything_is_bad
Перестаньте уже хранить порядок ключей в dict (хотелка такого с большой вероятностью проблема в архитектуре), хотите порядок положите ключ-значение в list (а не в OrderedDict, у тебя всё таки json, либо писать сериализатор для json)
Ответ написан 08 нояб. 2023
Нравится 2 10 комментариев
duff89 @duff89 Автор вопроса
Ну такое себе, с таким же успехом можно хранить json как простой текст, а потом при необходимости перегонять его обратно в json. Костыли какие-то
Everything_is_bad @Everything_is_bad
Костыли какие-то
вот как раз порядок ключей в json и есть костыль.
Перекладывание в json, особенно между разными системами не гарантирует сохранение порядка, это просто в питоне в какой-то версии оптимизация dict привела к его сохранению.
VoidVolker @VoidVolker
duff89 Открываете спецификации стандарта JSON, раздел 6, objects и внимательно читаете:
The JSON syntax does not impose any restrictions on the strings used as names, does not require that name strings be unique, and does not assign any significance to the ordering of name/value pairs.
Даже на официальном сайте стандарта JSON написано:
Объект — неупорядоченный набор пар ключ/значение.
Перестаньте уже хранить порядок ключей в dict
dict как-раз упорядочен
Everything_is_bad @Everything_is_bad
Перекладывание в json, особенно между разными системами не гарантирует сохранение порядка, это просто в питоне в какой-то версии оптимизация dict привела к его сохранению.

Добавьте поле номер по порядку в ваш dict/json, и сортируйте по нему.
VoidVolker, вы потеряли следующее предложение из текста по первой ссылке:
These are all semantic considerations that may be defined by
JSON processors or in specifications defining specific uses of JSON for data interchange
Если семантически требуется обеспечить некий порядок — почему этого зазорно хотеть-то? Я вам сходу могу как минимум 1 пример привести, из своего недавнего, где порядок пар ключей-значений имеет существенное значение: форматирование автогенерируемого контента swagger.json, чтобы каждый МР с минорными правками не превращался в «-1000 +1000 строк» просто потому, что «Перестаньте уже хранить порядок ключей в dict», ага.
Ну и да, если у автора в версии питона стоковый dict всё ещё не умеет в хранение порядка, то можно использовать, например:
json.JSONDecoder(object_pairs_hook=collections.OrderedDict)
Everything_is_bad @Everything_is_bad
Anton Kuzmichev, так где гарантия того что порядок сохранится в postgresql, в js или в любой другой системе, которая поработает с json?
VoidVolker @VoidVolker
Anton Kuzmichev если требуется — то обеспечивайте, никто же не запрещает. В стандарте написано, что порядок никто не гарантирует — поэтому и если надо сортированные данные, то либо класть в массив либо добавить поле для сортировки или сортировать ручками.
Everything_is_bad, в общем случае да, нет гарантии. Частно же, надо смотреть. Если из памяти гнать сразу в файл — вполне ок. Гнать в БД, в специализированное поле, а не текстовое — не ок.
О Python с Игорем Давыденко
Всякое разное о Python, раньше было про Django/Flask, сейчас больше про Fast Python и asyncio.
13 февраля 2009 г.
Простой и работающий JSONField для Django
Не успел, я починить PickleField для Django, мне понадобилось создать JSONField . Задача оказалась решенной на раз/два, плюс ко всему добавился маленький и полезный виджет для JSONField ‘а, который показывает красиво отформатированный JSON в textarea . Сразу скажу, что contribute_to_class метод чуть менне, чем полностью, скопипастен с снипетта 377.
from django.conf import settings from django.forms.widgets import Textarea from django.db.models import SubfieldBase, TextField from django.utils import simplejson class JSONField(TextField): __metaclass__ = SubfieldBase def contribute_to_class(self, cls, name): super(JSONField, self).contribute_to_class(cls, name) def get_json(model): return self.get_db_prep_value(getattr(model, self.attname)) setattr(cls, 'get_%s_json' % self.name, get_json) def set_json(model, json): setattr(model, self.attname, self.to_python(json)) setattr(cls, 'set_%s_json' % self.name, set_json) def formfield(self, **kwargs): kwargs['widget'] = JSONWidget(attrs=) return super(JSONField, self).formfield(**kwargs) def get_db_prep_value(self, value): return simplejson.dumps(value) def to_python(self, value): if not isinstance(value, basestring): return value try: return simplejson.loads(value, encoding=settings.DEFAULT_CHARSET) except ValueError, e: # If string could not parse as JSON it's means that it's Python # string saved to JSONField. return value class JSONWidget(Textarea): """ Prettify dumps of all non-string JSON data. """ def render(self, name, value, attrs=None): if not isinstance(value, basestring) and value is not None: value = simplejson.dumps(value, indent=4, sort_keys=True) return super(JSONWidget, self).render(name, value, attrs)
Ну и пару примеров использования, напоследок:
>>> # Пусть у нас есть простая модель >>> class Sample(models.Model): . name = models.CharField(max_length=16) . data = JSONField() >>> # Создадим модель и сохраним в data поле настройки для ShadowBox >>> sb = Sample.objects.create(name='ShadowBox', . data=) >>> # Теперь мы хотим напечатать эти настройки где-то в шаблоне >>> sb.get_data_json() '' >>> # Вспомним, что нам вообщем-то не зачем кастомное значение для overlayOpacity >>> del sb.data['overlayOpacity'] >>> sb.save() >>> # И опять напечатаем настройки в шаблоне >>> sb.get_data_json() ''
зы. ShadowBox — это лучший лайтбокс для любого JavaScript фреймворка, имо.
Простой и работающий JSONField для Django
Не успел, я починить PickleField для Django, мне понадобилось создать JSONField . Задача оказалась решенной на раз/два, плюс ко всему добавился маленький и полезный виджет для JSONField ‘а, который показывает красиво отформатированный JSON в textarea . Сразу скажу, что contribute_to_class метод чуть менне, чем полностью, скопипастен с снипетта 377.
from django.conf import settings from django.forms.widgets import Textarea from django.db.models import SubfieldBase, TextField from django.utils import simplejson class JSONField(TextField): __metaclass__ = SubfieldBase def contribute_to_class(self, cls, name): super(JSONField, self).contribute_to_class(cls, name) def get_json(model): return self.get_db_prep_value(getattr(model, self.attname)) setattr(cls, 'get_%s_json' % self.name, get_json) def set_json(model, json): setattr(model, self.attname, self.to_python(json)) setattr(cls, 'set_%s_json' % self.name, set_json) def formfield(self, **kwargs): kwargs['widget'] = JSONWidget(attrs=) return super(JSONField, self).formfield(**kwargs) def get_db_prep_value(self, value): return simplejson.dumps(value) def to_python(self, value): if not isinstance(value, basestring): return value try: return simplejson.loads(value, encoding=settings.DEFAULT_CHARSET) except ValueError, e: # If string could not parse as JSON it's means that it's Python # string saved to JSONField. return value class JSONWidget(Textarea): """ Prettify dumps of all non-string JSON data. """ def render(self, name, value, attrs=None): if not isinstance(value, basestring) and value is not None: value = simplejson.dumps(value, indent=4, sort_keys=True) return super(JSONWidget, self).render(name, value, attrs)
Ну и пару примеров использования, напоследок:
>>> # Пусть у нас есть простая модель >>> class Sample(models.Model): . name = models.CharField(max_length=16) . data = JSONField() >>> # Создадим модель и сохраним в data поле настройки для ShadowBox >>> sb = Sample.objects.create(name='ShadowBox', . data=) >>> # Теперь мы хотим напечатать эти настройки где-то в шаблоне >>> sb.get_data_json() '' >>> # Вспомним, что нам вообщем-то не зачем кастомное значение для overlayOpacity >>> del sb.data['overlayOpacity'] >>> sb.save() >>> # И опять напечатаем настройки в шаблоне >>> sb.get_data_json() ''
зы. ShadowBox — это лучший лайтбокс для любого JavaScript фреймворка, имо.
Вставка в Django JsonField без извлечения содержимого в память
Это JSON-поле содержит данные за два года, как тонна данных.
class DataQUery: def __init__(self, **kwargs): super(DataQUery, self).__init__() self.data = kwargs['data'] #is pulling the data in memory, #that's what I want to avoid # Then We have dictionary call today to hold daily data today = dict() # . putting stuff in today # Then insert it into data with today's date as key for that day self.data[f""] = today # Then update the database Employee.objects.filter(id=1).update(data=self.data)
Я хочу вставить в колонку данные, не извлекая их в память.
Да, я могу изменить default=dict на default=list и напрямую сделать
Employee.objects.filter(id=1).update(data=today)
НО мне нужна сегодняшняя ДАТА, чтобы определить разные дни.
Итак, если мне не нужно вытаскивать колонку данных, мне не нужен kwargs dict. Допустим, я ничего не инициализирую (поэтому ничего не тяну в память), как я могу обновить колонку данных со словарем, который идентифицируется сегодняшней датой, так, чтобы после обновления колонка данных (JSONField) выглядела как , 2021-08-11:<. >>
В реляционных базах данных можно хранить несколько элементов, принадлежащих одной и той же сущности, создав новую модель с привязкой ForeignKey к этой другой модели. Таким образом, это означает, что мы реализуем это как:
class Employee(models.Model): # … pass class EmployeePresence(models.Model): employee = models.ForeignKey(Employee, on_delete=models.CASCADE) date = models.DateField(auto_now_add=True) data = models.JSONField(default=dict, blank=True) class Meta: ordering = ['employee', 'date']
В этом случае мы хотим добавить новый объект EmployeePresence , который относится к объекту Employee e , поэтому мы создаем новый объект с:
EmployeePresence.objects.create( date='2021-08-11', data= )
Мы можем получить доступ ко всем EmployeePresence объектам данного Employee объекта e с помощью:
e.employeepresence_set.all()
создание, обновление, удаление одной записи EmployeePresence , таким образом, упрощается и может быть эффективно выполнено с помощью запросов.
Python-сообщество
![]()
- Начало
- » Django
- » Как правильно добавить большое количество атрибутов к объекту модели?
#1 Март 9, 2013 20:24:28
MikaMika Зарегистрирован: 2012-11-07 Сообщения: 51 Репутация: 0 Профиль Отправить e-mail
Как правильно добавить большое количество атрибутов к объекту модели?
Привет.
Ребята подскажите как правильнее решить следующую задачу.
Есть объект, пусть это будет камень.
Объект представлен классом модели:
class Stone(models.Model): name = models.CharField()
У этого объекта есть множество атрибутов: цвет, форма, размер, вес и т.д.
Эти атрибуты почти никогда не меняются!
Самое простое, что можно сделать это несколько классов: Color, StoneForm, Size и т.д., описать значение атрибутов в них, а в классе Stone поставить ForeingKey() на нужные классы.
Но я хотел бы сохранить в модели нечто такое:
'colors': ['red', 'green', 'blue'], 'stone_form':['round', 'square', 'convex'], 'size': ['small', 'medium', 'large'] >
Создать словарь, записать в него необходимые атрибуты и значения и записать в поле модели.
Хотел бы узнать, что для этого лучше использовать?
Встроенный pickle или jsonField?
А если jsonField, то где лучше скачать или писать самому?
Или всё же оставить вариант с классами модели, ведь у него тоже есть свои преимущества.
Отредактировано MikaMika (Март 9, 2013 20:25:29)
#2 Март 10, 2013 17:13:28
alafin Root От: Киев, Украина Зарегистрирован: 2006-04-06 Сообщения: 756 Репутация: 3 Профиль Отправить e-mail
Как правильно добавить большое количество атрибутов к объекту модели?
MikaMika, все зависит от задачи и как в будущем планируется использовать данный функционал.
1) Если все будете хранить в таблицах (Color, StoneForm, Size):
Плюсы: сможете нормально фильтровать записи (если вам конечно это нужно будет для задачи)
Минусы: Нужно создавать для задачи дополнительные таблицы (Color, StoneForm, Size)
2) Если будете хранить записи в JSONField-е (на основе TextField-а)
Плюсы: колонки в таблице будут храниться в читабельном виде (JSON)
Минусы: невозможно будет нормально фильтровать в запросах записи
3) Если будете хранить записи в PickleField:
Плюсы: вменяемых плюсов не знаю 🙂
Минусы: нечитабельный вид хранения в базе
4) В Postgres-е в версии 9.2 появился JSONField можно взглянуть и на него.