Каков наиболее эффективный способ хранения списка в моделях 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/
использование отношения "один ко многим" (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."