Как наиболее эффективно сохранить список в моделях Django? - PullRequest
116 голосов
/ 10 июля 2009

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

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

Ответы [ 11 ]

109 голосов
/ 11 июля 2009

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

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

Чтобы вернуть 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()
61 голосов
/ 10 июля 2009

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

30 голосов
/ 22 августа 2011

Простой способ сохранить список в 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'>
15 голосов
/ 28 марта 2016

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

15 голосов
/ 05 февраля 2013

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

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

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

9 голосов
/ 10 июля 2009
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 голосов
/ 22 июля 2016

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

6 голосов
/ 10 июля 2009

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

4 голосов
/ 10 июля 2009

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

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

2 голосов
/ 17 апреля 2018

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