Enumerating the contexts in which an object is loaded

Assume a Post model with an ForeignKey to User. I count four ways a User object can end up being loaded:

1. Querying model directly:         User.objects.all()
2. Accessing a singleton relation:  post.user
3. Via select_related():            Post.objects.select_related("user").first()
4. Via prefetch_related():          Post.objects.prefetch_related("user").first()

And which User Manager is invoked in each case:

1. default manager
2. base manager
3. -
4. base manager

Do I have that right?

<conjecture>
#1 - The default manager, unless you assign the objects attribute to a user-defined manager or define a manager by a different name first as the default. See Managers | Django documentation | Django

#2 - Specifically, an instance of Model._base_manager.

#3 - I don’t believe a manager is used here. This is a supplement function for the manager currently being used. It modifies the current query rather than adding a query of its own.

#4 - It appears that the model’s default manager would be used. See Making queries | Django documentation | Django
</conjecture>

1 Like

Also, I count two ways to add non-field attributes to a model instance:

  1. Using annotate:
u = User.objects.annotate(post_count=Count("post")).first()
u.post_count
  1. Defining a property:
class User(models.Model):
    ...
    @property
    def post_count(self):
        return self.post_set.count()

u = User.objects.first()
u.post_count

Yes it’s an attribute - but it’s a function. The @property decorator is little more than syntactic sugar. Fundamentally, there’s very little difference between that and any other function being added to a class.