Django:模型中的列表字段?


73

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


1
您正在使用哪个数据库?
erthalion 2014年

Answers:


103

您可以使用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的新类,您应该改用它。这是它的链接

youtube上有关于PostgreSQL JSON和数组的好话。观看它,它具有非常好的信息。


2
我们是否有特定的原因可以使用json序列化而不是自定义List字段(例如此线程中的第二个答案)?我主要在性能方面关注此问题,但我也想知道在这两种方法之间进行选择之前是否还有其他需要注意的地方。提前致谢!
Kostas Livieratos

1
我的实现比较简单,您可以传递列表,字典或任何可以转换为json的内容。
艾哈迈德(Ahmed)

json.dumps将一个完整的JSON的转换变化之类的东西- >。我不明白为什么要使用列表->字符串转换时使用它
stelios

如果您运行的是postgresql,则可以本机使用JSONField
Ahmed

2
在Django 1.10及更高版本中,ArrayField您可以使用一个新字段。请参阅下面的答案。
madmanick '17

35

如果您使用的是Django 1.10或更高版本的AND Postgres作为数据库,则可以使用 ArrayField。比起django-taggit或其他替代方法,最好使用它,因为它是Django框架的本机。 https://docs.djangoproject.com/zh-CN/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或更高版本,则它们已添加了对JSONField的支持大多数数据库后端(MariaDB 10.2.7 +,MySQL 5.7.8 +,Oracle,PostgreSQL和SQLite 3.9.0+)。您可以使用它来存储您的数组!

https://docs.djangoproject.com/zh-CN/3.1/ref/models/fields/#jsonfield

from django.db import models

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

2
谢谢!看起来像特定postgres,但是很棒!
Anupam

22

如果您使用的是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开始存在。)


19

我认为它将为您提供帮助。

从django.db导入模型
进口AST

类ListField(models.TextField):
    __metaclass__ = models.SubfieldBase
    description =“存储python列表”

    def __init __(self,* args,** kwargs):
        超级(ListField,self).__ init __(* args,** kwargs)

    def to_python(自我,价值):
        如果不值:
            值= []

        如果isinstance(value,list):
            返回值

        返回ast.literal_eval(value)

    def get_prep_value(self,value):
        如果值为None:
            返回值

        返回unicode(值)

    def value_to_string(self,obj):
        值= self._get_val_from_obj(obj)
        返回self.get_db_prep_value(value)

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
1升
>>> o.test_list
[[1、2、3],[2、3、4、4]
>>> 

我正在使用python 3.5,所以有几个问题-unicode现在是str,我遇到一个错误..create()接受1个位置参数,但给出了2个参数
Taposh DuttaRoy

我在Django 1.7中使用了此解决方案。现在,我将其升级到1.9.12,但无法正常工作。我应该做些什么改变才能使其在Django 1.9.12
Soham Navadiya'May 29''17

请分享您得到的错误,以及您正在使用哪个版本的python?
Prashant Gaur

8

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

在这种情况下,您无需关心字段值序列化-它会在后台进行。

希望能有所帮助。


6

这是一个很老的话题,但是由于它在搜索“ 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)

1
谢谢堆。我正在使用Django 1.11,并且不得不更改成员函数以采用5个参数“ def from_db_value(self,value,expression,connection,context):”
MagicLAMP

1

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

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


1
请注意,自Django 1.9起,本文中的每个链接均已弃用该工具。
凤凰城

1

以我目前的信誉我没有能力作出评论,所以我选择的答案引用了示例代码注释中回复PRASHANT高尔(感谢,高尔-这是有帮助的!) -他的样本是python2,因为python3没有

统一码
方法。

以下功能替换

get_prep_value(自身,值):
应该可以与运行python3的Django一起使用(我将很快使用此代码-尚未经过测试)。但是请注意,我正在通过
encoding ='utf-8',错误='ignore'
参数
解码()
unicode()方法
。编码应匹配您的Django settings.py配置并传递
错误='忽略'
是可选的(在极少数情况下,可能会导致静默数据丢失,而不是配置了错误的Django的异常)。

导入系统

...

    def get_prep_value(self,value):
        如果值为None:
            返回值
        如果sys.version_info [0]> = 3:
            如果isinstance(out_data,type(b'')):
                返回value.decode(encoding ='utf-8',errors ='ignore')
        其他:
            如果isinstance(out_data,type(b'')):
                返回unicode(值,编码='utf-8',错误='忽略')
        返回str(值)
...



0

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


0

如果您将Postgres用作数据库

有一个ArrayField仅用于Postgres的本地字段。

文档上阅读有关它的更多信息

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

如果您正在使用其他数据库

有两种存储数组的方法。(非官方)

1.创建一个自定义Django模型字段。

from django.db import models
from typing import Iterable

class ListField(models.TextField):
    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)

代码段形式@Yaroslav


2.将数组转换为字符串,然后用保存 CharField

import json

class List(models.Model):
    list = models.CharField(max_length=200)

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

    def get_list(self):
        return json.loads(self.foo)
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.