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

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模型字段。我知道我可以使用多人或一个人的关系,但我希望在代码中避免这种额外的间接性。

编辑:

我增加了这个相关的问题,人们会觉得哪些有用。


"过早的优化是万恶之源。"

牢牢记住这一点,让我们开始吧!一旦你的应用程序达到某一点,反规范化数据就很常见了。如果做得正确,它可以节省大量昂贵的数据库查找,但要花费更多的内务管理。

要返回一个list的朋友名,我们需要创建一个自定义的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()


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


在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。您可以使用所有可用的方法,如filtervalues_list

使用ManyToManyField关系和属性:

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)将使您的应用程序更具可扩展性(因为您可以用简单名称之外的其他属性来扩展朋友对象)。因此这是最好的方法