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


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

Відповіді:


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видається справжнім класом моделі, а не екземпляром відповідно до коду, який він надав.
Даніель ДіПаоло

@Daniel: правда. І все ж помилка Manager isn't accessible via Foo instancesможлива лише при спробі отримати доступ Managerдо екземпляра, що використовується. Див. Вихідний код: code.djangoproject.com/svn/django/trunk/django/db/models/…
Manoj Govindan

4
Дійсно, можливо, ще одна причина (крім "найкращої практики") не використовувати малі літери для імен класів :) Здавалося б, він потенційно використовує topicяк локальну змінну екземпляра і продуває посилання на клас.
Даніель ДіПаоло

2
Вам слід було скористатисяtopic.model_class().objects
Німо

7
Ви також можете використовувати topic.__class__.objects. Здавалося б, model_class()згадане вище @Nimo не працює
сонливий

54
topic.__class__.objects.get(id=topic_id)

Працює з 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 є приватними, тому рекомендується використовувати їх лише у тому випадку, якщо ви знаходитесь всередині моделі Topic, наприклад, коли ви хочете використовувати 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', ...]

5
Дякую, ця відповідь саме те, що я шукав. Я хотів би, щоб я міг голосувати не раз. Мій варіант використання для цього - коли ви додаєте функціональність до абстрактної моделі, де ви не будете знати (на цьому рівні), як називається кінцевий клас моделі.
fadedbee

2
Або використовувати topic.__class__.objects.get(id=topic_id).
Bentley4,

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()
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.