prefetch_related_objects, how to use, how to work?

The documentation says you can use prefetch_related_objects for recursive lookups.
I think this is used when Prefetching the same model over and over again.
If not, please let me know what this is for.

However, there are no detailed explanations or usage examples for this. (It is impossible to apply it to an actual project by only looking at the examples provided in the document.)

How do I use this?
And how is this different from using Prefetch repeatedly?
How does this work in practice?

Can someone explain this?

# model sample
class A:
  pass

class B:
  aa = models.ForeignKey(A)

class C:
  aa = models.ForeignKey(A)

This is not an accurate statement. At no point within the docs for this function does it make any reference to recursion.

This method is used when you have a list of objects, and not a queryset.

Quoting directly from the docs you referenced:

This is useful in code that receives a list of model instances as opposed to a QuerySet ; for example, when fetching models from a cache or instantiating them manually.

For example, using the classes in the docs, you might write:
restaurants = Restaurant.objects.all().prefetch_related('pizzas')
This use of the prefetch_related is being applied to a queryset (Restaurant.objects.all())

Now, let’s say you have a list of Restaurant objects that is not a queryset:
restaurants = list(Restaurant.objects.all())

This list is no longer a queryset. You cannot do something like:
restaurants_with_pizzas = restaurants.prefetch_related('pizzas')
(If you try this, you will get an AttributeError: 'list' object has no attribute 'prefetch_related'.)

However, what you can do is:
restaurants_with_pizzas = prefetch_related_objects(restaurants, 'pizzas')

So what is the difference from Prefetch??

A Prefetch object as described in the docs:

The Prefetch() object can be used to control the operation of prefetch_related().

As described in the section below that for prefetch_related_objects, you can also use a Prefetch object with that, too.

The Prefetch object provides more control over what is selected and how it is stored. (See the note on the to_attr attribute.)

In the simplest case, the Prefetch object can be passed a queryset which could limit the results being returned to a subset of the complete related table.
So you can use a Prefetch with either prefetch_related or prefetch_related_objects

Are you assuming that I have a full understanding of prefetch_related_objects?

I’m asking for detailed explanation to understand prefetch_related_objects, and I don’t understand your explanation at all.

Not at all. However, I believe you may be thinking that there’s more to it than what it is.

You use prefetch_related on a queryset. You use prefetch_related_objects with a list. Otherwise, they’re effectively the same.

If you think that there’s more to it than this, I’m going to need you to explain what you’re looking for.

As I wrote in the first, my purpose is clear.
Understanding what prefetch_related_objects is, how to use it, and how it works.

Using prefetch_related_objects, you mean pass a list of objects to prefetch_related?
Like passing a Prefetch queryset to prefetch_related?

It is a function. You pass it a list of objects and related fields, and it retrieves those related objects for all of the objects in the list.

You pass it a list of objects (all of the same class) and the related fields to retrieve. Optionally, you can pass it a Prefetch object.

In general terms, it works the same as prefetch_related.
For a full understanding, I can only suggest you read the source code.

No. If you have a queryset, you can use prefetch_related. If you have a list of objects, you can use prefetch_related_objects. They’re each used in two different situations.

No.

You use either prefetch_related or prefetch_related_objects, depending upon the situation.

If you have a queryset, you use prefetch_related. If you have a list, you use prefetch_related_objects.

You can use Prefetch objects with both prefetch_related and prefetch_related_objects. It is an object that you can pass to either function to change what data is retrieved.

If you would like to see an example of it being used, post a minimal example of a query using prefetch_related, and I will show you a similar usage of prefetch_related_objects.

I’m sorry, but your explanation isn’t helpful at all.
And how can I write an example if I don’t even know how to use it?

I wrote a sample model to provide an example in the text. Could you make an example using it?

I’m asking for a sample using prefetch_related, not prefetch_related_objects.

If you supply the sample using prefetch_related, then I can show you how you would use prefetch_related_objects. Otherwise, I would refer you back to my response at prefetch_related_objects, how to use, how to work? - #2 by KenWhitesell which shows both being used in the two different circumstances.

I’m asking for a sample using prefetch_related_objects, not prefetch_related.

I don’t know what you want to talk about. In my opinion, you are just repeating things that have nothing to do with the topic.

A sample using prefetch_related_objects is in my very first response.

If you need a different example, then I’ll gladly provide one. Give me a sample using prefetch_related, and I’ll show you the comparable usage of prefetch_related_objects.

a = A.objects.all().prefetch_related(Prefetch('b', B.objects.all()), Prefetch('c', C.objects.all()))
b = B.objects.all().prefetch_related(Prefetch('aa', A.objects.all()))

Great!

Lets break this down a little.

Your first example is:
a = A.objects.all().prefetch_related(Prefetch('b', B.objects.all()), Prefetch('c', C.objects.all()))

This query needs to be modified a little for it to work with the classes you defined above. The working version is:
a = A.objects.all().prefetch_related(Prefetch('b_set', B.objects.all()), Prefetch('c_set', C.objects.all()))

Now, the first part of this defines a queryset:
a = A.objects.all()

That means that we could rewrite the first example as two lines as:
a_queryset = A.objects.all()
a = a_queryset.prefetch_related(Prefetch('b_set', B.objects.all()), Prefetch('c_set', C.objects.all()))

These two lines produce the same results as the single line as you have written.

Now, I’m going to change the first line here. Instead of producing a queryset, I’m going to create a list:
a = list(A.objects.all())

And now, since I have a list instead of a queryset, I can now use prefetch_related_objects:

prefetch_related_objects(a, Prefetch('b_set', B.objects.all()), Prefetch('c_set', C.objects.all())

If I wanted to write this as a one line statement, I could then write it like this:
prefetch_related_objects(a:=list(A.objects.all()), Prefetch('b_set', B.objects.all()), Prefetch('c_set', C.objects.all()))

In your second example:

The call to prefetch_related isn’t necessary. For any one instance of B, there is only going to be one instance of A, because the B.aa field is a foreign key to A. This means that you would want to use select_related, making this query:
b = B.objects.all().select_related('aa')
(Using the prefetch_related call in this situation results in two queries instead of one.)

From the example you gave, I think prefetch_related_objects applies prefetch to a list of already evaluated objects.

Am I understanding this correctly?

I’m not sure I know what you intend by saying “already evaluated” objects.

I would remove that qualifier from your statement, and phrase it as
“I think prefetch_related_objects applies prefetch to a list of objects.”
Written like that, I would agree with that completely.

The list of objects being referenced do not necessarily need to have come from a single queryset. It just needs to be a list of objects of the same class.

For example, this would also work:

a1 = A.objects.get(id=1)
a2 = A.objects.get(id=2)
a = [a1, a2]
prefetch_related_objects(a, 'b_set')

How that list is created does not matter.

The example in the docs makes a hypothetical example of retrieving the list from a cache.

Or, as another example, that list of objects could have been created from a JSON submission of objects that were deserialized.

In neither of these last two cases was a query involved at all.