问题:在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关系,但是我希望避免代码中的额外间接访问。
编辑:
我添加了这个相关问题,人们可能会发现它很有用。
Currently I have a lot of python objects in my code similar to the following:
class MyClass():
def __init__(self, name, friends):
self.myName = name
self.myFriends = [str(x) for x in friends]
Now I want to turn this into a Django model, where self.myName is a string field, and self.myFriends is a list of strings.
from django.db import models
class myDjangoModelClass():
myName = models.CharField(max_length=64)
myFriends = ??? # what goes here?
Since the list is such a common data structure in python, I sort of expected there to be a Django model field for it. I know I can use a ManyToMany or OneToMany relationship, but I was hoping to avoid that extra indirection in the code.
Edit:
I added this related question, which people may find useful.
回答 0
将这种关系更好地表示为Friends
表的一对多外键关系吗?我知道这myFriends
只是字符串,但我认为更好的设计是创建一个Friend
模型,并MyClass
在结果表中包含外键。
Would this relationship not be better expressed as a one-to-many foreign key relationship to a Friends
table? I understand that myFriends
are just strings but I would think that a better design would be to create a Friend
model and have MyClass
contain a foreign key realtionship to the resulting table.
回答 1
“过早的优化是万恶之源。”
牢记这一点,让我们开始吧!一旦您的应用达到特定点,对数据进行非规范化就非常普遍。正确完成后,它可以节省大量昂贵的数据库查找,但需要多做一些整理工作。
要返回一个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()
“Premature optimization is the root of all evil.”
With that firmly in mind, let’s do this! Once your apps hit a certain point, denormalizing data is very common. Done correctly, it can save numerous expensive database lookups at the cost of a little more housekeeping.
To return a list
of friend names we’ll need to create a custom Django Field class that will return a list when accessed.
David Cramer posted a guide to creating a SeperatedValueField on his blog. Here is the code:
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)
The logic of this code deals with serializing and deserializing values from the database to Python and vice versa. Now you can easily import and use our custom field in the model class:
from django.db import models
from custom.fields import SeparatedValuesField
class Person(models.Model):
name = models.CharField(max_length=64)
friends = SeparatedValuesField()
回答 2
在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'>
A simple way to store a list in Django is to just convert it into a JSON string, and then save that as Text in the model. You can then retrieve the list by converting the (JSON) string back into a python list. Here’s how:
The “list” would be stored in your Django model like so:
class MyModel(models.Model):
myList = models.TextField(null=True) # JSON-serialized (text) version of your list
In your view/controller code:
Storing the list in the database:
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()
Retrieving the list from the database:
jsonDec = json.decoder.JSONDecoder()
myPythonList = jsonDec.decode(myModel.myList)
Conceptually, here’s what’s going on:
>>> 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'>
回答 3
如果您将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所提到的,也可以直接查询元素。文档参考
If you are using Django >= 1.9 with Postgres you can make use of ArrayField advantages
A field for storing lists of data. Most field types can be used, you
simply pass another field instance as the base_field. You may also
specify a size. ArrayField can be nested to store multi-dimensional
arrays.
It is also possible to nest array fields:
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,
)
As @thane-brimhall mentioned it is also possible to query elements directly. Documentation reference
回答 4
由于这是一个古老的问题,自此之后Django技术必定发生了重大变化,因此该答案反映了Django 1.4版,并且很可能适用于v 1.5。
Django默认使用关系数据库。您应该利用’em。使用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
但是请注意,这些关系是对称的:如果约瑟夫是鲍勃的朋友,那么鲍勃是约瑟夫的朋友。
As this is an old question, and Django techniques must have changed significantly since, this answer reflects Django version 1.4, and is most likely applicable for v 1.5.
Django by default uses relational databases; you should make use of ’em. Map friendships to database relations (foreign key constraints) with the use of ManyToManyField. Doing so allows you to use RelatedManagers for friendlists, which use smart querysets. You can use all available methods such as filter
or values_list
.
Using ManyToManyField
relations and properties:
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())
You can access a user’s friend list this way:
joseph = MyDjangoClass.objects.get(name="Joseph")
friends_of_joseph = joseph.friendlist
Note however that these relations are symmetrical: if Joseph is a friend of Bob, then Bob is a friend of Joseph.
回答 5
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')
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')
回答 6
请记住,这最终必须在关系数据库中结束。因此,使用关系确实是解决此问题的常用方法。如果绝对要在对象本身中存储列表,则可以使用逗号分隔列表,然后将其存储在字符串中,然后提供将字符串拆分为列表的访问器函数。这样,您将被限制为最大数量的字符串,并且您将失去有效的查询。
Remember that this eventually has to end up in a relational database. So using relations really is the common way to solve this problem. If you absolutely insist on storing a list in the object itself, you could make it for example comma-separated, and store it in a string, and then provide accessor functions that split the string into a list. With that, you will be limited to a maximum number of strings, and you will lose efficient queries.
回答 7
回答 8
回答 9
在Django模型中存储字符串列表:
class Bar(models.Model):
foo = models.TextField(blank=True)
def set_list(self, element):
if self.foo:
self.foo = self.foo + "," + element
else:
self.foo = element
def get_list(self):
if self.foo:
return self.foo.split(",")
else:
None
您可以这样称呼它:
bars = Bar()
bars.set_list("str1")
bars.set_list("str2")
list = bars.get_list()
if list is not None:
for bar in list:
print bar
else:
print "List is empty."
Storing a list of strings in Django model:
class Bar(models.Model):
foo = models.TextField(blank=True)
def set_list(self, element):
if self.foo:
self.foo = self.foo + "," + element
else:
self.foo = element
def get_list(self):
if self.foo:
return self.foo.split(",")
else:
None
and you can call it like this:
bars = Bar()
bars.set_list("str1")
bars.set_list("str2")
list = bars.get_list()
if list is not None:
for bar in list:
print bar
else:
print "List is empty."
回答 10
我的解决方案可能是它可以帮助某人:
import json
from django.db import models
class ExampleModel(models.Model):
_list = models.TextField(default='[]')
@property
def list(self):
return json.loads(self._list)
@list.setter
def list(self, value):
self._list = json.dumps(self.list + value)
My solution, may be it helps someone:
import json
from django.db import models
class ExampleModel(models.Model):
_list = models.TextField(default='[]')
@property
def list(self):
return json.loads(self._list)
@list.setter
def list(self, value):
self._list = json.dumps(self.list + value)
回答 11
使用一对多关系(从Friend到父类的FK)将使您的应用程序更具可伸缩性(因为您可以使用简单名称之外的其他属性来简单地扩展Friend对象)。因此这是最好的方法
Using one-to-many relation (FK from Friend to parent class) will make your app more scalable (as you can trivially extend the Friend object with additional attributes beyond the simple name). And thus this is the best way