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

108

В настоящее время у меня есть много объектов 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
    @drozzy: Ну, наверное, я мог бы использовать другую фразу, но в основном я имел в виду, что я хочу передать список строк и получить список строк. Я не хочу создавать группу объектов Friend и вызывать inst.myFriends.add (friendObj) для каждого из них. Не то чтобы это было так сложно, но ...
Теги:
django-models

11 ответов

59
Лучший ответ

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

  • 10
    Это, вероятно, то, что я в конечном итоге сделаю, но я действительно надеялся, что основная структура для этого будет встроена. Я думаю, что я ленивый.
107

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

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

Чтобы вернуть 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()
  • 5
    +1 за отличный ответ, но мы уже делаем что-то подобное. Это действительно сжимает все значения в одну строку, а затем разделяет их. Наверное, я надеялся на что-то более похожее на ListofStringsField, которое фактически создает отдельную таблицу и автоматически создает внешние ключи. Я не уверен, возможно ли это в Джанго. Если это так, и я найду ответ, я опубликую его на stackoverflow.
  • 2
    Если это так, то вы ищете django-denorm initcrash. Вы найдете его на github: github.com/initcrash/django-denorm/tree/master
Показать ещё 2 комментария
29

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

Поскольку это старый вопрос, и методы 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

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

13

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

  • 2
    Большим преимуществом этого является то, что вы можете запрашивать элементы непосредственно из поля массива.
  • 0
    @ ThaneBrimhall ты прав. Может быть, я должен обновить ответ с этой информацией. Спасибо
Показать ещё 2 комментария
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')
6

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

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

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

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

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

  • 0
    Который зависит от базы данных.
  • 3
    Нет, это не так. Прочитайте описание фрагмента.
Показать ещё 1 комментарий
1

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

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

  • 2
    Это не масштабируемость, это расширяемость. Часто один за счет другого. В этом случае, если вы знаете, что вам всегда будет нужен список строк, вы можете избежать дорогостоящего объединения, что сделает ваш код более масштабируемым (т.е. более производительным в результате денормализации).
  • 0
    Выше приведено несколько предостережений: 1) вы знаете, что никогда не хотите запрашивать эти данные, и 2) хранилище все еще дешевле, чем вычислительная мощность и память (кто знает, может быть, это меняется с квантовыми вычислениями)

Ещё вопросы

Сообщество Overcoder
Наверх
Меню