【问题标题】:List field in model?模型中的列表字段?
【发布时间】:2014-04-15 22:08:44
【问题描述】:

在我的模型中,我想要一个包含三元组列表的字段。例如[[1, 3, 4], [4, 2, 6], [8, 12, 3], [3, 3, 9]]。有没有一个字段可以把这些数据存储到数据库中?

【问题讨论】:

标签: django django-orm


【解决方案1】:

您可以使用JSON将其转换为字符串并将其存储为字符串。

例如,

In [3]: json.dumps([[1, 3, 4], [4, 2, 6], [8, 12, 3], [3, 3, 9]])

Out[3]: '[[1, 3, 4], [4, 2, 6], [8, 12, 3], [3, 3, 9]]'

你可以在你的类中添加一个方法来自动为你转换它。

import json


class Foobar(models.Model):
    foo = models.CharField(max_length=200)

    def set_foo(self, x):
        self.foo = json.dumps(x)

    def get_foo(self):
        return json.loads(self.foo)

如果您使用 Django 1.9 或更高版本,并且使用 postgresql,则有一个名为 JSONField 的新类,您应该使用它。 Here is a link to it

youtube 上有一篇关于 PostgreSQL JSON 和数组的精彩讨论。看看吧,里面有很好的信息。

【讨论】:

  • 是否有特定原因我们可以使用 json 序列化而不是自定义 List 字段(如此线程中的第二个答案)?我主要关注性能方面的问题,但我也想知道在这两种方法之间进行选择之前是否还有其他需要注意的地方。提前致谢!
  • 我的实现起来比较简单,可以传list,dict,或者任何可以转成json的东西。
  • json.dumps 将进行完整的 json 转换,改变诸如 None --> null 之类的东西。当您想要列表->字符串转换时,我不明白为什么要使用它
  • 如果你运行的是 postgresql,你可以原生使用 JSONField
  • 在 Django 1.10 及更高版本中,您可以使用一个新的 ArrayField 字段。请参阅下面的答案。
【解决方案2】:

如果您使用 Django 1.10 或更新版本并将 Postgres 作为数据库,则可以使用 ArrayField。它比 django-taggit 或其他替代方案更好用,因为它是 Django 框架的原生。 https://docs.djangoproject.com/en/3.1/ref/contrib/postgres/fields/#arrayfield

from django.db import models
from django.contrib.postgres.fields import ArrayField

class ChessBoard(models.Model):
    board = ArrayField(
        ArrayField(
            models.CharField(max_length=10, blank=True),
            size=8,
        ),
        size=8,
    )

如果您使用的是 Django 3.1 或更高版本,他们会在大多数数据库后端(MariaDB 10.2.7+、MySQL 5.7.8+、Oracle、PostgreSQL 和 SQLite 3.9)中添加对 JSONField 的支持.0+)。你可以用它来存储你的数组!

https://docs.djangoproject.com/en/3.1/ref/models/fields/#jsonfield

from django.db import models

class ChessBoard(models.Model):
    list_of_pieces = models.JSONField()

【讨论】:

  • 谢谢!看起来像它的postgres specific,但很棒!
  • 你如何使用这个? from django.db import models class ChessBoard(models.Model): list_of_pieces = models.JSONField() 接下来是什么?你的清单在哪里?您在 DB 中是否有任何字段,或者您是否在某处创建了列表?
【解决方案3】:

如果您使用的是 PostgreSQL,则可以将 ArrayField 与嵌套的 ArrayField 一起使用:https://docs.djangoproject.com/en/2.2/ref/contrib/postgres/fields/

这样,底层数据库将知道数据结构。此外,ORM 还为它带来了特殊的功能。

请注意,您必须自己创建 GIN 索引(请参阅上面的链接,进一步向下:https://docs.djangoproject.com/en/2.2/ref/contrib/postgres/fields/#indexing-arrayfield)。

(编辑:更新了最新 Django LTS 的链接,此功能至少从 1.8 开始就存在。)

【讨论】:

    【解决方案4】:

    我想它会对你有所帮助。

    from django.db import models
    import ast
    
    class ListField(models.TextField):
        __metaclass__ = models.SubfieldBase
        description = "Stores a python list"
    
        def __init__(self, *args, **kwargs):
            super(ListField, self).__init__(*args, **kwargs)
    
        def to_python(self, value):
            if not value:
                value = []
    
            if isinstance(value, list):
                return value
    
            return ast.literal_eval(value)
    
        def get_prep_value(self, value):
            if value is None:
                return value
    
            return unicode(value)
    
        def value_to_string(self, obj):
            value = self._get_val_from_obj(obj)
            return self.get_db_prep_value(value)
    
    class ListModel(models.Model):
        test_list = ListField()
    

    例子:

    >>> ListModel.objects.create(test_list= [[1,2,3], [2,3,4,4]])
    <ListModel: ListModel object>
    >>> ListModel.objects.get(id=1)
    <ListModel: ListModel object>
    >>> o = ListModel.objects.get(id=1)
    >>> o.id
    1L
    >>> o.test_list
    [[1, 2, 3], [2, 3, 4, 4]]
    >>> 
    

    【讨论】:

    • 我使用的是 python 3.5,所以有几个问题 - unicode 现在是 str,我收到一个错误 ..create() 需要 1 个位置参数,但给出了 2 个
    • 我在 django 1.7 中使用了这个解决方案。现在我将它升级到 1.9.12 但它不起作用。我应该进行哪些更改才能使其在 django 1.9.12 中可用
    • 请分享您遇到的错误,以及您使用的是哪个版本的python?
    • 考虑使用json.loads 而不是ast.literal_eval,因为前者快了大约6 倍。
    【解决方案5】:

    只需使用这些第三方包提供的 JSON 字段即可:

    在这种情况下,您不需要关心字段值序列化 - 它会在后台发生。

    【讨论】:

      【解决方案6】:

      这是一个相当古老的话题,但是由于它是在搜索“django list field”时返回的,所以我将分享我修改为使用 Python 3 和 Django 2 的自定义 django 列表字段代码。它现在支持管理界面和不使用 eval(这在 Prashant Gaur 的代码中是一个巨大的安全漏洞)。

      from django.db import models
      from typing import Iterable
      
      class ListField(models.TextField):
          """
          A custom Django field to represent lists as comma separated strings
          """
      
          def __init__(self, *args, **kwargs):
              self.token = kwargs.pop('token', ',')
              super().__init__(*args, **kwargs)
      
          def deconstruct(self):
              name, path, args, kwargs = super().deconstruct()
              kwargs['token'] = self.token
              return name, path, args, kwargs
      
          def to_python(self, value):
      
              class SubList(list):
                  def __init__(self, token, *args):
                      self.token = token
                      super().__init__(*args)
      
                  def __str__(self):
                      return self.token.join(self)
      
              if isinstance(value, list):
                  return value
              if value is None:
                  return SubList(self.token)
              return SubList(self.token, value.split(self.token))
      
          def from_db_value(self, value, expression, connection):
              return self.to_python(value)
      
          def get_prep_value(self, value):
              if not value:
                  return
              assert(isinstance(value, Iterable))
              return self.token.join(value)
      
          def value_to_string(self, obj):
              value = self.value_from_object(obj)
              return self.get_prep_value(value)
      

      【讨论】:

      • 谢谢大家。我正在使用 Django 1.11,并且必须更改成员函数以获取 5 个参数“def from_db_value(self, value, expression, connection, context):”
      【解决方案7】:

      以我目前的声誉,我无法发表评论,所以我选择在reply by Prashant Gaur 中引用 cmets 的示例代码(谢谢,Gaur - 这很有帮助!) - 他的示例适用于 python2,因为 python3没有

      unicode
      方法。

      下面的函数替换

      get_prep_value(self, value):
      应该与运行 python3 的 Django 一起使用(我将很快使用此代码 - 尚未测试)。 但请注意,我将
      encoding='utf-8', errors='ignore'
      参数传递给
      decode()
      unicode() 方法。编码应该与您的 Django settings.py 配置相匹配,并且传递 
      errors='ignore'
      是可选的(并且可能会导致静默数据丢失,而不是在极少数情况下配置错误的 django 异常)。 导入系统 ... def get_prep_value(自我,价值): 如果值为无: 返回值 如果 sys.version_info[0] >= 3: 如果是实例(out_data,类型(b'')): return value.decode(encoding='utf-8', errors='ignore') 别的: 如果是实例(out_data,类型(b'')): 返回 unicode(值,编码='utf-8',错误='忽略') 返回字符串(值) ...

      【讨论】:

        【解决方案8】:

        您可以展平列表,然后将值存储到CommaSeparatedIntegerField。当您从数据库中读取回数据时,只需将这些值重新分组即可。

        免责声明:根据数据库规范化理论,最好不要将集合存储在单个字段中;相反,您会被鼓励将这些三元组中的值存储在它们自己的字段中,并通过外键链接它们。然而,在现实世界中,有时这太麻烦/太慢了。

        【讨论】:

        • 请注意,根据文章中的链接,从 Django 1.9 开始不推荐使用。
        【解决方案9】:

        如果您使用 Google App Engine 或 MongoDB 作为后端,并且您使用的是 djangoappengine 库,那么有一个内置的 ListField 可以完全满足您的需求。此外,很容易查询 Listfield 以查找包含列表中元素的所有对象。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2021-11-02
          • 2011-04-16
          • 1970-01-01
          • 2011-03-07
          • 2018-12-14
          • 2022-01-02
          • 2019-12-04
          相关资源
          最近更新 更多