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

193 投票
15 回答
221622 浏览
提问于 2025-04-15 12:49

目前我在代码中有很多类似下面的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会有一个模型字段来处理它。我知道可以使用多对多或一对多的关系,但我希望能避免在代码中增加那种额外的复杂性。

编辑:

我添加了这个相关问题,可能会对大家有帮助。

15 个回答

68

在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'>
152

"过早优化是万恶之源."

记住这一点,我们开始吧!当你的应用程序发展到一定阶段后,数据的非规范化是很常见的做法。如果做得对,它可以节省很多昂贵的数据库查询时间,代价只是需要多花一点时间来整理数据。

为了返回一个朋友名字的 list,我们需要创建一个自定义的 Django 字段类,这样在访问时就能返回一个列表。

David Cramer 在他的博客上发布了关于如何创建一个 SeparatedValueField 的指南。以下是代码:

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()
100

这个关系是不是更适合用一个一对多的外键关系来表示,比如指向一个叫做Friends的表?我明白myFriends只是一些字符串,但我觉得更好的设计是创建一个Friend模型,然后让MyClass和这个表之间建立外键关系。

撰写回答