目前,我的代码中有很多 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 关系,但我希望避免在代码中出现这种额外的间接性。
编辑:
我添加了这个 related question,人们可能会觉得它有用。
“过早的优化是万恶之源。”
牢记这一点,让我们这样做!一旦您的应用程序达到某个点,非规范化数据就很常见了。如果操作正确,它可以节省大量昂贵的数据库查找,但需要多做一些内务处理。
要返回 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()
这种关系不是更好地表示为与 Friends
表的一对多外键关系吗?我知道 myFriends
只是字符串,但我认为更好的设计是创建一个 Friend
模型并让 MyClass
包含对结果表的外键关系。
在 Django 中存储列表的一种简单方法是将其转换为 JSON 字符串,然后将其保存为模型中的文本。然后,您可以通过将 (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 >= 1.9 与 Postgres 一起使用,您可以利用 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 提到的,也可以直接查询元素。文档reference
由于这是一个老问题,并且 Django 技术必须已经发生了重大变化,所以这个答案反映了 Django 版本 1.4,并且很可能适用于 v 1.5。
Django 默认使用关系数据库;你应该利用它们。使用 ManyToManyField 将友谊映射到数据库关系(外键约束)。这样做允许您将 RelatedManagers 用于使用智能查询集的好友列表。您可以使用所有可用的方法,例如 filter
或 values_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
但是请注意,这些关系是对称的:如果约瑟夫是鲍勃的朋友,那么鲍勃就是约瑟夫的朋友。
请记住,这最终必须在关系数据库中结束。所以使用关系确实是解决这个问题的常用方法。如果您绝对坚持将列表存储在对象本身中,则可以将其例如以逗号分隔,并将其存储在字符串中,然后提供将字符串拆分为列表的访问器函数。这样,您将被限制在最大数量的字符串中,并且您将失去有效的查询。
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')
如果您使用的是 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/
因为在 2021 年,这篇文章在谷歌搜索结果中排名第一。这几天存在很好的优雅解决方案
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>]
在 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
您可以使用 Django Pickle Field 存储几乎任何对象,就像这个片段:
http://www.djangosnippets.org/snippets/513/
使用一对多关系(从 Friend 到父类的 FK)将使您的应用程序更具可扩展性(因为您可以使用简单名称之外的其他属性轻松扩展 Friend 对象)。因此这是最好的方法
由于 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
)
不定期副业成功案例分享
my_vals = SeparatedValuesField(blank=True, default="")
的现有模型中,但由于 NULL 而得到 IntegrityError。默认参数是否未正确传递?to_python
中不再调用读取。因此,要完成这项工作,您需要添加:def from_db_value(self, value, expression, connection, context): return self.to_python(value)