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

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

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

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

有什么线索吗?

【问题讨论】:

  • “一些数据”——定义不明确。没有一些数据模型或其他提示,就没有答案。

标签: python django django-models orm persistence


【解决方案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 对象填充到表中。
                      猜你喜欢
                      • 2017-08-26
                      • 1970-01-01
                      • 2012-03-29
                      • 2021-09-15
                      • 1970-01-01
                      • 2016-02-11
                      • 2018-02-16
                      • 2015-04-19
                      • 1970-01-01
                      相关资源
                      最近更新 更多