在 Django 模型中存储列表的最有效方法是什么?

目前,我的代码中有很多与下面类似的 python 对象:

class MyClass():
def __init__(self, name, friends):
self.myName = name
self.myFriends = [str(x) for x in friends]

现在我想把它转换成一个 Django 模型,其中 self. myName 是一个字符串字段,self. myFriends 是一个字符串列表。

from django.db import models


class myDjangoModelClass():
myName = models.CharField(max_length=64)
myFriends = ??? # what goes here?

由于列表是 python 中的一种常见数据结构,我有点期望它有一个 Django 模型字段。我知道我可以使用 ManyTomany 或 OneTomany 关系,但是我希望在代码中避免这种额外的间接性。

编辑:

我添加了这个 相关问题,人们可能会发现它很有用。

195975 次浏览

这种关系是否可以更好地表示为 Friends表的一对多外键关系?我知道 myFriends只是字符串,但我认为更好的设计是创建一个 Friend模型,并让 MyClass包含与结果表的外键关系。

请记住,这最终必须以关系数据库结束。所以使用关系式 是解决这个问题的常用方法。如果您坚持要在对象本身中存储一个列表,那么可以使用逗号分隔,并将其存储在一个字符串中,然后提供访问器函数,将该字符串分隔为一个列表。这样,您将被限制在字符串的最大数量上,并且将失去有效的查询。

你可以使用 Django Pickle Field 来存储几乎任何对象,详见下面的代码片段:

Http://www.djangosnippets.org/snippets/513/

class Course(models.Model):
name = models.CharField(max_length=256)
students = models.ManyToManyField(Student)


class Student(models.Model):
first_name = models.CharField(max_length=256)
student_number = models.CharField(max_length=128)
# other fields, etc...


friends = models.ManyToManyField('self')

“过早优化是万恶之源”

牢记这一点,让我们开始吧!一旦您的应用程序达到某一点,反规范化数据是非常常见的。如果操作正确,它可以节省大量昂贵的数据库查找,而代价是多一点内务管理。

要返回朋友名称的 list,我们需要创建一个自定义 Django Field 类,该类在访问时将返回一个列表。

David Cramer 在他的博客上发布了一个创建 SeperatedValueField 的指南,代码如下:

from django.db import models


class SeparatedValuesField(models.TextField):
__metaclass__ = models.SubfieldBase


def __init__(self, *args, **kwargs):
self.token = kwargs.pop('token', ',')
super(SeparatedValuesField, self).__init__(*args, **kwargs)


def to_python(self, value):
if not value: return
if isinstance(value, list):
return value
return value.split(self.token)


def get_db_prep_value(self, value):
if not value: return
assert(isinstance(value, list) or isinstance(value, tuple))
return self.token.join([unicode(s) for s in value])


def value_to_string(self, obj):
value = self._get_val_from_obj(obj)
return self.get_db_prep_value(value)

此代码的逻辑处理从数据库到 Python 的序列化和反序列化值,反之亦然。现在您可以轻松地导入和使用模型类中的自定义字段:

from django.db import models
from custom.fields import SeparatedValuesField


class Person(models.Model):
name = models.CharField(max_length=64)
friends = SeparatedValuesField()

使用一对多关系(FK 从 Friend 到父类)将使您的应用程序更具可伸缩性(因为您可以使用除简单名称之外的其他属性来扩展 Friend 对象)。所以这是最好的办法

在 Django 中存储列表的一个简单方法是将其转换为 JSON 字符串,然后将其保存为模型中的 Text。然后可以通过将(JSON)字符串转换回 python 列表来检索该列表。方法如下:

“列表”将存储在您的 Django 模型中,如下所示:

class MyModel(models.Model):
myList = models.TextField(null=True) # JSON-serialized (text) version of your list

在视图/控制器代码中:

在数据库中存储列表:

import simplejson as json # this would be just 'import json' in Python 2.7 and later
...
...


myModel = MyModel()
listIWantToStore = [1,2,3,4,5,'hello']
myModel.myList = json.dumps(listIWantToStore)
myModel.save()

从数据库中检索列表:

jsonDec = json.decoder.JSONDecoder()
myPythonList = jsonDec.decode(myModel.myList)

从概念上讲,事情是这样的:

>>> myList = [1,2,3,4,5,'hello']
>>> import simplejson as json
>>> myJsonList = json.dumps(myList)
>>> myJsonList
'[1, 2, 3, 4, 5, "hello"]'
>>> myJsonList.__class__
<type 'str'>
>>> jsonDec = json.decoder.JSONDecoder()
>>> myPythonList = jsonDec.decode(myJsonList)
>>> myPythonList
[1, 2, 3, 4, 5, u'hello']
>>> myPythonList.__class__
<type 'list'>

由于这是一个老问题,而且 Django 技术肯定已经发生了重大变化,因此这个答案反映了 Django 版本1.4,并且很可能适用于 v1.5。

缺省情况下,Django 使用关系数据库; 您应该使用它们。使用 ManyToManyField 将友谊映射到数据库关系(外键约束)。这样做允许您为友情列表使用 RelatedManager,友情列表使用智能查询集。您可以使用所有可用的方法,如 filtervalues_list

使用 ManyToManyField关系和属性:

class MyDjangoClass(models.Model):
name = models.CharField(...)
friends = models.ManyToManyField("self")


@property
def friendlist(self):
# Watch for large querysets: it loads everything in memory
return list(self.friends.all())

你可以这样访问用户的好友列表:

joseph = MyDjangoClass.objects.get(name="Joseph")
friends_of_joseph = joseph.friendlist

然而,请注意这些关系是对称的: 如果约瑟夫是鲍勃的朋友,那么鲍勃就是约瑟夫的朋友。

如果在 Postgres 中使用 Django > = 1.9,则可以利用 ArrayField的优势

用于存储数据列表的字段。可以使用大多数字段类型 只需将另一个字段实例作为 base _ field 传递 可以嵌套 ArrayField 来存储多维数据 数组。

还可以嵌套数组字段:

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


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

正如@thane-brimhall 提到的,也可以直接查询元素

如果你使用 postgres,你可以使用这样的东西:

class ChessBoard(models.Model):


board = ArrayField(
ArrayField(
models.CharField(max_length=10, blank=True),
size=8,
),
size=8,
)

如你需要更多资料,可浏览以下连结: Https://docs.djangoproject.com/pt-br/1.9/ref/contrib/postgres/fields/

在 Django 模型中存储字符串列表:

class Bar(models.Model):
foo = models.TextField(blank=True)
    

def set_list(self, element):
self.foo += "," + element if self.foo else element
    

def get_list(self):
return self.foo.split(",") if self.foo else None

你可以这样说:

bars = Bar()
bars.set_list("str1")
bars.set_list("str2")
bar_list = bars.get_list()


for bar in bar_list:
print bar

因为在2021年这个帖子是第一个在谷歌结果。 这天存在着美好优雅的解决方案

MySql 文档

PostgreSQL

from django.db.models import CharField, Model
from django_mysql.models import ListCharField


class Person(Model):
name = CharField()
post_nominals = ListCharField(
base_field=CharField(max_length=10),
size=6,
max_length=(6 * 11)  # 6 * 10 character nominals, plus commas
)

表格

from django.db.models import IntegerField, Model
from django_mysql.models import ListTextField


class Widget(Model):
widget_group_ids = ListTextField(
base_field=IntegerField(),
size=100,  # Maximum of 100 ids in list
)

质疑

>>> Person.objects.create(name='Horatio', post_nominals=['PhD', 'Esq.', 'III'])
>>> Person.objects.create(name='Severus', post_nominals=['PhD', 'DPhil'])
>>> Person.objects.create(name='Paulus', post_nominals=[])


>>> Person.objects.filter(post_nominals__contains='PhD')
[<Person: Horatio>, <Person: Severus>]


>>> Person.objects.filter(post_nominals__contains='Esq.')
[<Person: Horatio>]


>>> Person.objects.filter(post_nominals__contains='DPhil')
[<Person: Severus>]


>>> Person.objects.filter(Q(post_nominals__contains='PhD') & Q(post_nominals__contains='III'))
[<Person: Horatio>]

因为 ListCharField 是 CharField 的子类,所以也可以设置任何 CharField 选项。最重要的是,您需要设置 max _ length 来确定要在数据库中保留多少个字符。

实例化示例:

from django.db.models import CharField, Model
from django_mysql.models import ListCharField




class Person(Model):
name = CharField()
post_nominals = ListCharField(
base_field=CharField(max_length=10),
size=6,
max_length=(6 * 11),  # 6 * 10 character nominals, plus commas
)

有各种解决潜在路径的答案,如 ArrayField,如果使用 PostgreSQL,通过将列表转换成一个 JSON 字符串,TextField,或者仅仅创建一个新模型,然后使用 外国钥匙,如 我是 Daniel Roseman所指出的。

尽管如此,这个用例中我最喜欢的一个(以及 ForeignKey 选项)被遗漏了。更具体地说,我想参考 JSONField作为一个更好的选择。根据记录,

用于存储 JSON 编码数据的字段。在 Python 中,数据以其 Python 本机格式表示: 字典、列表、字符串、数字、布尔值和无。

MariaDB、 MySQL 5.7.8 + 、 Oracle、 PostgreSQL 和 SQLite (启用了 JSON1扩展)支持 JSONField。

它比 ArrayField有优势,因为它在比 PostgreSQL (从姜戈3.1开始)更多的数据库中得到支持。

此外,它优于 TextField,因为它是有目的地构建来存储 JSON 的,并且对于诸如读取之类的操作需要更少的步骤。

此外,它比自定义字段更好,因为它已经与 Django 一起提供了(不需要额外的工作/维护)。