Модель прокси Джанго и ForeignKey - PullRequest
15 голосов
/ 08 октября 2010

Как сделать 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

Преобразование из Category в CategoryProxy тоже нормально, но я не очень хорошо знаком с внутренностями ORM для правильного копирования внутреннего состояния ...

EDIT. Причина: я добавил метод в CategoryProxy и хочу использовать его:

EntryProxy.objects.get(pk=1).category.method_at_category_proxy()

РЕДАКТИРОВАТЬ 2. В настоящее время я реализовал это так:

EntryProxy._meta.get_field_by_name('category')[0].rel.to = CategoryProxy

но выглядит ужасно ...

Ответы [ 7 ]

12 голосов
/ 09 августа 2011

Чтобы переключиться с класса модели на прокси-класс без обращения к базе данных:

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
6 голосов
/ 26 июля 2013

Это открытая проблема Django: # 10961 (разрешить пользователям переопределять прямые и обратные отношения на моделях прокси с полями ForeignKey)

Вы можете обойти это, сбросив соответствующие поля после определения моделей прокси:

EntryProxy.add_to_class('category', CategoryProxy)
1 голос
/ 31 января 2014

На этот вопрос уже есть принятый ответ, но он хотел опубликовать его для всех, кто может искать.

Вы можете исправить модель во время выполнения с новым полем, чтобы отношения работали, как и ожидалось.Полный пример можно увидеть здесь - 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))
0 голосов
/ 30 ноября 2018

Ни одно из текущих решений (включая принятое) не работает с Django 2.0.

Опираясь на работы Мэтта Шинкеля по переопределению отношений модели прокси , вот решение , которое будетработа с Django 2.0 и 2.1 .

0 голосов
/ 27 января 2012

Немного адаптируя ответ Бернда Петерзона, мы получим:

class EntryProxy(Entry):
    @property
    def category(self):
        return CategoryProxy.objects.get(id=self.category_id)

Это должно быть более экономичным с базой данных.Для добавления улучшений вы можете установить приватный атрибут (self._category) при первом вызове метода, а затем вернуть его все последующие разы.

0 голосов
/ 09 октября 2010

Ответ Джозефа Спироса на один из моих вопросов может помочь вам:

Наследование Джанго и постоянные ссылки

Я не уверен, как это будет работать с моделями прокси.

0 голосов
/ 08 октября 2010

Определите свойство category в EntryProxy, которое ищет CategoryProxy по его идентификатору:

class EntryProxy(Entry):
    @property
    def category(self):
        cid = super(EntryProxy, self).category.id
        return CategoryProxy.objects.get(id=cid)

    class Meta:
        proxy = True
...