Менеджер недоступен через экземпляры модели

87

Я пытаюсь получить экземпляр объекта модели в другом и вызываю эту ошибку:

 Manager isn't accessible via topic instance

Вот моя модель:

class forum(models.Model):
    # Some attributs

class topic(models.Model):
    # Some attributs

class post(models.Model):
    # Some attributs

    def delete(self):
        forum = self.topic.forum
        super(post, self).delete()
        forum.topic_count = topic.objects.filter(forum = forum).count()

Вот мой взгляд:

def test(request, post_id):
    post = topic.objects.get(id = int(topic_id))
    post.delete()

И я получаю:

post.delete()
forum.topic_count = topic.objects.filter(forum = forum).count()
Manager isn't accessible via topic instances
ThomasDurin
источник

Ответы:

126

Рассматриваемая ошибка возникает, когда вы пытаетесь получить доступ к Managerмодели через экземпляр модели. Вы использовали имена классов в нижнем регистре . Из-за этого сложно сказать, вызвана ли ошибка экземпляром, обращающимся к Managerфайлу. Поскольку другие сценарии, которые могут вызвать эту ошибку, неизвестны, я исхожу из предположения, что вы каким-то образом перепутали topicпеременную, так что вы в конечном итоге указываете на экземпляр topicмодели вместо класса.

Эта строка является виновником:

forum.topic_count = topic.objects.filter(forum = forum).count()
#                   ^^^^^

Вы должны использовать:

forum.topic_count = Topic.objects.filter(forum = forum).count()
#                   ^^^^^
#                   Model, not instance.

Что не так? objectsэто Managerдоступно на уровне класса, а не к экземплярам. Подробности см. В документации по извлечению объектов . Денежная цитата:

Managersдоступны только через классы модели, а не из экземпляров модели, чтобы обеспечить разделение между операциями «уровня таблицы» и операциями «уровня записи».

(Курсив добавлен)

Обновить

См. Комментарии от @Daniel ниже. Это хорошая идея (нет, вы ДОЛЖНЫ: P) использовать регистр заголовка для имен классов. Например, Topicвместо topic. Имена ваших классов вызывают некоторую путаницу, имеете ли вы в виду экземпляр или класс. Поскольку Manager isn't accessible via <model> instancesэто очень конкретный вопрос, я могу предложить решение. Ошибка не всегда может быть настолько очевидной.

Манодж Говиндан
источник
Однако, topicпохоже, это реальный класс модели, а не экземпляр в соответствии с предоставленным им кодом.
Даниэль ДиПаоло
@ Даниэль: правда. И все же ошибка Manager isn't accessible via Foo instancesвозможна только при попытке доступа Managerк экземпляру с использованием экземпляра. См. Исходный код: code.djangoproject.com/svn/django/trunk/django/db/models/…
Манодж Говиндан,
4
В самом деле, возможно, еще одна причина (кроме «это лучшая практика») не использовать строчные буквы в именах классов :) Похоже, он потенциально использует topicв качестве локальной переменной экземпляра и удаляет ссылку на класс.
Даниэль ДиПаоло
2
Вы должны были использоватьtopic.model_class().objects
Nimo
7
Вы также можете использовать topic.__class__.objects. Казалось бы, model_class()упомянутое выше @Nimo не работает
sleepycal
54
topic.__class__.objects.get(id=topic_id)
Mihaicc
источник
Работает с Django v1.10.
Джеймс
3
Это также __class__лучше работает для методов в абстрактных моделях, поскольку мы не знаем фактического имени класса-потомка. В этой ситуации я использовалself.__class__.objects.get
Cometsong
33

Для django <1.10

topic._default_manager.get(id=topic_id)

Хотя не стоит так использовать. _Default_manager и _base_manager являются частными, поэтому рекомендуется использовать их только в том случае, если вы находитесь внутри модели Тема, например, когда вы хотите использовать Manager в проприетарной функции, скажем:

class Topic(Model):
.
.
.
    def related(self)
        "Returns the topics with similar starting names"
        return self._default_manager.filter(name__startswith=self.name)

topic.related() #topic 'Milan wins' is related to:
# ['Milan wins','Milan wins championship', 'Milan wins by one goal', ...]
Mihaicc
источник
5
Спасибо, этот ответ был именно тем, что я искал. Хотел бы я проголосовать больше одного раза. Мой вариант использования для этого - когда вы добавляете функциональность в абстрактную модель, когда вы не знаете (на этом уровне), как называется последний класс модели.
fadedbee
2
Или используйте topic.__class__.objects.get(id=topic_id).
Bentley4 07
1
Это старый ответ, но с Django v1.10 я больше не вижу этих частных методов. Тем self.__class__.objectsне менее, трюк с вашим другим ответом.
Джеймс
5

Также может быть вызвано слишком большим количеством скобок, например

ModelClass().objects.filter(...)

вместо правильного

ModelClass.objects.filter(...)

У меня иногда случается, когда bpython (или IDE) автоматически добавляет скобки.

Результат, конечно же, тот же - у вас есть экземпляр вместо класса.

Маркус
источник
0

если бы тема была экземпляром ContentType (а это не так), это сработало бы:

topic.model_class().objects.filter(forum = forum)
Нимо
источник
model_class()это метод ContentTypeмодели. Другие экземпляры модели, в том числе topic, не имеют model_classметода.
Alasdair
Извините, я, должно быть, неправильно прочитал вопрос. Я пытался решить, казалось бы, похожий вопрос ...
Нимо
0

У меня просто возникла проблема, похожая на эту ошибку. И, оглядываясь на ваш код, кажется, что это тоже может быть ваша проблема. Я думаю, ваша проблема в том, что вы сравниваете "id" с "int (topic_id)" и topic_id не установлено.

def test(request, post_id):
    post = topic.objects.get(id = int(topic_id))
    post.delete()

Я предполагаю, что ваш код должен использовать "post_id", а не "topic_id"

def test(request, post_id):
    post = topic.objects.get(id = int(post_id))
    post.delete()
Brianwaganer
источник