В настоящее время у меня есть много объектов 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:
Я добавил этот связанный вопрос, который люди могут найти полезным.
Будет ли это отношение лучше не выражаться как отношение внешнего ключа "один ко многим" к таблице Friends
? Я понимаю, что myFriends
- это просто строки, но я бы подумал, что лучшим дизайном будет создание модели Friend
и MyClass
будет содержать реальную связь внешнего ключа с результирующей таблицей.
С учетом этого, сделайте это! Когда ваши приложения попадают в определенную точку, денормализация данных очень распространена. Совершенно правильно, он может сэкономить множество дорогостоящих поисков баз данных за счет немного более домашнего хозяйства.
Чтобы вернуть 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()
Простой способ сохранить список в 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 и, скорее всего, применим для версии 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
Обратите внимание, что эти отношения симметричны: если Джозеф является другом Боба, тогда Боб - друг Иосифа.
Если вы используете 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')
Если вы используете 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 Pickle Field, ala этот фрагмент:
Сохранение списка строк в модели 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."
Использование отношения "один ко многим" (FK от друга к родительскому классу) сделает ваше приложение более масштабируемым (так как вы можете тривиально расширить объект Friend с дополнительными атрибутами за пределами простого имени). И, таким образом, это лучший способ