Auf den Manager kann nicht über Modellinstanzen zugegriffen werden

87

Ich versuche, eine Modellobjektinstanz in einer anderen zu erhalten, und es wird der folgende Fehler ausgelöst:

 Manager isn't accessible via topic instance

Hier ist mein Modell:

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()

Hier ist meine Ansicht:

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

Und ich bekomme:

post.delete()
forum.topic_count = topic.objects.filter(forum = forum).count()
Manager isn't accessible via topic instances
ThomasDurin
quelle

Antworten:

124

Der betreffende Fehler wird verursacht, wenn Sie versuchen, Managerüber eine Instanz des Modells auf das Modell zuzugreifen . Sie haben Klassennamen in Kleinbuchstaben verwendet. Dies macht es schwierig zu sagen, ob der Fehler durch eine Instanz verursacht wird, die auf die zugreift Manageroder nicht. Da andere Szenarien, die diesen Fehler verursachen können, unbekannt sind, gehe ich davon aus, dass Sie die topicVariable irgendwie verwechselt haben , sodass Sie am Ende auf eine Instanz des topicModells anstatt auf die Klasse verweisen .

Diese Linie ist der Schuldige:

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

Sie müssen verwenden:

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

Was läuft falsch? objectsist Managerauf Klassenebene verfügbar, nicht für die Instanzen. Weitere Informationen finden Sie in der Dokumentation zum Abrufen von Objekten . Geldzitat:

Managerssind nur über Modellklassen und nicht über Modellinstanzen zugänglich , um eine Trennung zwischen Operationen auf Tabellenebene und Operationen auf Datensatzebene zu erzwingen.

(Betonung hinzugefügt)

Aktualisieren

Siehe die Kommentare von @Daniel unten. Es ist eine gute Idee (nein, Sie MÜSSEN: P), die Groß- und Kleinschreibung für Klassennamen zu verwenden. Zum Beispiel Topicstatt topic. Ihre Klassennamen führen zu Verwirrung, unabhängig davon, ob Sie sich auf eine Instanz oder eine Klasse beziehen. Da das Manager isn't accessible via <model> instancessehr spezifisch ist, kann ich eine Lösung anbieten. Der Fehler ist möglicherweise nicht immer so selbstverständlich.

Manoj Govindan
quelle
Es topicscheint jedoch die tatsächliche Modellklasse zu sein und keine Instanz gemäß dem von ihm bereitgestellten Code.
Daniel DiPaolo
@ Daniel: stimmt. Der Fehler Manager isn't accessible via Foo instancesist jedoch nur möglich, wenn Sie versuchen, Managermithilfe einer Instanz auf eine zuzugreifen . Siehe den Quellcode: code.djangoproject.com/svn/django/trunk/django/db/models/…
Manoj Govindan
4
In der Tat vielleicht ein weiterer Grund (außer "es ist eine bewährte Methode"), keine Kleinbuchstaben für Klassennamen zu verwenden :) Es scheint, dass er möglicherweise topicals lokale Instanzvariable verwendet und den Verweis auf die Klasse wegbläst.
Daniel DiPaolo
2
Sie sollten verwendet habentopic.model_class().objects
Nimo
7
Sie könnten auch verwenden topic.__class__.objects. Es scheint model_class()von @Nimo oben erwähnt zu scheinen , funktioniert nicht
Sleepycal
54
topic.__class__.objects.get(id=topic_id)
mihaicc
quelle
Funktioniert ab Django v1.10.
James
3
Dies __class__funktioniert auch besser für Methoden in abstrakten Modellen, da wir den tatsächlichen Namen der Nachkommenklasse nicht kennen. In dieser Situation habe ichself.__class__.objects.get
Cometsong
33

Für Django <1.10

topic._default_manager.get(id=topic_id)

Obwohl Sie es nicht so verwenden sollten. Der _default_manager und der _base_manager sind privat. Es wird daher empfohlen, sie nur zu verwenden, wenn Sie sich im Topic-Modell befinden, z. B. wenn Sie den Manager in einer proprietären Funktion verwenden möchten.

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
quelle
5
Danke, diese Antwort war genau das, wonach ich gesucht hatte. Ich wünschte, ich könnte mehr als einmal abstimmen. Mein Anwendungsfall hierfür ist, wenn Sie einem abstrakten Modell Funktionen hinzufügen, bei denen Sie (auf dieser Ebene) nicht wissen, wie die endgültige Modellklasse heißt.
Fadedbee
2
Oder verwenden topic.__class__.objects.get(id=topic_id).
Bentley4
1
Dies ist eine alte Antwort, aber ab Django v1.10 sehe ich diese privaten Methoden nicht mehr. Allerdings self.__class__.objectsmacht der Trick nach Ihrer anderen Antwort.
James
5

Könnte auch durch ein Paar zu viele Klammern verursacht werden, z

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

statt der richtigen

ModelClass.objects.filter(...)

Passiert mir manchmal, wenn bpython (oder eine IDE) automatisch Klammern hinzufügt.

Das Ergebnis ist natürlich dasselbe - Sie haben eine Instanz anstelle einer Klasse.

Markus
quelle
0

Wenn das Thema eine ContentType-Instanz wäre (was nicht der Fall ist), hätte dies funktioniert:

topic.model_class().objects.filter(forum = forum)
Nimo
quelle
model_class()ist eine Methode des ContentTypeModells. Andere Modellinstanzen, einschließlich topic, haben keine model_classMethode.
Alasdair
Entschuldigung, ich muss die Frage falsch verstanden haben. Ich habe versucht, eine scheinbar ähnliche Frage zu lösen ...
Nimo
0

Ich hatte gerade ein ähnliches Problem wie dieser Fehler. Und wenn Sie auf Ihren Code zurückblicken, scheint es, dass dies auch Ihr Problem sein könnte. Ich denke, Ihr Problem ist, dass Ihr Vergleich von "id" mit "int (topic_id)" und topic_id nicht festgelegt ist.

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

Ich vermute, Ihr Code sollte "post_id" und nicht "topic_id" verwenden.

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