110

I'm trying to get model objects instance in another one and I raise this error :

Manager isn't accessible via topic instance

Here's my model :

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

Here's my view :

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

And I get :

post.delete()
forum.topic_count = topic.objects.filter(forum = forum).count()
Manager isn't accessible via topic instances
Super Kai - Kazuya Ito
  • 22,221
  • 10
  • 124
  • 129
ThomasDurin
  • 1,943
  • 2
  • 14
  • 20
  • 1
    you need to use Topic (class name) to access objects (Manager). as mentioned in Django documentation (Managers are accessible only via model classes, rather than from model instances, to enforce a separation between “table-level” operations and “record-level” operations.) – amir jj Nov 11 '22 at 17:00

7 Answers7

165

The error in question is caused when you try to access the Manager of a model through an instance of the model. You have used lower case class names. This makes it hard to say if the error is caused by an instance accessing the Manager or not. Since other scenarios that can cause this error are unknown I am proceeding on the assumption that you have somehow mixed up the topic variable so that you end up pointing to an instance of the topic model instead of the class.

This line is the culprit:

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

You have to use:

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

What is going wrong? objects is a Manager available at the class level, not to the instances. See the documentation for retrieving objects for details. Money quote:

Managers are accessible only via model classes, rather than from model instances, to enforce a separation between "table-level" operations and "record-level" operations.

(Emphasis added)

Update

See the comments from @Daniel below. It is a good idea (nay, you MUST :P) to use title case for class names. For instance Topic instead of topic. Your class names cause some confusion whether you are referring to an instance or a class. Since the Manager isn't accessible via <model> instances is very specific I am able to offer a solution.The error may not be so self evident always.

Manoj Govindan
  • 72,339
  • 21
  • 134
  • 141
  • However, `topic` does appear to be the actual model class, and not an instance according to the code he provided. – Daniel DiPaolo Oct 06 '10 at 16:16
  • @Daniel: true. And yet the error `Manager isn't accessible via Foo instances` is only possible when you try to access a `Manager` using an instance. See the source code: http://code.djangoproject.com/svn/django/trunk/django/db/models/manager.py – Manoj Govindan Oct 06 '10 at 16:18
  • 4
    Indeed, perhaps another reason (other than "it's best practice") not to use lowercase letters for class names :) It would appear he's potentially using `topic` as a local instance variable and blowing away the reference to the class. – Daniel DiPaolo Oct 06 '10 at 16:19
  • 3
    You should have used `topic.model_class().objects` – Nimo Jun 09 '13 at 18:10
  • 7
    You could also use `topic.__class__.objects`. It would seem `model_class()` mentioned by @Nimo above doesn't work – SleepyCal Mar 13 '14 at 16:23
  • Ignoring the obvious issues with the class names, @sleepycal provides the best answer if you're genuinely trying to access the manage based on an instance. – AppHandwerker Jan 21 '19 at 12:04
  • An alternative would be `topic._meta.model.objects` – bdoubleu Aug 29 '19 at 12:04
76
topic.__class__.objects.get(id=topic_id)
mihaicc
  • 3,034
  • 1
  • 24
  • 20
  • Works as of Django v1.10. – James Feb 08 '17 at 20:24
  • 5
    This `__class__` works better for methods within abstract models as well, as we don't know the actual name of the descendant class. In this situation, I used `self.__class__.objects.get` – Cometsong Jun 07 '18 at 17:36
  • Works awesome on a `models.py` model. `def get_object(self): return self.__class__.objects.get(...)`. Using this for a `GenericForeignKey`. – Jarad Oct 18 '21 at 19:39
33

For django < 1.10

topic._default_manager.get(id=topic_id)

Though you should not use it like this. The _default_manager and _base_manager are private, so it's recomended to use them only if you're inside the Topic model, like when you want to use the Manager in a proprietary function let's say:

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
  • 3,034
  • 1
  • 24
  • 20
  • 5
    Thanks, this answer was just what I'd been searching for. I wish I could up vote more than once. My use-case for this is when you're adding functionality to an abstract model, where you won't know (at this level) what the final model class is called. – fadedbee May 20 '12 at 06:11
  • 3
    Or use `topic.__class__.objects.get(id=topic_id)`. – Bentley4 Oct 07 '13 at 12:40
  • 1
    This is an old answer, but as of Django v1.10 I don't see these private methods anymore. However, `self.__class__.objects` does the trick per your other answer. – James Feb 08 '17 at 20:23
12

Could also be caused by a pair of parantheses too much, e.g.

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

instead of the correct

ModelClass.objects.filter(...)

Happens to me sometimes when bpython (or an IDE) automatically adds parantheses.

The result, of course, is the same - you have an instance instead of a class.

Markus
  • 2,412
  • 29
  • 28
0

if topic were a ContentType instance (which it is not), this would have worked:

topic.model_class().objects.filter(forum = forum)
Nimo
  • 7,984
  • 5
  • 39
  • 41
  • [`model_class()`](https://docs.djangoproject.com/en/dev/ref/contrib/contenttypes/#django.contrib.contenttypes.models.ContentType.model_class) is a method of the `ContentType` model. Other model instances, including `topic`, do not have a `model_class` method. – Alasdair Jun 09 '13 at 19:36
  • Sorry, I must have misread the question. I was trying to solve a seemingly-similar question... – Nimo Jun 16 '13 at 15:39
0

I just had an issue similar to this error. And looking back at your code it seems that it could be your issue too. I think your issue is that your comparing "id" to "int(topic_id)" and topic_id is not set.

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

I'm guessing your code should use "post_id" not "topic_id"

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

I got the same error below:

AttributeError: Manager isn't accessible via CustomUser instances

When I accessed Manager with request.user.objects as shown below:

"views.py"

from django.http import HttpResponse

def test(request):
    print(request.user.objects)
    return HttpResponse("Test")
Super Kai - Kazuya Ito
  • 22,221
  • 10
  • 124
  • 129
  • The `objects` manager is accessible at class level and not at instance level. You cannot do `request.user.objects`. Instead use `User.objects`. Not sure what you are trying to print here anyway.. `objects` is the default name of the manager. Why print manager? – Sarang Aug 12 '23 at 10:15