Каков наиболее эффективный способ хранения списка в моделях Django?

В настоящее время у меня есть много объектов python в моем коде, подобных следующему:

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

теперь я хочу превратить это в модель Django, где self.myName-это строковое поле и self.друзьями-это список строк.

from django.db import models

class myDjangoModelClass():
    myName = models.CharField(max_length=64)
    myFriends = ??? # what goes here?

поскольку список является такой общей структурой данных в python, я ожидал, что для него будет поле модели Django. Я знаю, что могу использовать отношения ManyToMany или OneToMany, но я надеялся избежать этой дополнительной косвенности в код.

Edit:

я добавил этот вопрос, которые люди могут счесть полезным.

11 ответов


не будет ли это отношение лучше выражено как отношение внешнего ключа "один ко многим" к Friends таблицы? Я понимаю это myFriends просто строки, но я думаю, что лучше было бы создать Friend модель и есть MyClass содержит внешний ключ realtionship к результирующей таблице.


"преждевременная оптимизация-корень всех зол."

С этим твердо в виду, давайте сделаем это! После того, как ваши приложения попали в определенную точку, денормализация данных очень распространена. Сделано правильно, это может сэкономить многочисленные дорогостоящие поиски базы данных за счет немного больше уборки.

возвратить list имен друзей нам нужно создать пользовательский класс полей Django, который будет возвращать список при доступе.

Дэвид Крамер опубликовал руководство по созданию 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)

логика этого кода связана с сериализацией и десериализацией значений из базы данных в Python и наоборот. Теперь вы можете легко импортировать и использовать наше настраиваемое поле в классе модели:

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 следующим образом:

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

поскольку это старый вопрос, и методы Django должны значительно измениться, так как этот ответ отражает версию Django 1.4 и, скорее всего, применим для v 1.5.

в Django по умолчанию использует реляционные базы данных, вы должны использовать их. Сопоставьте дружеские отношения с отношениями базы данных (ограничения внешнего ключа) с использованием ManyToManyField. Это позволяет использовать RelatedManagers для списков друзей, которые используют интеллектуальные запросы. Можно использовать все доступные методы, такие как filter или values_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

Если вы используете Django > = 1.9 с Postgres, вы можете использовать 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, это также можно напрямую запрашивать элементы. Документация ссылка


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')

помните, что это в конечном итоге должно оказаться в реляционной базе данных. Так что использование отношений действительно is общий способ решения этой проблемы. Если вы абсолютно настаиваете на сохранении списка в самом объекте, вы можете сделать его, например, разделенным запятыми, и сохранить его в строке, а затем предоставить функции доступа, которые разделяют строку на список. При этом вы будете ограничены максимальным количеством строк, и вы потеряете эффективные запросы.


Если вы используете 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/


вы можете хранить практически любой объект, используя поле рассола Django, ala этот фрагмент:

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


использование отношения "один ко многим" (FK от друга к родительскому классу) сделает ваше приложение более масштабируемым (поскольку вы можете тривиально расширить объект Friend дополнительными атрибутами за пределами простого имени). И таким образом это лучший способ


сохранение списка строк в модели 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(",")
        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."