django 模型中的列表字段?

8fq7wneg  于 2023-01-31  发布在  Go
关注(0)|答案(9)|浏览(164)

在我的模型中,我希望有一个包含三元组的字段,例如[[1, 3, 4], [4, 2, 6], [8, 12, 3], [3, 3, 9]]。是否有一个字段可以在数据库中存储这些数据?

zmeyuzjn

zmeyuzjn1#

您可以使用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)

如果你使用的是Django1.9或更高版本,并且你使用postgresql,有一个新的类叫做JSONField,你应该使用它。
youtube上有一个关于PostgreSQL JSON和数组的很好的讨论。观看它,它有非常好的信息。

cgh8pdjw

cgh8pdjw2#

如果你使用Django1.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,
    )

如果你使用Django3.1或更高版本,他们已经在大多数数据库后端(MariaDB10.2.7+,MySQL 5.7.8+,Oracle,PostgreSQL和SQLite3.9.0+)添加了对JSONField的支持。
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()
cwtwac6a

cwtwac6a3#

如果您使用的是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).
(Edit:更新了最新Django LTS的链接,这个特性至少从1.8开始就存在了。)

ki1q1bka

ki1q1bka4#

我想这会对你有帮助。

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]]
>>>
aamkag61

aamkag615#

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

8yparm6h6#

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

在这种情况下,您不需要关心字段值的序列化-它将在引擎盖下发生。

yfwxisqw

yfwxisqw7#

以我目前的声誉,我没有能力发表评论,所以我选择Prashant Gaurreply中的示例代码发表answer refering评论(谢谢,Gaur --这很有帮助!)--他的示例是针对python 2的,因为python3没有

unicode

方法。
以下功能的替代品

get_prep_value(self, value):

应该可以在运行python3的Django上运行(我很快就会用到这段代码--还没有测试)。注意,我将

encoding='utf-8', errors='ignore'

参数传递给

decode()

unicode() methods

。编码应该与settings.py配置匹配,并且

errors='ignore'

是可选的(并可能导致沉默的数据丢失,而不是异常whith错误配置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)
...
8zzbczxx

8zzbczxx8#

您可以展开列表,然后将值存储到CommaSeparatedIntegerField。当您从数据库读回时,只需将值重新分组为三个。
免责声明:根据数据库规范化理论,集合最好不要存储在单个字段中;相反,我们鼓励你将这些三元组中的值存储在它们自己的字段中,并通过外键将它们链接起来。2然而,在真实的世界中,有时候这太麻烦/太慢了。

llmtgqce

llmtgqce9#

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

相关问题