如何在 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
实现。
例如,在您的views
的create
和update
方法中,您可以添加:
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 模型上存储字典?的主要内容,如果未能解决你的问题,请参考以下文章