首页 文章

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

提问于
浏览
106

目前我的代码中有很多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关系,但我希望避免代码中的额外间接 .

Edit:

我添加了这个related question,人们可能觉得它很有用 .

11 回答

  • 56

    这种关系是不是更好地表达为与 Friends 表的一对多外键关系?据我所知, myFriends 仅仅是字符串,但我想一个更好的设计是 Build 一个 Friend 模型,并有 MyClass 包含一个外键realtionship到结果表 .

  • 15

    “过早优化是万恶之源 . ”

    牢记这一点,让我们这样做!一旦您的应用程序达到某一点,非正常化数据非常常见 . 如果操作正确,它可以节省大量昂贵的数据库查找,但需要多花一些时间 .

    要返回 list 的朋友名称,我们需要创建一个自定义的Django Field类,它将在访问时返回一个列表 .

    David Cramer posted a guide to creating a SeperatedValueField on his blog . 这是代码:

    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()
    
  • 1

    在Django中存储列表的一种简单方法是将其转换为JSON字符串,然后将其保存为模型中的Text . 然后,您可以通过将(JSON)字符串转换回python列表来检索列表 . 这是如何做:

    “list”将存储在您的Django模型中,如下所示:

    class MyModel(models.Model):
        myList = models.TextField(null=True) # JSON-serialized (text) version of your list
    

    在您的视图/控制器代码中:

    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'>
    
  • 6

    由于这是一个老问题,并且Django技术必须发生重大变化,因此,这个答案反映了Django版本1.4,并且最有可能适用于v 1.5 .

    Django默认使用关系数据库;你应该利用他们 . 使用ManyToManyField将友谊映射到数据库关系(外键约束) . 这样做允许您将RelatedManagers用于使用智能查询集的好友列表 . 您可以使用所有可用的方法,例如 filtervalues_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
    

    但请注意,这些关系是对称的:如果约瑟夫是鲍勃的朋友,那么鲍勃是约瑟夫的朋友 .

  • 13

    如果你在Postgres中使用Django> = 1.9,你可以利用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

  • 29
    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

    请记住,这最终必须以关系数据库结束 . 所以使用关系确实是解决这个问题的常用方法 . 如果你绝对坚持对对象本身存储的列表,你可以把例如逗号分隔,并将其存储在一个字符串,然后再把该字符串分割成一个列表访问功能 . 这样,您将被限制为最大字符串数,并且您将失去有效的查询 .

  • 102

    如果你使用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/

  • 0

    您可以使用Django Pickle Field存储几乎任何对象,这个片段:

    http://www.djangosnippets.org/snippets/513/

  • 4

    使用一个一对多的关系(FK从朋友到父类)可以将您的应用程序更具伸缩性(你可以用平凡超出了简单的名称附加属性扩展朋友的对象) . 因此这是最好的方法

  • 9

    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
    

    你可以这样称呼它:

    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."
    

相关问题