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

В настоящее время у меня есть много объектов 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:

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

Ответ 1

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

Ответ 2

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

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

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

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

Ответ 3

Простой способ сохранить список в 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'>

Ответ 4

Если вы используете Django> = 1.9 с Postgres, вы можете воспользоваться преимуществами ArrayField advantages

Поле для хранения списков данных. Большинство типов полей могут быть использованы, вы просто передайте другой экземпляр поля как 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, можно также запрашивать элементы напрямую. Документация ссылка

Ответ 5

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

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

Обратите внимание, что эти отношения симметричны: если Джозеф является другом Боба, тогда Боб - друг Иосифа.

Ответ 6

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

Ответ 7

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

Ответ 8

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

Ответ 10

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

Ответ 11

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

Ответ 12

Мое решение, может быть, это кому-то поможет:

import json
from django.db import models


class ExampleModel(models.Model):
    _list = models.TextField(default='[]')

    @property
    def list(self):
        return json.loads(self._list)

    @list.setter
    def list(self, value):
        self._list = json.dumps(self.list + value)