Как сохранить словарь в модели Django?

Мне нужно сохранить некоторые данные в модели Django. Эти данные не совпадают для всех экземпляров модели.

Сначала я думал о создании подкласса модели, но стараюсь сохранить гибкость приложения. Если я использую подклассы, мне нужно будет создавать целый класс каждый раз, когда мне понадобится новый тип объекта, а это бесполезно. Я также получу много подклассов только для хранения пары дополнительных полей.

Я действительно считаю, что словарь был бы лучшим подходом, но в документации Django нет ничего о хранении словаря в модели Django (или я не могу его найти).

Какие-нибудь подсказки?

"некоторые данные" - плохо определены. Без какой-либо модели данных или других подсказок нет ответа.

S.Lott 31.12.2008 06:29
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
60
1
61 215
13
Перейти к ответу Данный вопрос помечен как решенный

Ответы 13

Если вам не нужно запрашивать какие-либо из этих дополнительных данных, вы можете сохранить их как сериализованный словарь. Используйте repr, чтобы превратить словарь в строку, и eval, чтобы превратить строку обратно в словарь. Позаботьтесь о eval, чтобы в словаре не было пользовательских данных, или используйте реализацию safe_eval.

Например, в методах create и update вашего views вы можете добавить:

if isinstance(request.data, dict) == False:
    req_data = request.data.dict().copy()
else:
    req_data = request.data.copy()

dict_key = 'request_parameter_that_has_a_dict_inside'
if dict_key in req_data.keys() and isinstance(req_data[dict_key], dict):
    req_data[dict_key] = repr(req_data[dict_key])

Подумайте и найдите общие черты каждого набора данных ... затем определите свою модель. Это может потребовать использования подклассов или нет. Не следует избегать внешних ключей, представляющих общие черты, но поощрять их, когда они имеют смысл.

Вставлять случайные данные в таблицу SQL неразумно, если только это действительно нереляционные данные. В таком случае определите свою проблему, и мы сможем помочь.

+1: не просто помещайте объекты Python в таблицу наугад.

S.Lott 31.12.2008 14:27

Я не совсем уверен в природе проблемы, которую вы пытаетесь решить, но это странно похоже на BigTable Expando от Google App Engine.

Expandos позволяет указывать и сохранять дополнительные поля в экземпляре объекта, поддерживаемом базой данных, во время выполнения. Цитата из документов:

import datetime
from google.appengine.ext import db

class Song(db.Expando):
  title = db.StringProperty()

crazy = Song(title='Crazy like a diamond',
             author='Lucy Sky',
             publish_date='yesterday',
             rating=5.0)

crazy.last_minute_note=db.Text('Get a train to the station.')

Google App Engine в настоящее время поддерживает как Python, так и платформу Django. Возможно, стоит посмотреть, если это лучший способ выразить ваши модели.

Традиционные модели реляционных баз данных не обладают такой гибкостью добавления столбцов. Если ваши типы данных достаточно просты, вы можете отказаться от традиционной философии РСУБД и поместить значения в один столбец с помощью сериализации, как предлагает @Ned Batchelder; однако, если вы используете имеют для использования СУБД, наследование модели Django, вероятно, будет правильным решением. В частности, он создаст отношение взаимно однозначный внешний ключ для каждого уровня деривации.

Как ответил Нед, вы не сможете запросить «некоторые данные», если будете использовать словарный подход.

Если вам все еще нужно хранить словари, то лучшим подходом, безусловно, является класс PickleField, описанный в новой книге Марти Алчина Pro Django. Этот метод использует свойства класса Python для выделения / извлечения объекта Python только по запросу, который хранится в поле модели.

Основы этого подхода - использовать метод django contibute_to_class для динамического добавления нового поля в вашу модель и использовать getattr / setattr для сериализации по запросу.

Один из немногих похожих онлайн-примеров, которые я смог найти, - это определение JSONField.

Ответ принят как подходящий

Если это действительно словарь, как произвольные данные, которые вы ищете, вы, вероятно, можете использовать двухуровневую настройку с одной моделью, которая является контейнером, и другой моделью, которая представляет собой пары ключ-значение. Вы должны создать экземпляр контейнера, создать каждый из экземпляров "ключ-значение" и связать набор экземпляров "ключ-значение" с экземпляром контейнера. Что-то вроде:

class Dicty(models.Model):
    name      = models.CharField(max_length=50)

class KeyVal(models.Model):
    container = models.ForeignKey(Dicty, db_index=True)
    key       = models.CharField(max_length=240, db_index=True)
    value     = models.CharField(max_length=240, db_index=True)

Это некрасиво, но это позволит вам получить доступ / искать внутренности словаря с помощью БД, тогда как решение pickle / serialize не будет.

Единственным недостатком является то, что последует дополнительный запрос к БД.

Filip Dupanović 08.05.2010 01:28

Еще одним недостатком является то, что у вас есть ровно один «уровень» данных, вы не можете создавать многоуровневые сложные данные в стиле JSON. (но тем не менее хорошая идея)

Nick Perkins 27.07.2011 00:43

Нашел хорошее расширение этого решения: djangosnippets.org/snippets/2451 этот парень расширил словарь на все функции словаря Python

michel.iamit 07.03.2014 19:26

Так как это называется?

Micah Walter 27.06.2014 00:56

@NickPerkins Вы можете сделать это рекурсивным, если Dicty содержит поле parent = models.ForeignKey('self', on_delete=models.CASCADE, null=True, blank=True). Любой dict без родителя - это объект верхнего уровня. Тадааа! Да, было бы неплохо, если бы value указывал на другой словарь вместо использования специального поля, но ¯_ (ツ) _ / ¯ ... В качестве альтернативы на KeyVal добавьте child = models.ForeignKey('Dicty', models.CASCADE, null=True, blank=True)

Anthony Manning-Franklin 13.03.2017 10:31

То, что «не равно всем экземплярам модели», звучит для меня как хорошее совпадение с «базой данных без схемы». CouchDB является образцом этого подхода, и вы можете это рассмотреть.

В одном проекте я переместил несколько столов, которые никогда не играли очень хорошо с Django ORM, на CouchDB, и я вполне доволен этим. Я использую couchdb-python без каких-либо модулей CouchDB, специфичных для Django. Описание модели данных можно найти в здесь. Переход от пяти «моделей» в Django к 3 «моделям» в Django и одной «базе данных» CouchDB фактически немного сократил общее количество строк кода в моем приложении.

Django-Geo включает поле DictionaryField, которое может оказаться полезным:

http://code.google.com/p/django-geo/source/browse/trunk/fields.py?r=13#49.

В общем, если вам не нужно запрашивать данные, используйте денормализованный подход, чтобы избежать дополнительных запросов. Пользовательские настройки - хороший тому пример!

Я согласен с тем, что вам нужно воздержаться от помещения структурированных данных в один столбец. Но если вы должны это сделать, Django имеет встроенную программу XMLField.

Также в снипплетах Django есть JSONField.

Я пришел к этому сообщению по четвертому результату Google для "объекта хранилища django"

Немного поздно, но джанго-рассол мне кажется хорошим решением.

Пример из документа:

Чтобы использовать, просто определите поле в своей модели:

>>> from picklefield.fields import PickledObjectField
>>> class SomeObject(models.Model):
>>>     args = PickledObjectField()

и назначьте в поле все, что вам нравится (при условии, что это можно выбрать):

>>> obj = SomeObject()
>>> obj.args = ['fancy', {'objects': 'inside'}]
>>> obj.save()

Еще одно чистое и быстрое решение можно найти здесь: https://github.com/bradjasper/django-jsonfield

Для удобства скопировал простые инструкции.

Установить

pip install jsonfield

Применение

from django.db import models
from jsonfield import JSONField

class MyModel(models.Model):
    json = JSONField()

Это удобный способ использования, но по умолчанию он не поддерживает проверку словаря. Поэтому, если я отправляю int или строку, они напрямую сохраняют эти данные.

Devendra Bhat 14.02.2019 10:10
from django.contrib.postgres import fields для PostgreSQL
Dimitrios Mistriotis 22.01.2020 14:03

Если вы используете Postgres, вы можете использовать поле hstore: https://docs.djangoproject.com/en/1.10/ref/contrib/postgres/fields/#hstorefield.

Это старый вопрос, но у меня была та же проблема, которая закончилась здесь, и выбранный ответ больше не мог решить мою проблему.

Если вы хотите хранить словари в Django или REST Api, либо для использования в качестве объектов в вашем интерфейсе, либо потому, что ваши данные не обязательно будут иметь одинаковую структуру, решение, которое я использовал, может вам помочь.

При сохранении данных в вашем API используйте метод json.dump (), чтобы иметь возможность хранить их в надлежащем формате json, как описано в этом вопрос.

Если вы используете эту структуру, ваши данные уже будут в соответствующем формате json, которые будут вызываться во внешнем интерфейсе с помощью JSON.parse () в вашем вызове ajax (или другом).

Я использую текстовое поле и json.loads() / json.dumps()

models.py

import json
from django.db import models

class Item(models.Model):
    data = models.TextField(blank=True, null=True, default='{}')

    def save(self, *args, **kwargs):
        ## load the current string and
        ## convert string to python dictionary
        data_dict = json.loads(self.data)

        ## do something with the dictionary
        for something in somethings:
            data_dict[something] = some_function(something)

        ## if it is empty, save it back to a '{}' string,
        ## if it is not empty, convert the dictionary back to a json string
        if not data_dict:
            self.data = '{}'
        else:
            self.data = json.dumps(data_dict)


        super(Item, self).save(*args, **kwargs)

Другие вопросы по теме