Прокси-модель Django и ForeignKey
Как сделать entry.category примером экземпляра CategoryProxy? Подробнее см. В коде:
class Category(models.Model): pass
class Entry(models.Model):
category = models.ForeignKey(Category)
class EntryProxy(Entry):
class Meta:
proxy = True
class CategoryProxy(Category):
class Meta:
proxy = True
entry = EntryProxy.objects.get(pk=1)
entry.category # !!! I want CategoryProxy instance here
Отключение от категории к категорииProxy тоже нормально, но я не очень хорошо знаком с внутренними компонентами ORM, чтобы правильно скопировать внутреннее состояние...
ИЗМЕНИТЬ.
Причина: я добавил метод CategoryProxy и хочу использовать его:
EntryProxy.objects.get(pk=1).category.method_at_category_proxy()
ИЗМЕНИТЬ 2.
В настоящее время я реализовал это следующим образом:
EntryProxy._meta.get_field_by_name('category')[0].rel.to = CategoryProxy
но выглядит ужасно...
Ответы
Ответ 1
Чтобы перейти от класса модели к прокси-классу без попадания в базу данных:
class EntryProxy(Entry):
@property
def category(self):
new_inst = EntryProxy()
new_inst.__dict__ = super(EntryProxy, self).category.__dict__
return new_inst
edit: фрагмент выше, похоже, не работает на django 1.4.
Так как django 1.4, я беру все поля значений вручную следующим образом:
class EntryProxy(Entry):
@property
def category(self):
category = super(EntryProxy, self).category
new_inst = EntryProxy()
for attr in [f.attname for f in category.__class__._meta.fields] + ['_state']:
setattr(new_inst, attr, getattr(category, attr))
return new_inst
Чтобы переключиться с набора запросов на дочерний прокси-класс без попадания в базу данных:
class CategoryProxy(Category):
@property
def entry_set(self):
qs = super(CategoryProxy, self).entry_set
qs.model = EntryProxy
return qs
Ответ 2
Это открытая проблема Django: # 10961 (Разрешить пользователям переопределять отношения форварда и обратного отношения к прокси-моделям с полями ForeignKey)
Вы можете обойти это, сбросив соответствующие поля после определения прокси-моделей:
EntryProxy.add_to_class('category', CategoryProxy)
Ответ 3
У этого вопроса уже есть принятый ответ, но он хотел опубликовать его для всех, кто может найти его.
Вы можете исправить модель во время выполнения с новым полем, чтобы отношения работали должным образом. Полный пример можно увидеть здесь - https://gist.github.com/carymrobbins/8721082
from django.db.models.fields.related import ReverseSingleRelatedObjectDescriptor
def override_model_field(model, field, field_name, column_name):
"""Force override a field in a Django Model.
Usage: override_model_field(
MyModel, models.ForeignKey(OtherModel), 'other', 'other_id')
:type model: django.db.models.base.ModelBase
:type field: django.db.models.fields.Field
:type field_name: basestring
:type column_name: basestring
"""
field.name = field_name
field.attname = column_name
for i, f in enumerate(model._meta.fields):
if f.name == field_name:
model._meta.fields[i] = field
break
else:
raise TypeError('Model {!r} does not have a field {!r}.'
.format(model, field_name))
model.add_to_class(field_name,
ReverseSingleRelatedObjectDescriptor(field))
Ответ 4
Ответ Джозефа Спироса на один из моих вопросов может помочь вам:
Наследование и перформанс Django
Я не уверен, как это будет работать с прокси-моделями.
Ответ 5
Я адаптировал работу Мэтта Шинкеля над переопределением отношений между прокси- образцами и решением для Django 2.0 и 2.1. Код доступен как библиотека на pypi.
Ответ 6
Ни одно из существующих решений (включая принятое) не работает с Django 2.0.
Основываясь на работе Мэтта Шинкеля над переопределением отношений между прокси- образцами, вот решение, которое будет работать с Django 2.0 и 2.1.
Ответ 7
Определите свойство category
в EntryProxy, которое ищет CategoryProxy по его id:
class EntryProxy(Entry):
@property
def category(self):
cid = super(EntryProxy, self).category.id
return CategoryProxy.objects.get(id=cid)
class Meta:
proxy = True
Ответ 8
Адаптация Бернда Петерсона слегка ответит, мы имеем:
class EntryProxy(Entry):
@property
def category(self):
return CategoryProxy.objects.get(id=self.category_id)
Это должно быть более экономичным с базой данных. Для дополнительных улучшений вы можете установить частный атрибут (self._category
) при первом вызове метода, а затем вернуть это все последующие времена.