Django:模型中的列表字段?

时间:2014-03-12 01:34:56

标签: django django-orm

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

9 个答案:

答案 0 :(得分:78)

您可以使用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和数组的很好的讨论。观看它,它有非常好的信息。

答案 1 :(得分:20)

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

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

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

答案 2 :(得分:18)

如果您使用的是更现代版的Django,如1.10,而您的数据库是Postgres,则有一个新的 ArrayField 比django-taggit或其他替代品更好用,因为它是原生的到Django框架。

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

答案 3 :(得分:16)

我认为它会对你有所帮助。

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.objects.get(id=1)

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

答案 4 :(得分:8)

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

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

希望有所帮助。

答案 5 :(得分:1)

您可以展平列表,然后将值存储到CommaSeparatedIntegerField。当您从数据库中回读时,只需将值分组为三个。

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

答案 6 :(得分:1)

这是一个很老的话题,但是由于它是在搜索“ django list field”时返回的,因此我将分享经过修改以与Python 3和Django 2一起使用的自定义django list field代码。它现在支持管理界面,并且不使用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)

答案 7 :(得分:0)

如果您使用Google App Engine或MongoDB作为后端,并且使用的是djangoappengine库,则内置ListField可以完全按照您的要求执行操作。此外,可以轻松查询Listfield以查找列表中包含元素的所有对象。

答案 8 :(得分:0)

以我目前的声誉,我无法发表评论,因此我选择replyPrashant Gaur中示例代码的答案引用评论(感谢Gaur,这很有帮助!)-他的示例适用于python2,因为python3没有

unicode
方法。

下面的功能

get_prep_value(self, value):
的替换应该与运行python3的Django一起使用(我将很快使用此代码-尚未经过测试)。 不过请注意,我正在将
encoding='utf-8', errors='ignore'
参数传递给
decode()
unicode() methods
。编码应与您的Django settings.py配置匹配,并且传递
errors='ignore'
是可选的(在极少数情况下,可能会导致静默数据丢失,而不是配置了django的异常)。

import sys

...

    def get_prep_value(self, value):
        if value is None:
            return value
        if sys.version_info[0] >= 3:
            if isinstance(out_data, type(b'')):
                return value.decode(encoding='utf-8', errors='ignore')
        else:
            if isinstance(out_data, type(b'')):
                return unicode(value, encoding='utf-8', errors='ignore')
        return str(value)
...