What is the most efficient way to store a list in the Django models?
目前,我的代码中有很多类似于以下内容的python对象:
1 2 3 4 | class MyClass(): def __init__(self, name, friends): self.myName = name self.myFriends = [str(x) for x in friends] |
现在我想把它转换成django模型,self.myname是一个字符串字段,self.myfriends是一个字符串列表。
1 2 3 4 5 | from django.db import models class myDjangoModelClass(): myName = models.CharField(max_length=64) myFriends = ??? # what goes here? |
由于这个列表在Python中是如此常见的数据结构,我有点期待它会有一个django模型字段。我知道我可以使用多人或一个人的关系,但我希望在代码中避免这种额外的间接性。
编辑:
我增加了这个相关的问题,人们会觉得哪些有用。
"过早的优化是万恶之源。"
牢牢记住这一点,让我们开始吧!一旦你的应用程序达到某一点,反规范化数据就很常见了。如果做得正确,它可以节省大量昂贵的数据库查找,但要花费更多的内务管理。
要返回一个
大卫·克莱默在他的博客上贴了一本关于创建一个独立的高级网站的指南。代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | 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的值的序列化和反序列化,反之亦然。现在,您可以轻松地导入和使用模型类中的自定义字段:
1 2 3 4 5 6 | from django.db import models from custom.fields import SeparatedValuesField class Person(models.Model): name = models.CharField(max_length=64) friends = SeparatedValuesField() |
这种关系是否不能更好地表示为与
在Django中存储列表的一个简单方法是将其转换为JSON字符串,然后将其保存为模型中的文本。然后,您可以通过将(json)字符串转换回python列表来检索该列表。以下是如何:
"列表"将存储在Django模型中,如下所示:
1 2 | class MyModel(models.Model): myList = models.TextField(null=True) # JSON-serialized (text) version of your list |
在视图/控制器代码中:
将列表存储在数据库中:
1 2 3 4 5 6 7 8 | 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() |
正在从数据库中检索列表:
1 2 | jsonDec = json.decoder.JSONDecoder() myPythonList = jsonDec.decode(myModel.myList) |
从概念上讲,这是发生的事情:
1 2 3 4 5 6 7 8 9 10 11 12 13 | >>> 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默认使用关系数据库;您应该使用'em.通过使用manytomanyfield将友谊映射到数据库关系(外键约束)。这样做可以让您对使用智能查询集的好友列表使用RelatedManager。您可以使用所有可用的方法,如
使用
1 2 3 4 5 6 7 8 | 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()) |
您可以通过以下方式访问用户的朋友列表:
1 2 | joseph = MyDjangoClass.objects.get(name="Joseph") friends_of_joseph = joseph.friendlist |
但是要注意这些关系是对称的:如果约瑟夫是鲍勃的朋友,那么鲍勃是约瑟夫的朋友。
如果在Postgres中使用django>=1.9,则可以利用arrayfield的优势。
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.
也可以嵌套数组字段:
1 2 3 4 5 6 7 8 9 10 11 | 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所提到的,也可以直接查询元素。文件参考
1 2 3 4 5 6 7 8 9 10 | 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,您可以使用如下内容:
1 2 3 4 5 6 7 8 9 | 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 pickle字段存储几乎所有对象,这段代码是:
http://www.djangosnippets.org/snippets/513/
在Django模型中存储字符串列表:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | 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 |
你可以这样称呼它:
1 2 3 4 5 6 7 8 9 | 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." |
使用一对多关系(从朋友到父类的FK)将使您的应用程序更具可扩展性(因为您可以用简单名称之外的其他属性来扩展朋友对象)。因此这是最好的方法