如何在 Django 模型上存储字典?

Posted

技术标签:

【中文标题】如何在 Django 模型上存储字典?【英文标题】:How to store a dictionary on a Django Model? 【发布时间】:2010-09-28 23:15:15 【问题描述】:

我需要在 Django 模型中存储一些数据。这些数据并不等于模型的所有实例。

起初我考虑将模型子类化,但我试图保持应用程序的灵活性。如果我使用子类,每次我需要一种新的对象时都需要创建一个完整的类,这不好。我最终也会得到很多子类,只是为了存储一对额外的字段。

我真的觉得字典是最好的方法,但是 Django 文档中没有关于在 Django 模型中存储字典的内容(或者我找不到它)。

有什么线索吗?

【问题讨论】:

“一些数据”——定义不明确。没有一些数据模型或其他提示,就没有答案。 【参考方案1】:

我知道这是一个老问题,但今天(2021 年)最干净的替代方法是使用本机 JSONfield(自 django 3.1 起)

文档:https://docs.djangoproject.com/en/3.2/ref/models/fields/#django.db.models.JSONField

您只需在类模型中创建一个名为 jsonfield 的模型字段,然后瞧

【讨论】:

【参考方案2】:

如果您不需要查询这些额外数据,则可以将其存储为序列化字典。使用repr 将字典转换为字符串,使用eval 将字符串转换回字典。注意 eval 字典中没有用户数据,或者使用 safe_eval 实现。

例如,在您的viewscreateupdate 方法中,您可以添加:

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])

【讨论】:

【参考方案3】:

我使用文本字段和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)

【讨论】:

简而言之,首先将dict更改为JSON,然后分别使用json.loads和json.dumps将其更改为字符串,并将其保存在db中。最后,当您检索数据时,您将其作为字典读取,对吧?【参考方案4】:

这个问题很老,但我遇到了同样的问题,到此结束,选择的答案无法再解决我的问题。

如果您想在 Django 或 REST Api 中存储字典,或者用作前端的对象,或者因为您的数据不一定具有相同的结构,我使用的解决方案可以帮助您。

在您的 API 中保存数据时,使用 json.dump() 方法能够以正确的 json 格式存储数据,如 question 中所述。

如果您使用此结构,您的数据将已经采用适当的 json 格式,以便在您的 ajax(或其他)调用中使用 JSON.parse() 在前端调用。

【讨论】:

【参考方案5】:

如果您使用的是 Postgres,则可以使用 hstore 字段:https://docs.djangoproject.com/en/1.10/ref/contrib/postgres/fields/#hstorefield。

【讨论】:

【参考方案6】:

在我看来,“不等于模型的所有实例”听起来很适合“无模式数据库”。 CouchDB 是该方法的典型代表,您可能会考虑这一点。

在一个项目中,我将几个在 Django ORM 中表现不佳的表移到了 CouchDB,对此我感到非常满意。我使用 couchdb-python 没有任何 Django 特定的 CouchDB 模块。数据模型的描述可以在here 找到。从 Django 中的 5 个“模型”到 Django 中的 3 个“模型”和一个 CouchDB“数据库”实际上稍微减少了我的应用程序中的总代码行数。

【讨论】:

【参考方案7】:

另一个干净快速的解决方案可以在这里找到: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 或一个字符串,它会直接存储该数据。 from django.contrib.postgres import fields 用于 PostgreSQL【参考方案8】:

我不确定您要解决的问题的性质,但它听起来与Google App Engine's BigTable Expando 非常相似。

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 框架。如果这是表达模型的最佳方式,可能值得研究。

传统的关系数据库模型不具备这种列添加灵活性。如果您的数据类型足够简单,您可以打破传统的 RDBMS 理念,并按照@Ned Batchelder 的建议通过序列化将值转换为单个列;但是,如果您必须使用 RDBMS,Django 模型继承可能是要走的路。值得注意的是,它将为每个派生级别创建a one-to-one foreign key 关系。

【讨论】:

【参考方案9】:

我是通过 google 的第 4 个结果到“django store object”来看到这篇文章的

有点晚了,但django-picklefield 对我来说似乎是个不错的解决方案。

文档示例:

要使用,只需在模型中定义一个字段:

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

并将您喜欢的任何内容(只要它是可腌制的)分配给该字段:

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

【讨论】:

【参考方案10】:

我同意您需要避免将其他结构化数据填充到单个列中。但如果你必须这样做,Django 有一个 XMLField 内置。

还有 JSONField 在 Django snipplets。

【讨论】:

【参考方案11】:

Django-Geo 包含一个您可能会觉得有用的“DictionaryField”:

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

一般来说,如果您不需要跨数据查询,请使用非规范化方法来避免额外查询。用户设置就是一个很好的例子!

【讨论】:

【参考方案12】:

如果您要查找的确实是字典之类的任意数据,您可能可以使用两级设置,其中一个模型是容器,另一个模型是键值对。您将创建容器的一个实例,创建每个键值实例,并将键值实例集与容器实例相关联。比如:

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)

它并不漂亮,但它可以让您使用 DB 访问/搜索字典的内部结构,而 pickle/serialize 解决方案则不会。

【讨论】:

唯一的缺点是会产生额外的数据库查询 另一个缺点是你只有一个“层次”的数据,你不能创建多层次的复杂 JSON 样式的数据。 (不过还是个好主意) 找到了这个解决方案的一个很好的扩展:djangosnippets.org/snippets/2451这个家伙将字典扩展到所有pythonic字典函数 那么这个怎么调用呢? @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)【参考方案13】:

正如 Ned 所回答的,如果您使用字典方法,您将无法查询“某些数据”。

如果您仍然需要存储字典,那么到目前为止,最好的方法是 Marty Alchin 的新书 Pro Django 中记录的 PickleField 类。此方法仅根据需要使用 Python 类属性来腌制/取消腌制 Python 对象,该对象存储在模型字段中。

这种方法的基本原理是使用 django 的 contibute_to_class 方法向模型动态添加新字段,并使用 getattr/setattr 进行按需序列化。

我能找到的几个类似的在线示例之一是JSONField 的定义。

【讨论】:

【参考方案14】:

仔细考虑,找出每个数据集的共同点……然后定义你的模型。它可能需要或不需要使用子类。不能避免表示共同点的外键,但在有意义时鼓励使用。

将随机数据填充到 SQL 表中并不明智,除非它是真正的非关系数据。如果是这种情况,请明确您的问题,我们或许可以提供帮助。

【讨论】:

+1:不要只是随意地将 Python 对象填充到表中。

以上是关于如何在 Django 模型上存储字典?的主要内容,如果未能解决你的问题,请参考以下文章

如何将 Django 模型对象转换为字典并且仍然拥有它们的外键? [复制]

Django中如何将数据库的数据展示在网页上

如何在 Django 模型中存储电话号码?

如何在 Django 中构建模型?

如何在 Django 模型中存储任意名称/值键对?

如何访问 Django 模板中的字典元素?