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

gri*_*eve 122 python django django-models

目前我的代码中有很多python对象,类似于以下内容:

class MyClass():
  def __init__(self, name, friends):
      self.myName = name
      self.myFriends = [str(x) for x in friends]
Run Code Online (Sandbox Code Playgroud)

现在我想把它变成一个Django模型,其中self.myName是一个字符串字段,self.myFriends是一个字符串列表.

from django.db import models

class myDjangoModelClass():
    myName = models.CharField(max_length=64)
    myFriends = ??? # what goes here?
Run Code Online (Sandbox Code Playgroud)

由于列表是python中常见的数据结构,我希望它有一个Django模型字段.我知道我可以使用ManyToMany或OneToMany关系,但我希望避免代码中的额外间接.

编辑:

我添加了这个相关的问题,人们可能觉得有用.

jb.*_*jb. 113

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

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

要返回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)
Run Code Online (Sandbox Code Playgroud)

此代码的逻辑涉及将数据库中的值序列化和反序列化为Python,反之亦然.现在,您可以轻松地在模型类中导入和使用我们的自定义字段:

from django.db import models
from custom.fields import SeparatedValuesField 

class Person(models.Model):
    name = models.CharField(max_length=64)
    friends = SeparatedValuesField()
Run Code Online (Sandbox Code Playgroud)

  • +1代表一个很好的答案,但我们已经做了类似的事情.它实际上是将所有值压缩成一个字符串然后将它们分开.我想我希望更像ListofStringsField,它实际上构建了单独的表并自动生成外键.我不确定Django是否可行.如果是,我找到了答案,我会将它发布在stackoverflow上. (6认同)
  • +1.但是字符串中的逗号可能存在​​问题.如何从json序列化和反序列化? (3认同)
  • 如果是这样,那么你正在寻找initcrash的django-denorm.你可以在github上找到它:http://github.com/initcrash/django-denorm/tree/master (2认同)

And*_*are 61

这种关系不能更好地表达为与Friends表格的一对多外键关系吗?我知道这myFriends只是字符串,但我认为更好的设计是创建一个Friend模型,并MyClass在结果表中包含一个外键实现.

  • 这可能是我最终要做的事情,但我真的希望这个底层结构能够内置.我想我是懒惰的. (12认同)

min*_*ief 32

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

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

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

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

将列表存储在数据库中:

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()
Run Code Online (Sandbox Code Playgroud)

从数据库中检索列表:

jsonDec = json.decoder.JSONDecoder()
myPythonList = jsonDec.decode(myModel.myList)
Run Code Online (Sandbox Code Playgroud)

从概念上讲,这是正在发生的事情:

>>> 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'>
Run Code Online (Sandbox Code Playgroud)

  • 不幸的是,这无法帮助您使用django admin管理列表 (6认同)

wol*_*anh 22

如果你在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,
    )
Run Code Online (Sandbox Code Playgroud)

正如@ thane-brimhall所提到的,也可以直接查询元素.文档参考

  • 应该提到的是,这仅适用于 PostGres。 (3认同)
  • 这样做的一大优点是您可以直接从数组字段中查询元素. (2认同)
  • Django 1.8 也有 ArrayField:https://docs.djangoproject.com/en/1.8/ref/contrib/postgres/fields/ (2认同)

sle*_*anc 15

由于这是一个老问题,并且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())
Run Code Online (Sandbox Code Playgroud)

您可以通过以下方式访问用户的好友列表:

joseph = MyDjangoClass.objects.get(name="Joseph")
friends_of_joseph = joseph.friendlist
Run Code Online (Sandbox Code Playgroud)

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


Rob*_*ert 12

因为到了 2021 年,这篇文章在 google 结果中排名第一。如今存在很好的优雅解决方案

MySql 文档

PostgreSQL

from django.db.models import CharField, Model
from django_mysql.models import ListCharField

class Person(Model):
    name = CharField()
    post_nominals = ListCharField(
        base_field=CharField(max_length=10),
        size=6,
        max_length=(6 * 11)  # 6 * 10 character nominals, plus commas
    )
Run Code Online (Sandbox Code Playgroud)

形式

from django.db.models import IntegerField, Model
from django_mysql.models import ListTextField

class Widget(Model):
    widget_group_ids = ListTextField(
        base_field=IntegerField(),
        size=100,  # Maximum of 100 ids in list
    )
Run Code Online (Sandbox Code Playgroud)

询问

>>> Person.objects.create(name='Horatio', post_nominals=['PhD', 'Esq.', 'III'])
>>> Person.objects.create(name='Severus', post_nominals=['PhD', 'DPhil'])
>>> Person.objects.create(name='Paulus', post_nominals=[])

>>> Person.objects.filter(post_nominals__contains='PhD')
[<Person: Horatio>, <Person: Severus>]

>>> Person.objects.filter(post_nominals__contains='Esq.')
[<Person: Horatio>]

>>> Person.objects.filter(post_nominals__contains='DPhil')
[<Person: Severus>]

>>> Person.objects.filter(Q(post_nominals__contains='PhD') & Q(post_nominals__contains='III'))
[<Person: Horatio>]
Run Code Online (Sandbox Code Playgroud)


And*_*yuk 9

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')
Run Code Online (Sandbox Code Playgroud)


Mar*_*wis 6

请记住,这最终必须以关系数据库结束.所以使用关系确实解决这个问题的常用方法.如果您绝对坚持在对象本身中存储列表,则可以将其设置为逗号分隔,并将其存储在字符串中,然后提供将字符串拆分为列表的访问器函数.这样,您将被限制为最大字符串数,并且您将失去有效的查询.

  • 我很好地将数据库存储为关系,我希望Django模型已经为我抽出了那部分.从应用程序方面来看,我总是希望将其视为字符串列表. (3认同)

小智 6

如果你使用postgres,你可以使用这样的东西:

class ChessBoard(models.Model):

    board = ArrayField(
        ArrayField(
            models.CharField(max_length=10, blank=True),
            size=8,
        ),
        size=8,
    )
Run Code Online (Sandbox Code Playgroud)

如果您需要更多详细信息,请参阅以下链接:https: //docs.djangoproject.com/pt-br/1.9/ref/contrib/postgres/fields/


Aht*_*ham 5

在 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(",")
        return None
Run Code Online (Sandbox Code Playgroud)

你可以这样称呼它:

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."      
Run Code Online (Sandbox Code Playgroud)