{"id":6844,"date":"2026-11-10T03:31:13","date_gmt":"2026-11-10T06:31:13","guid":{"rendered":"https:\/\/tucumandevelopers.com\/index.php\/2026\/11\/10\/select_related-vs-prefetch_related-practical-django-examples\/"},"modified":"2026-11-10T03:31:13","modified_gmt":"2026-11-10T06:31:13","slug":"select_related-vs-prefetch_related-practical-django-examples","status":"publish","type":"post","link":"https:\/\/tucumandevelopers.com\/index.php\/2026\/11\/10\/select_related-vs-prefetch_related-practical-django-examples\/","title":{"rendered":"select_related vs prefetch_related: Practical Django Examples"},"content":{"rendered":"<div>\n<div><\/header>\n<div data-article-id=\"5008978\" id=\"article-body\">\n<p><code>select_related()<\/code> and <code>prefetch_related()<\/code> both fix the N+1 problem, but they work differently. Here&#8217;s the practical difference.<\/p>\n<h2> <a name=\"the-rule\" href=\"#the-rule\"> <\/a> The rule <\/h2>\n<ul>\n<li> <strong>select_related<\/strong> \u2192 SQL JOIN, one query. For ForeignKey and OneToOne.<\/li>\n<li> <strong>prefetch_related<\/strong> \u2192 separate queries, joined in Python. For ManyToMany and reverse ForeignKey.<\/li>\n<\/ul>\n<h2> <a name=\"example-models\" href=\"#example-models\"> <\/a> Example models <\/h2>\n<div>\n<pre><code><span>class<\/span> <span>Book<\/span><span>(<\/span><span>models<\/span><span>.<\/span><span>Model<\/span><span>)<\/span><span>:<\/span> <span>title<\/span> <span>=<\/span> <span>models<\/span><span>.<\/span><span>CharField<\/span><span>(<\/span><span>max_length<\/span><span>=<\/span><span>200<\/span><span>)<\/span> <span>author<\/span> <span>=<\/span> <span>models<\/span><span>.<\/span><span>ForeignKey<\/span><span>(<\/span><span>Author<\/span><span>,<\/span> <span>on_delete<\/span><span>=<\/span><span>models<\/span><span>.<\/span><span>CASCADE<\/span><span>,<\/span> <span>related_name<\/span><span>=<\/span><span>\"books\"<\/span><span>)<\/span> <span>publisher<\/span> <span>=<\/span> <span>models<\/span><span>.<\/span><span>ForeignKey<\/span><span>(<\/span><span>Publisher<\/span><span>,<\/span> <span>on_delete<\/span><span>=<\/span><span>models<\/span><span>.<\/span><span>CASCADE<\/span><span>)<\/span> <span>tags<\/span> <span>=<\/span> <span>models<\/span><span>.<\/span><span>ManyToManyField<\/span><span>(<\/span><span>Tag<\/span><span>,<\/span> <span>related_name<\/span><span>=<\/span><span>\"books\"<\/span><span>)<\/span> <span>class<\/span> <span>Review<\/span><span>(<\/span><span>models<\/span><span>.<\/span><span>Model<\/span><span>)<\/span><span>:<\/span> <span>book<\/span> <span>=<\/span> <span>models<\/span><span>.<\/span><span>ForeignKey<\/span><span>(<\/span><span>Book<\/span><span>,<\/span> <span>on_delete<\/span><span>=<\/span><span>models<\/span><span>.<\/span><span>CASCADE<\/span><span>,<\/span> <span>related_name<\/span><span>=<\/span><span>\"reviews\"<\/span><span>)<\/span> <span>rating<\/span> <span>=<\/span> <span>models<\/span><span>.<\/span><span>PositiveSmallIntegerField<\/span><span>(<\/span><span>)<\/span> <span>approved<\/span> <span>=<\/span> <span>models<\/span><span>.<\/span><span>BooleanField<\/span><span>(<\/span><span>default<\/span><span>=<\/span><span>False<\/span><span>)<\/span> <\/code><\/pre>\n<div>\n<\/p><\/div>\n<\/p><\/div>\n<h2> <a name=\"before-201-queries\" href=\"#before-201-queries\"> <\/a> Before: 201 queries <\/h2>\n<div>\n<pre><code><span>for<\/span> <span>book<\/span> <span>in<\/span> <span>Book<\/span><span>.<\/span><span>objects<\/span><span>.<\/span><span>all<\/span><span>(<\/span><span>)<\/span><span>[<\/span><span>:<\/span><span>100<\/span><span>]<\/span><span>:<\/span> <span>print<\/span><span>(<\/span><span>book<\/span><span>.<\/span><span>title<\/span><span>,<\/span> <span>book<\/span><span>.<\/span><span>author<\/span><span>.<\/span><span>name<\/span><span>,<\/span> <span>book<\/span><span>.<\/span><span>publisher<\/span><span>.<\/span><span>name<\/span><span>)<\/span> <\/code><\/pre>\n<div>\n<\/p><\/div>\n<\/p><\/div>\n<h2> <a name=\"after-1-query\" href=\"#after-1-query\"> <\/a> After: 1 query <\/h2>\n<div>\n<pre><code><span>books<\/span> <span>=<\/span> <span>Book<\/span><span>.<\/span><span>objects<\/span><span>.<\/span><span>select_related<\/span><span>(<\/span><span>\"author\"<\/span><span>,<\/span> <span>\"publisher\"<\/span><span>)<\/span><span>[<\/span><span>:<\/span><span>100<\/span><span>]<\/span> <\/code><\/pre>\n<div>\n<\/p><\/div>\n<\/p><\/div>\n<h2> <a name=\"many-relations-2-queries\" href=\"#many-relations-2-queries\"> <\/a> Many relations: 2 queries <\/h2>\n<div>\n<pre><code><span>books<\/span> <span>=<\/span> <span>Book<\/span><span>.<\/span><span>objects<\/span><span>.<\/span><span>prefetch_related<\/span><span>(<\/span><span>\"tags\"<\/span><span>)<\/span><span>[<\/span><span>:<\/span><span>100<\/span><span>]<\/span> <\/code><\/pre>\n<div>\n<\/p><\/div>\n<\/p><\/div>\n<h2> <a name=\"combined\" href=\"#combined\"> <\/a> Combined <\/h2>\n<div>\n<pre><code><span>books<\/span> <span>=<\/span> <span>(<\/span> <span>Book<\/span><span>.<\/span><span>objects<\/span> <span>.<\/span><span>select_related<\/span><span>(<\/span><span>\"author__profile\"<\/span><span>,<\/span> <span>\"publisher\"<\/span><span>)<\/span> <span>.<\/span><span>prefetch_related<\/span><span>(<\/span><span>\"tags\"<\/span><span>)<\/span> <span>.<\/span><span>order_by<\/span><span>(<\/span><span>\"-id\"<\/span><span>)<\/span><span>[<\/span><span>:<\/span><span>100<\/span><span>]<\/span> <span>)<\/span> <\/code><\/pre>\n<div>\n<\/p><\/div>\n<\/p><\/div>\n<h2> <a name=\"the-common-trap\" href=\"#the-common-trap\"> <\/a> The common trap <\/h2>\n<div>\n<pre><code><span>for<\/span> <span>book<\/span> <span>in<\/span> <span>Book<\/span><span>.<\/span><span>objects<\/span><span>.<\/span><span>prefetch_related<\/span><span>(<\/span><span>\"reviews\"<\/span><span>)<\/span><span>:<\/span> <span>book<\/span><span>.<\/span><span>reviews<\/span><span>.<\/span><span>filter<\/span><span>(<\/span><span>approved<\/span><span>=<\/span><span>True<\/span><span>)<\/span> <span># new query per book<\/span> <\/code><\/pre>\n<div>\n<\/p><\/div>\n<\/p><\/div>\n<p><code>.filter()<\/code>, <code>.order_by()<\/code>, and <code>.count()<\/code> on a prefetched relation bypass the cache. Use <code>Prefetch<\/code> instead: <\/p>\n<div>\n<pre><code><span>from<\/span> <span>django<\/span><span>.<\/span><span>db<\/span><span>.<\/span><span>models<\/span> <span>import<\/span> <span>Prefetch<\/span> <span>books<\/span> <span>=<\/span> <span>Book<\/span><span>.<\/span><span>objects<\/span><span>.<\/span><span>prefetch_related<\/span><span>(<\/span> <span>Prefetch<\/span><span>(<\/span> <span>\"reviews\"<\/span><span>,<\/span> <span>queryset<\/span><span>=<\/span><span>Review<\/span><span>.<\/span><span>objects<\/span><span>.<\/span><span>filter<\/span><span>(<\/span><span>approved<\/span><span>=<\/span><span>True<\/span><span>)<\/span><span>.<\/span><span>only<\/span><span>(<\/span><span>\"id\"<\/span><span>,<\/span> <span>\"book_id\"<\/span><span>,<\/span> <span>\"rating\"<\/span><span>)<\/span><span>,<\/span> <span>to_attr<\/span><span>=<\/span><span>\"approved_reviews\"<\/span><span>,<\/span> <span>)<\/span> <span>)<\/span> <\/code><\/pre>\n<div>\n<\/p><\/div>\n<\/p><\/div>\n<p>Include the foreign key (<code>book_id<\/code>) when you use <code>.only()<\/code> in a <code>Prefetch<\/code>, since Django needs it to match rows to parents.<\/p>\n<h2> <a name=\"lock-it-in-with-a-test\" href=\"#lock-it-in-with-a-test\"> <\/a> Lock it in with a test <\/h2>\n<div>\n<pre><code><span>with<\/span> <span>self<\/span><span>.<\/span><span>assertNumQueries<\/span><span>(<\/span><span>2<\/span><span>)<\/span><span>:<\/span> <span>books<\/span> <span>=<\/span> <span>list<\/span><span>(<\/span> <span>Book<\/span><span>.<\/span><span>objects<\/span><span>.<\/span><span>select_related<\/span><span>(<\/span><span>\"author\"<\/span><span>,<\/span> <span>\"publisher\"<\/span><span>)<\/span><span>.<\/span><span>prefetch_related<\/span><span>(<\/span><span>\"tags\"<\/span><span>)<\/span><span>[<\/span><span>:<\/span><span>50<\/span><span>]<\/span> <span>)<\/span> <span>for<\/span> <span>b<\/span> <span>in<\/span> <span>books<\/span><span>:<\/span> <span>_<\/span> <span>=<\/span> <span>b<\/span><span>.<\/span><span>author<\/span><span>.<\/span><span>name<\/span><span>,<\/span> <span>b<\/span><span>.<\/span><span>publisher<\/span><span>.<\/span><span>name<\/span><span>,<\/span> <span>[<\/span><span>t<\/span><span>.<\/span><span>name<\/span> <span>for<\/span> <span>t<\/span> <span>in<\/span> <span>b<\/span><span>.<\/span><span>tags<\/span><span>.<\/span><span>all<\/span><span>(<\/span><span>)<\/span><span>]<\/span> <\/code><\/pre>\n<div>\n<\/p><\/div>\n<\/p><\/div>\n<h2> <a name=\"full-guide\" href=\"#full-guide\"> <\/a> Full guide <\/h2>\n<p>The complete post covers multi-level prefetching, six common pitfalls, behavior on large tables, a decision guide, and an FAQ:<\/p>\n<p>\ud83d\udc49 <a href=\"https:\/\/blog.reiteachaigean.com\/blog\/select-related-vs-prefetch-related-django\" target=\"_blank\" rel=\"noopener noreferrer\">https:\/\/blog.reiteachaigean.com\/blog\/select-related-vs-prefetch-related-django<\/a><\/p>\n<p>If you work with big tables, these two follow-ups go deeper:<\/p>\n<ul>\n<li><a href=\"https:\/\/blog.reiteachaigean.com\/blog\/django-orm-optimize-millions-of-records\" target=\"_blank\" rel=\"noopener noreferrer\">Django ORM optimization for millions of records<\/a><\/li>\n<li><a href=\"https:\/\/blog.reiteachaigean.com\/blog\/process-5-million-records-django-celery\" target=\"_blank\" rel=\"noopener noreferrer\">Processing 5 million records with Django and Celery<\/a><\/li>\n<\/ul>\n<p>What&#8217;s the worst N+1 you&#8217;ve found in a real project?<\/p>\n<\/p><\/div>\n<\/article>\n<p> <!-- Bottom content skipped via SKIP_BOTTOM_CONTENT config --> <\/div>\n<p> <\/main> <\/div>\n<\/div>\n<\/div>\n<\/div>\n<p>Fuente: <a href=\"https:\/\/dev.to\/sizan_mahmud0_e7c3fd0cb68\/selectrelated-vs-prefetchrelated-practical-django-examples-j87\">Art\u00edculo original<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>select_related() and prefetch_related() both fix the N+1 problem, but they work differently. Here&#8217;s the practical difference. The rule select_related \u2192 SQL JOIN, one query. For ForeignKey and OneToOne. prefetch_related \u2192 separate queries, joined in Python. For ManyToMany and reverse ForeignKey. Example models class Book(models.Model): title = models.CharField(max_length=200) author = models.ForeignKey(Author, on_delete=models.CASCADE, related_name=&#8221;books&#8221;) publisher = models.ForeignKey(Publisher, [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":6843,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":"","jetpack_publicize_message":"","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":true,"jetpack_social_options":{"image_generator_settings":{"template":"highway","default_image_id":0,"font":"","enabled":false},"version":2},"webixso_pending_account_ids":""},"categories":[41],"tags":[],"class_list":["post-6844","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-devto"],"jetpack_publicize_connections":[],"_links":{"self":[{"href":"https:\/\/tucumandevelopers.com\/index.php\/wp-json\/wp\/v2\/posts\/6844","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/tucumandevelopers.com\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/tucumandevelopers.com\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/tucumandevelopers.com\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/tucumandevelopers.com\/index.php\/wp-json\/wp\/v2\/comments?post=6844"}],"version-history":[{"count":0,"href":"https:\/\/tucumandevelopers.com\/index.php\/wp-json\/wp\/v2\/posts\/6844\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/tucumandevelopers.com\/index.php\/wp-json\/wp\/v2\/media\/6843"}],"wp:attachment":[{"href":"https:\/\/tucumandevelopers.com\/index.php\/wp-json\/wp\/v2\/media?parent=6844"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/tucumandevelopers.com\/index.php\/wp-json\/wp\/v2\/categories?post=6844"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/tucumandevelopers.com\/index.php\/wp-json\/wp\/v2\/tags?post=6844"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}