regression for 17439 a model bio with a onetoonefield primary key that references a nonpk field name on the related model is prefetchable regression test for 20242 queryset in didn t work the first time when using prefetchrelated this was fixed by the removal of chunked reads from queryset iteration in 70679243d1786e03557c28929f9762a119e3ac14 a m2m can be followed through another m2m with self assertnumqueries3 qs objects prefetchrelatedbooksreadby lists strr for r in b readby all for b in a books all for a in qs self assertequal lists amy belinda charlotte poems jane eyre amy anne poems amy emily poems wuthering heights amy belinda jane sense and sense def testoverridingprefetchself with self assertnumqueries3 qs objects prefetchrelatedbooks booksreadby lists strr for r in b readby all for b in a books all for a in qs self assertequal lists amy belinda charlotte poems jane eyre amy anne poems amy emily poems wuthering heights amy belinda jane sense and sense with self assertnumqueries3 qs objects prefetchrelatedbooksreadby books lists strr for r in b readby all for b in a books all for a in qs self assertequal lists amy belinda charlotte poems jane eyre amy anne poems amy emily poems wuthering heights amy belinda jane sense and sense def testgetself need a double with self assertnumqueries3 objects prefetchrelatedbooksreadby get namecharlotte lists strr for r in b readby all for b in books all self assertequallists amy belinda poems jane eyre def testforeignkeythenm2mself with self assertnumqueries2 qs objects selectrelatedfirstbook prefetchrelated firstbookreadby lists strr for r in a firstbook readby all for a in qs self assertequallists amy amy amy amy belinda def testreverseonetoonethenm2mself qs objects prefetchrelatedbiobooks selectrelatedbio with self assertnumqueries1 listqs all bio objects createself 1 with self assertnumqueries2 listqs all def testattributeerrorself qs reader objects prefetchrelatedbooksreadxyz msg cannot find xyz on book object booksreadxyz is an invalid parameter to prefetchrelated with self assertraisesmessageattributeerror msg as cm listqs self assertinprefetchrelated strcm exception def testinvalidfinallookupself qs book objects prefetchrelatedsname msg sname does not resolve to an item that supports prefetching this is an invalid parameter to prefetchrelated with self assertraisesmessagevalueerror msg as cm listqs self assertinprefetchrelated strcm exception self assertinname strcm exception def testprefetcheqself prefetch1 prefetchs queryset objects all prefetch2 prefetchbooks querysetbook objects all self assertequalprefetch1 prefetch1 self assertequalprefetch1 mock any self assertnotequalprefetch1 prefetch2 def testforwardm2mtoattrconflictself msg toattrs conflicts with a field on the book model s objects all with self assertraisesmessagevalueerror msg list book objects prefetchrelated prefetchs querysets toattrs without the valueerror an was deleted due to the implicit save of the relation assignment self assertequalself book1 s count 3 def testreversem2mtoattrconflictself msg toattrbooks conflicts with a field on the model poems book objects filtertitlepoems with self assertraisesmessagevalueerror msg list objects prefetchrelated prefetchbooks querysetpoems toattrbooks without the valueerror a book was deleted due to the implicit save of reverse relation assignment self assertequalself 1 books count 2 def testm2mthenreversefkobjectidsself with capturequeriescontextconnection as queries listbook objects prefetchrelatedsaddresses sql queries1sql self assertwherecontainssql self 1 name def testm2mthenm2mobjectidsself with capturequeriescontextconnection as queries listbook objects prefetchrelatedsfavorites sql queries1sql self assertwherecontainssql self 1 name def testm2mthenreverseonetooneobjectidsself with capturequeriescontextconnection as queries listbook objects prefetchrelatedswithage sql queries1sql self assertwherecontainssql self 1 id def testfilterdeferredself addq query addq for relation in s firsttimes with self subtestrelationrelation with mock patch object query addq autospectrue sideeffectlambda self q addqself q as addqmock listbook objects prefetchrelatedrelation self assertequaladdqmock callcount 1 def testnamedvalueslistself qs objects prefetchrelatedbooks self assertcountequal value name for value in qs valueslistname namedtrue anne charlotte emily jane def testm2mprefetchingiteratorwithchunksself with self assertnumqueries3 s b s first for b in book objects prefetchrelateds iteratorchunksize2 self assertequal s self 1 self 1 self 3 self 4 def testm2mprefetchingiteratorwithoutchunkserrorself msg chunksize must be provided when using queryset iterator after prefetchrelated with self assertraisesmessagevalueerror msg book objects prefetchrelateds iterator class rawquerysetteststestdatamixin testcase def testbasicself with self assertnumqueries2 books book objects raw select from prefetchrelatedbook where id s self book1 id prefetchrelateds book1 listbooks0 with self assertnumqueries0 self assertcountequal book1 s all self 1 self 2 self 3 def testprefetchbeforerawself with self assertnumqueries2 books book objects prefetchrelateds raw select from prefetchrelatedbook where id s self book1 id book1 listbooks0 with self assertnumqueries0 self assertcountequal book1 s all self 1 self 2 self 3 def testclearself with self assertnumqueries5 withprefetch objects raw select from prefetchrelated prefetchrelatedbooks withoutprefetch withprefetch prefetchrelatednone lista books all for a in withoutprefetch class customprefetchteststestcase classmethod def traverseqscls objiter path retval if hasattrobjiter all objiter objiter all try iterobjiter except typeerror objiter objiter for obj in objiter relobjs for part in path if not part continue try related getattrobj part0 except objectdoesnotexist continue if related is not none relobjs extendcls traverseqsrelated part1 retval appendobj relobjs return retval classmethod def setuptestdatacls cls person1 person objects createnamejoe cls person2 person objects createnamemary set mainroom for each house before creating the next one for databases where supportsnullableuniqueconstraints is false cls house1 house objects create namehouse 1 address123 main st ownercls person1 cls room11 room objects createnamedining room housecls house1 cls room12 room objects createnamelounge housecls house1 cls room13 room objects createnamekitchen housecls house1 cls house1 mainroom cls room11 cls house1 save cls person1 houses addcls house1 cls house2 house objects create namehouse 2 address45 side st ownercls person1 cls room21 room objects createnamedining room housecls house2 cls room22 room objects createnamelounge housecls house2 cls room23 room objects createnamekitchen housecls house2 cls house2 mainroom cls room21 cls house2 save cls person1 houses addcls house2 cls house3 house objects create namehouse 3 address6 downing st ownercls person2 cls room31 room objects createnamedining room housecls house3 cls room32 room objects createnamelounge housecls house3 cls room33 room objects createnamekitchen housecls house3 cls house3 mainroom cls room31 cls house3 save cls person2 houses addcls house3 cls house4 house objects create namehouse 4 address7 regents st ownercls person2 cls room41 room objects createnamedining room housecls house4 cls room42 room objects createnamelounge housecls house4 cls room43 room objects createnamekitchen housecls house4 cls house4 mainroom cls room41 cls house4 save cls person2 houses addcls house4 def testtraverseqsself qs person objects prefetchrelatedhouses relatedobjsnormal listp houses all for p in qs relatedobjsfromtraverse inner0 for inner in o1 for o in self traverseqsqs houses self assertequalrelatedobjsnormal relatedobjsfromtraverse def testambiguousself ambiguous lookup was already seen with a different queryset msg houses lookup was already seen with a different queryset you may need to adjust the ordering of your lookups lookup queryset shouldn t be evaluated with self assertnumqueries3 with self assertraisesmessagevalueerror msg self traverseqs person objects prefetchrelated housesrooms prefetchhouses querysethouse objects all houses rooms ambiguous lookup houseslst doesn t yet exist when performing houseslstrooms msg cannot find houseslst on person object houseslstrooms is an invalid parameter to prefetchrelated with self assertraisesmessageattributeerror msg self traverseqs person objects prefetchrelated houseslstrooms prefetch houses querysethouse objects all toattrhouseslst houses rooms not ambiguous self traverseqs person objects prefetchrelatedhousesrooms houses houses rooms self traverseqs person objects prefetchrelated housesrooms prefetchhouses querysethouse objects all toattrhouseslst houses rooms def testm2mself control lookups with self assertnumqueries2 lst1 self traverseqs person objects prefetchrelatedhouses houses test lookups with self assertnumqueries2 lst2 self traverseqs person objects prefetchrelatedprefetchhouses houses self assertequallst1 lst2 with self assertnumqueries2 lst2 self traverseqs person objects prefetchrelated prefetchhouses toattrhouseslst houseslst self assertequallst1 lst2 def testreversem2mself control lookups with self assertnumqueries2 lst1 self traverseqs house objects prefetchrelatedoccupants occupants test lookups with self assertnumqueries2 lst2 self traverseqs house objects prefetchrelatedprefetchoccupants occupants self assertequallst1 lst2 with self assertnumqueries2 lst2 self traverseqs house objects prefetchrelated prefetchoccupants toattroccupantslst occupantslst self assertequallst1 lst2 def testm2mthroughfkself control lookups with self assertnumqueries3 lst1 self traverseqs room objects prefetchrelatedhouseoccupants house occupants test lookups with self assertnumqueries3 lst2 self traverseqs room objects prefetchrelatedprefetchhouseoccupants house occupants self assertequallst1 lst2 with self assertnumqueries3 lst2 self traverseqs room objects prefetchrelated prefetchhouseoccupants toattroccupantslst house occupantslst self assertequallst1 lst2 def testm2mthroughgfkself taggeditem objects createtaghouses contentobjectself house1 taggeditem objects createtaghouses contentobjectself house2 control lookups with self assertnumqueries3 lst1 self traverseqs taggeditem objects filtertaghouses prefetchrelated contentobjectrooms contentobject rooms test lookups with self assertnumqueries3 lst2 self traverseqs taggeditem objects prefetchrelated prefetchcontentobject prefetchcontentobjectrooms toattrroomslst contentobject roomslst self assertequallst1 lst2 def testo2mthroughm2mself control lookups with self assertnumqueries3 lst1 self traverseqs person objects prefetchrelatedhouses housesrooms houses rooms test lookups with self assertnumqueries3 lst2 self traverseqs person objects prefetchrelatedprefetchhouses housesrooms houses rooms self assertequallst1 lst2 with self assertnumqueries3 lst2 self traverseqs person objects prefetchrelated prefetchhouses prefetchhousesrooms houses rooms self assertequallst1 lst2 with self assertnumqueries3 lst2 self traverseqs person objects prefetchrelated prefetchhouses toattrhouseslst houseslstrooms houseslst rooms self assertequallst1 lst2 with self assertnumqueries3 lst2 self traverseqs person objects prefetchrelated prefetchhouses toattrhouseslst prefetchhouseslstrooms toattrroomslst houseslst roomslst self assertequallst1 lst2 def testgenericrelself bookmark bookmark objects createurlhttp www djangoproject com taggeditem objects createcontentobjectbookmark tagdjango taggeditem objects create contentobjectbookmark favoritebookmark tagpython control lookups with self assertnumqueries4 lst1 self traverseqs bookmark objects prefetchrelated tags tagscontentobject favoritetags tags contentobject favoritetags test lookups with self assertnumqueries4 lst2 self traverseqs bookmark objects prefetchrelated prefetchtags toattrtagslst prefetchtagslstcontentobject prefetchfavoritetags tagslst contentobject favoritetags self assertequallst1 lst2 def testtraversesingleitempropertyself control lookups with self assertnumqueries5 lst1 self traverseqs person objects prefetchrelated housesrooms primaryhouseoccupantshouses primaryhouse occupants houses test lookups with self assertnumqueries5 lst2 self traverseqs person objects prefetchrelated housesrooms prefetchprimaryhouseoccupants toattroccupantslst primaryhouseoccupantslsthouses primaryhouse occupantslst houses self assertequallst1 lst2 def testtraversemultipleitemspropertyself control lookups with self assertnumqueries4 lst1 self traverseqs person objects prefetchrelated houses allhousesoccupantshouses allhouses occupants houses test lookups with self assertnumqueries4 lst2 self traverseqs person objects prefetchrelated houses prefetchallhousesoccupants toattroccupantslst allhousesoccupantslsthouses allhouses occupantslst houses self assertequallst1 lst2 def testcustomqsself test basic with self assertnumqueries2 lst1 listperson objects prefetchrelatedhouses with self assertnumqueries2 lst2 list person objects prefetchrelated prefetch houses querysethouse objects all toattrhouseslst self assertequal self traverseqslst1 houses self traverseqslst2 houseslst test queryset filtering with self assertnumqueries2 lst2 list person objects prefetchrelated prefetch houses querysethouse objects filter pkinself house1 pk self house3 pk toattrhouseslst self assertequallenlst20 houseslst 1 self assertequallst20 houseslst0 self house1 self assertequallenlst21 houseslst 1 self assertequallst21 houseslst0 self house3 test flattened with self assertnumqueries3 lst1 listperson objects prefetchrelatedhousesrooms with self assertnumqueries3 lst2 list person objects prefetchrelated prefetch housesrooms querysetroom objects all toattrroomslst self assertequal self traverseqslst1 houses rooms self traverseqslst2 houses roomslst test inner selectrelated with self assertnumqueries3 lst1 listperson objects prefetchrelatedhousesowner with self assertnumqueries2 lst2 list person objects prefetchrelated prefetchhouses querysethouse objects selectrelatedowner self assertequal self traverseqslst1 houses owner self traverseqslst2 houses owner test inner prefetch innerroomsqs room objects filterpkinself room11 pk self room12 pk housesqsprf house objects prefetchrelated prefetchrooms querysetinnerroomsqs toattrroomslst with self assertnumqueries4 lst2 list person objects prefetchrelated prefetch houses querysethousesqsprf filterpkself house1 pk toattrhouseslst prefetchhouseslstroomslstmainroomof self assertequallenlst20 houseslst0 roomslst 2 self assertequallst20 houseslst0 roomslst0 self room11 self assertequallst20 houseslst0 roomslst1 self room12 self assertequallst20 houseslst0 roomslst0 mainroomof self house1 self assertequallenlst21 houseslst 0 test forwardmanytoonedescriptor houses house objects selectrelatedowner with self assertnumqueries6 rooms room objects prefetchrelatedhouse lst1 self traverseqsrooms house owner with self assertnumqueries2 rooms room objects prefetchrelatedprefetchhouse querysethouses lst2 self traverseqsrooms house owner self assertequallst1 lst2 with self assertnumqueries2 houses house objects selectrelatedowner rooms room objects prefetchrelated prefetchhouse querysethouses toattrhouseattr lst2 self traverseqsrooms houseattr owner self assertequallst1 lst2 room room objects prefetchrelated prefetchhouse querysethouses filteraddressdoesnotexist first with self assertraisesobjectdoesnotexist getattrroom house room room objects prefetchrelated prefetch house querysethouses filteraddressdoesnotexist toattrhouseattr first self assertisnoneroom houseattr rooms room objects prefetchrelated prefetchhouse querysethouse objects onlyname with self assertnumqueries2 getattrrooms first house name with self assertnumqueries3 getattrrooms first house address test reverseonetoonedescriptor houses house objects selectrelatedowner with self assertnumqueries6 rooms room objects prefetchrelatedmainroomof lst1 self traverseqsrooms mainroomof owner with self assertnumqueries2 rooms room objects prefetchrelated prefetchmainroomof querysethouses lst2 self traverseqsrooms mainroomof owner self assertequallst1 lst2 with self assertnumqueries2 rooms list room objects prefetchrelated prefetch mainroomof querysethouses toattrmainroomofattr lst2 self traverseqsrooms mainroomofattr owner self assertequallst1 lst2 room room objects filtermainroomofisnullfalse prefetchrelated prefetchmainroomof querysethouses filteraddressdoesnotexist first with self assertraisesobjectdoesnotexist getattrroom mainroomof room room objects filtermainroomofisnullfalse prefetchrelated prefetch mainroomof querysethouses filteraddressdoesnotexist toattrmainroomofattr first self assertisnoneroom mainroomofattr the custom queryset filters should be applied to the queryset instance returned by the manager person person objects prefetchrelated prefetchhouses querysethouse objects filternamehouse 1 getpkself person1 pk self assertequal listperson houses all listperson houses all all def testnestedprefetchrelatedarenotoverwrittenself regression test for 24873 houses2 house objects prefetchrelatedprefetchrooms persons person objects prefetchrelatedprefetchhouses querysethouses2 houses house objects prefetchrelatedprefetchoccupants querysetpersons listhouses queryset must be evaluated once to reproduce the bug self assertequal houses all0 occupants all0 houses all1 rooms all0 self room21 def testnestedprefetchrelatedwithduplicateprefetcherself occupants person objects prefetchrelated prefetchhouses toattrsomeattrname prefetchhouses querysethouse objects prefetchrelatedmainroom houses house objects prefetchrelated prefetchoccupants querysetoccupants with self assertnumqueries5 self traverseqslisthouses occupants houses mainroom def testnestedprefetchrelatedwithduplicateprefetchanddepthself people person objects prefetchrelated prefetch housesmainroom querysetroom objects filternamedining room toattrdiningroom housesmainroom with self assertnumqueries4 mainroom people0 houses all0 people person objects prefetchrelated housesmainroom prefetch housesmainroom querysetroom objects filternamedining room toattrdiningroom with self assertnumqueries4 mainroom people0 houses all0 self assertequalmainroom mainroom self room11 def testvaluesquerysetself msg prefetch querysets cannot use raw values and valueslist with self assertraisesmessagevalueerror msg prefetchhouses house objects valuespk with self assertraisesmessagevalueerror msg prefetchhouses house objects valueslistpk that error doesn t affect managers with custom modeliterable subclasses self assertis teacher objectscustom all iterableclass modeliterablesubclass prefetchteachers teacher objectscustom all def testrawquerysetself msg prefetch querysets cannot use raw values and valueslist with self assertraisesmessagevalueerror msg prefetchhouses house objects rawselect pk from house def testtoattrdoesntcachethroughattraslistself house house objects prefetchrelated prefetchrooms querysetroom objects all toattrtorooms getpkself house3 pk self assertisinstancehouse rooms all queryset def testtoattrcachedpropertyself persons person objects prefetchrelated prefetchhouses house objects all toattrcachedallhouses for person in persons to bypass caching at the related descriptor level don t use person houses all here allhouses listhouse objects filteroccupantsperson with self assertnumqueries0 self assertequalperson cachedallhouses allhouses def testfilterdeferredself addq query addq with mock patch object query addq autospectrue sideeffectlambda self q addqself q as addqmock list house objects prefetchrelated prefetchoccupants querysetperson objects all self assertequaladdqmock callcount 1 class defaultmanagerteststestcase classmethod def setuptestdatacls cls qual1 qualification objects createnameba cls qual2 qualification objects createnamebsci cls qual3 qualification objects createnamema cls qual4 qualification objects createnamephd cls teacher1 teacher objects createnamemr cleese cls teacher2 teacher objects createnamemr idle cls teacher3 teacher objects createnamemr chapman cls teacher1 qualifications addcls qual1 cls qual2 cls qual3 cls qual4 cls teacher2 qualifications addcls qual1 cls teacher3 qualifications addcls qual2 cls dept1 department objects createnameenglish cls dept2 department objects createnamephysics cls dept1 teachers addcls teacher1 cls teacher2 cls dept2 teachers addcls teacher1 cls teacher3 def testm2mthenm2mself with self assertnumqueries3 when we prefetch the teachers and force the query we don t want the default manager on teachers to immediately get all the related qualifications since this will do one query per teacher qs department objects prefetchrelatedteachers depts join s department sn dept name joinstrt for t in dept teachers all for dept in qs self assertequal depts english department mr cleese ba bsci ma phd mr idle ban physics department mr cleese ba bsci ma phd mr chapman bscin class genericrelationteststestcase classmethod def setuptestdatacls book1 book objects createtitlewinnie the pooh book2 book objects createtitledo you like green eggs and spam book3 book objects createtitlethree men in a boat reader1 reader objects createnameme reader2 reader objects createnameyou reader3 reader objects createnamesomeone book1 readby addreader1 reader2 book2 readby addreader2 book3 readby addreader3 cls book1 cls book2 cls book3 book1 book2 book3 cls reader1 cls reader2 cls reader3 reader1 reader2 reader3 def testprefetchgfkself taggeditem objects createtagawesome contentobjectself book1 taggeditem objects createtaggreat contentobjectself reader1 taggeditem objects createtagoutstanding contentobjectself book2 taggeditem objects createtagamazing contentobjectself reader3 1 for taggeditem table 1 for book table 1 for reader table with self assertnumqueries3 qs taggeditem objects prefetchrelatedcontentobject listqs def testprefetchgfknonintpkself comment objects createcommentawesome contentobjectself book1 1 for comment table 1 for book table with self assertnumqueries2 qs comment objects prefetchrelatedcontentobject c contentobject for c in qs def testprefetchgfkuuidpkself article article objects createnamedjango comment objects createcommentawesome contentobjectuuidarticle qs comment objects prefetchrelatedcontentobjectuuid self assertequalc contentobjectuuid for c in qs article def testprefetchgfkfkpkself book book objects createtitlepoems bookwithyear bookwithyear objects createbookbook publishedyear2019 comment objects createcommentawesome contentobjectbookwithyear qs comment objects prefetchrelatedcontentobject self assertequalc contentobject for c in qs bookwithyear def testtraversegfkself taggeditem objects createtagawesome contentobjectself book1 taggeditem objects createtagawesome contentobjectself book2 taggeditem objects createtagawesome contentobjectself book3 taggeditem objects createtagawesome contentobjectself reader1 taggeditem objects createtagawesome contentobjectself reader2 ct contenttype objects getformodelbook we get 3 queries 1 for main query 1 for contentobjects since they all use the same table and 1 for the readby relation with self assertnumqueries3 if we limit to books we know that they will have readby attributes so the following makes sense qs taggeditem objects filter contenttypect tagawesome prefetchrelatedcontentobjectreadby readersofawesomebooks r name for tag in qs for r in tag contentobject readby all self assertequalreadersofawesomebooks me you someone def testnullablegfkself taggeditem objects create tagawesome contentobjectself book1 createdbyself reader1 taggeditem objects createtaggreat contentobjectself book2 taggeditem objects createtagrubbish contentobjectself book3 with self assertnumqueries2 result t createdby for t in taggeditem objects prefetchrelatedcreatedby self assertequalresult t createdby for t in taggeditem objects all def testgenericrelationself bookmark bookmark objects createurlhttp www djangoproject com taggeditem objects createcontentobjectbookmark tagdjango taggeditem objects createcontentobjectbookmark tagpython with self assertnumqueries2 tags t tag for b in bookmark objects prefetchrelatedtags for t in b tags all self assertequalsortedtags django python def testcharfieldgfkself b bookmark objects createurlhttp www djangoproject com taggeditem objects createcontentobjectb tagdjango taggeditem objects createcontentobjectb favoriteb tagpython with self assertnumqueries3 bookmark bookmark objects filterpkb pk prefetchrelated tags favoritetags 0 self assertequal sortedi tag for i in bookmark tags all django python self assertequali tag for i in bookmark favoritetags all python def testcustomquerysetself bookmark bookmark objects createurlhttp www djangoproject com djangotag taggeditem objects createcontentobjectbookmark tagdjango taggeditem objects createcontentobjectbookmark tagpython with self assertnumqueries2 bookmark bookmark objects prefetchrelated prefetchtags taggeditem objects filtertagdjango get with self assertnumqueries0 self assertequallistbookmark tags all djangotag the custom queryset filters should be applied to the queryset instance returned by the manager self assertequallistbookmark tags all listbookmark tags all all def testdeletedgfkself taggeditem objects createtagawesome contentobjectself book1 taggeditem objects createtagawesome contentobjectself book2 ct contenttype objects getformodelbook book1pk self book1 pk self book1 delete with self assertnumqueries2 qs taggeditem objects filtertagawesome prefetchrelated contentobject result tag objectid tag contenttypeid tag contentobject for tag in qs self assertequal result book1pk ct pk none self book2 pk ct pk self book2 class multitableinheritancetesttestcase classmethod def setuptestdatacls cls book1 bookwithyear objects createtitlepoems publishedyear2010 cls book2 bookwithyear objects createtitlemore poems publishedyear2011 cls 1 withage objects create namejane firstbookcls book1 age50 cls 2 withage objects create nametom firstbookcls book1 age49 cls 3 withage objects create namerobert firstbookcls book2 age48 cls address address objects create cls 1 addresssomestreet 1 cls book2 ageds addcls 2 cls 3 cls br1 bookreview objects createbookcls book1 notesreview book1 cls br2 bookreview objects createbookcls book2 notesreview book2 def testforeignkeyself with self assertnumqueries2 qs withage objects prefetchrelatedaddresses addresses straddress for address in obj addresses all for obj in qs self assertequaladdresses strself address def testforeignkeytoinheritedself with self assertnumqueries2 qs bookreview objects prefetchrelatedbook titles obj book title for obj in qs self assertcountequaltitles poems more poems def testm2mtoinheritingmodelself qs withage objects prefetchrelatedbookswithyear with self assertnumqueries2 lst strbook for book in bookswithyear all for in qs qs withage objects all lst2 strbook for book in bookswithyear all for in qs self assertequallst lst2 qs bookwithyear objects prefetchrelatedageds with self assertnumqueries2 lst str for in book ageds all for book in qs qs bookwithyear objects all lst2 str for in book ageds all for book in qs self assertequallst lst2 def testparentlinkprefetchself with self assertnumqueries2 a for a in withage objects prefetchrelated overridesettingsdebugtrue def testchildlinkprefetchself with self assertnumqueries2 s a withage for a in objects prefetchrelatedwithage regression for 18090 the prefetching query must include an in clause note that on oracle the table name is upper case in the generated sql thus the lower call self assertinwithage connection queries1sql lower self assertin in connection queries1sql self assertequals a withage for a in objects all class foreignkeytofieldtesttestcase classmethod def setuptestdatacls cls book book objects createtitlepoems cls 1 objects createnamejane firstbookcls book cls 2 objects createnametom firstbookcls book cls 3 objects createnamerobert firstbookcls book cls address address objects create cls 1 addresssomestreet 1 favorites objects createcls 1 likescls 2 favorites objects createcls 2 likescls 3 favorites objects createcls 3 likescls 1 def testforeignkeyself with self assertnumqueries2 qs objects prefetchrelatedaddresses addresses straddress for address in obj addresses all for obj in qs self assertequaladdresses strself address def testm2mself with self assertnumqueries3 qs objects prefetchrelatedfavorites favorsme favorites strilike for ilike in favorites all strlikesme for likesme in favorsme all for in qs self assertequal favorites strself 2 strself 3 strself 3 strself 1 strself 1 strself 2 class lookuporderingtesttestcase classmethod def setuptestdatacls person1 person objects createnamejoe person2 person objects createnamemary set mainroom for each house before creating the next one for databases where supportsnullableuniqueconstraints is false house1 house objects createaddress123 main st room11 room objects createnamedining room househouse1 room objects createnamelounge househouse1 room objects createnamekitchen househouse1 house1 mainroom room11 house1 save person1 houses addhouse1 house2 house objects createaddress45 side st room21 room objects createnamedining room househouse2 room objects createnamelounge househouse2 house2 mainroom room21 house2 save person1 houses addhouse2 house3 house objects createaddress6 downing st room31 room objects createnamedining room househouse3 room objects createnamelounge househouse3 room objects createnamekitchen househouse3 house3 mainroom room31 house3 save person2 houses addhouse3 house4 house objects createaddress7 regents st room41 room objects createnamedining room househouse4 room objects createnamelounge househouse4 house4 mainroom room41 house4 save person2 houses addhouse4 def testorderself with self assertnumqueries4 the following two queries must be done in the same order as written otherwise primaryhouse will cause nonprefetched lookups qs person objects prefetchrelated housesrooms primaryhouseoccupants listp primaryhouse occupants all for p in qs class nullabletesttestcase classmethod def setuptestdatacls boss employee objects createnamepeter employee objects createnamejoe bossboss employee objects createnameangela bossboss def testtraversenullableself because we use selectrelated for boss it doesn t need to be prefetched but we can still traverse it although it contains some nulls with self assertnumqueries2 qs employee objects selectrelatedboss prefetchrelatedbossserfs coserfs liste boss serfs all if e boss is not none else for e in qs qs2 employee objects selectrelatedboss coserfs2 liste boss serfs all if e boss is not none else for e in qs2 self assertequalcoserfs coserfs2 def testprefetchnullableself one for main employee one for boss one for serfs with self assertnumqueries3 qs employee objects prefetchrelatedbossserfs coserfs liste boss serfs all if e boss is not none else for e in qs qs2 employee objects all coserfs2 liste boss serfs all if e boss is not none else for e in qs2 self assertequalcoserfs coserfs2 def testinbulkself boss1 employee objects createnamepeter boss2 employee objects createnamejack with self assertnumqueries2 prefetch is done and it does not cause any errors bulk employee objects prefetchrelatedserfs inbulk boss1 pk boss2 pk for b in bulk values listb serfs all class multidbteststestcase databases default other def testusingishonoredm2mself b book objects usingother a objects usingother book1 b createtitlepoems book2 b createtitlejane eyre book3 b createtitlewuthering heights book4 b createtitlesense and sensibility 1 a createnamecharlotte firstbookbook1 2 a createnameanne firstbookbook1 3 a createnameemily firstbookbook1 4 a createnamejane firstbookbook4 book1 s add1 2 3 book2 s add1 book3 s add3 book4 s add4 forward qs1 b prefetchrelateds with self assertnumqueries2 usingother books join s sn book title joina name for a in book s all for book in qs1 self assertequal books poems charlotte anne emilyn jane eyre charlotten wuthering heights emilyn sense and sensibility janen reverse qs2 a prefetchrelatedbooks with self assertnumqueries2 usingother s join s sn name joinb title for b in books all for in qs2 self assertequal s charlotte poems jane eyren anne poemsn emily poems wuthering heightsn jane sense and sensibilityn def testusingishonoredfkeyself b book objects usingother a objects usingother book1 b createtitlepoems book2 b createtitlesense and sensibility a createnamecharlotte bronte firstbookbook1 a createnamejane austen firstbookbook2 forward with self assertnumqueries2 usingother books join a firstbook title for a in a prefetchrelatedfirstbook self assertequalpoems sense and sensibility books reverse with self assertnumqueries2 usingother books join s sn b title joina name for a in b firsttimes all for b in b prefetchrelatedfirsttimes self assertequal books poems charlotte brontensense and sensibility jane austenn def testusingishonoredinheritanceself b bookwithyear objects usingother a withage objects usingother book1 b createtitlepoems publishedyear2010 b createtitlemore poems publishedyear2011 a createnamejane firstbookbook1 age50 a createnametom firstbookbook1 age49 parent link with self assertnumqueries2 usingother s joina name for a in a prefetchrelated self assertequals jane tom child link with self assertnumqueries2 usingother ages join stra withage age for a in a prefetchrelatedwithage self assertequalages 50 49 def testusingishonoredcustomqsself b book objects usingother a objects usingother book1 b createtitlepoems book2 b createtitlesense and sensibility a createnamecharlotte bronte firstbookbook1 a createnamejane austen firstbookbook2 implicit hinting with self assertnumqueries2 usingother prefetch prefetchfirsttimes queryset objects all books join s sn b title joina name for a in b firsttimes all for b in b prefetchrelatedprefetch self assertequal books poems charlotte brontensense and sensibility jane austenn explicit using on the same db with self assertnumqueries2 usingother prefetch prefetch firsttimes queryset objects usingother books join s sn b title joina name for a in b firsttimes all for b in b prefetchrelatedprefetch self assertequal books poems charlotte brontensense and sensibility jane austenn explicit using on a different db with self assertnumqueries1 usingdefault self assertnumqueries 1 usingother prefetch prefetch firsttimes queryset objects usingdefault books join s sn b title joina name for a in b firsttimes all for b in b prefetchrelatedprefetch self assertequalbooks poems n sense and sensibility n class ticket19607teststestcase classmethod def setuptestdatacls lessonentry objects bulkcreate lessonentryidid name1name1 name2name2 for id name1 name2 in 1 einfach simple 2 schwierig difficult wordentry objects bulkcreate wordentryidid lessonentryidlessonentryid namename for id lessonentryid name in 1 1 einfach 2 1 simple 3 2 schwierig 4 2 difficult def testbugself list wordentry objects prefetchrelated lessonentry lessonentrywordentryset class ticket21410teststestcase classmethod def setuptestdatacls book1 book objects createtitlepoems book2 book objects createtitlejane eyre book3 book objects createtitlewuthering heights book4 book objects createtitlesense and sensibility 1 2 objects createnamecharlotte firstbookbook1 2 2 objects createnameanne firstbookbook1 3 2 objects createnameemily firstbookbook1 4 2 objects createnamejane firstbookbook4 1 favoritebooks addbook1 book2 book3 2 favoritebooks addbook1 3 favoritebooks addbook2 4 favoritebooks addbook3 def testbugself list2 objects prefetchrelatedfirstbook favoritebooks class ticket21760teststestcase classmethod def setuptestdatacls cls rooms for in range3 house house objects create for in range3 cls rooms appendroom objects createhousehouse set mainroom for each house before creating the next one for databases where supportsnullableuniqueconstraints is false house mainroom cls rooms3 house save def testbugself prefetcher getprefetcherself rooms0 house house0 queryset prefetcher getprefetchquerysetslistroom objects all0 self assertnotin join strqueryset query class directprefetchedobjectcachereuseteststestcase classmethod def setuptestdatacls cls book1 cls book2 book objects createtitlebook1 book objects createtitlebook2 cls 11 cls 12 cls 21 objects createfirstbookcls book1 name11 objects createfirstbookcls book1 name12 objects createfirstbookcls book2 name21 cls 1address1 cls 1address2 cls 2address1 address objects createcls 11 addresshappy place address objects createcls 12 addresshaunted house address objects createcls 21 addresshappy place cls bookwithyear1 bookwithyear objects create titlepoems publishedyear2010 cls bookreview1 bookreview objects createbookcls bookwithyear1 def testdetectisfetchedself with self assertnumqueries3 books book objects filtertitleinbook1 book2 prefetchrelated prefetch firsttimes objects prefetchrelated prefetch addresses address objects filteraddresshappy place book1 book2 listbooks with self assertnumqueries0 self assertsequenceequal book1 firsttimes all self 11 self 12 self assertsequenceequalbook2 firsttimes all self 21 self assertsequenceequal book1 firsttimes all0 addresses all self 1address1 self assertsequenceequal book1 firsttimes all1 addresses all self assertsequenceequal book2 firsttimes all0 addresses all self 2address1 self assertequal listbook1 firsttimes all listbook1 firsttimes all all self assertequal listbook2 firsttimes all listbook2 firsttimes all all self assertequal listbook1 firsttimes all0 addresses all listbook1 firsttimes all0 addresses all all self assertequal listbook1 firsttimes all1 addresses all listbook1 firsttimes all1 addresses all all self assertequal listbook2 firsttimes all0 addresses all listbook2 firsttimes all0 addresses all all def testdetectisfetchedwithtoattrself with self assertnumqueries3 books book objects filtertitleinbook1 book2 prefetchrelated prefetch firsttimes objects prefetchrelated prefetch addresses address objects filteraddresshappy place toattrhappyplace toattrfirsts book1 book2 listbooks with self assertnumqueries0 self assertequalbook1 firsts self 11 self 12 self assertequalbook2 firsts self 21 self assertequal book1 firsts0 happyplace self 1address1 self assertequalbook1 firsts1 happyplace self assertequal book2 firsts0 happyplace self 2address1 def testprefetchreverseforeignkeyself with self assertnumqueries2 bookwithyear1 bookwithyear objects prefetchrelatedbookreviewset with self assertnumqueries0 self assertcountequal bookwithyear1 bookreviewset all self bookreview1 with self assertnumqueries0 prefetchrelatedobjectsbookwithyear1 bookreviewset def testaddclearsprefetchedobjectsself bookwithyear bookwithyear objects getpkself bookwithyear1 pk prefetchrelatedobjectsbookwithyear bookreviewset self assertcountequalbookwithyear bookreviewset all self bookreview1 newreview bookreview objects create bookwithyear bookreviewset addnewreview self assertcountequal bookwithyear bookreviewset all self bookreview1 newreview def testremoveclearsprefetchedobjectsself bookwithyear bookwithyear objects getpkself bookwithyear1 pk prefetchrelatedobjectsbookwithyear bookreviewset self assertcountequalbookwithyear bookreviewset all self bookreview1 bookwithyear bookreviewset removeself bookreview1 self assertcountequalbookwithyear bookreviewset all class readprefetchedobjectscacheteststestcase classmethod def setuptestdatacls cls book1 book objects createtitleles confessions volume i cls book2 book objects createtitlecandide cls 1 withage objects create namerousseau firstbookcls book1 age70 cls 2 withage objects create namevoltaire firstbookcls book2 age65 cls book1 s addcls 1 cls book2 s addcls 2 favorites objects createcls 1 likescls 2 def testretrievesresultsfromprefetchedobjectscacheself s withage objects prefetchrelated prefetch queryset objects prefetchrelated results are saved in the relatedmanager s cache prefetchedobjectscache and do not replace the relatedmanager on instances favorites prefetchfavoritesfirstbook with self assertnumqueries4 withage favorites book self assertsequenceequals self 1 self 2 class nestedprefetchteststestcase classmethod def setuptestdatacls house house objects createnamebig house address123 main st cls room room objects createnamekitchen househouse def testnestedprefetchisnotoverwrittenbyrelatedobjectself queryset house objects onlyname prefetchrelated prefetch rooms querysetroom objects prefetchrelated prefetchhouse querysethouse objects onlyaddress with self assertnumqueries3 house queryset first self assertisroom house iscachedself room true with self assertnumqueries0 house rooms first house address class prefetchlimitteststestdatamixin testcase skipunlessdbfeaturesupportsoverclause def testm2mforwardself s objects all meta ordering with self assertnumqueries3 books list book objects prefetchrelated prefetchs s prefetchs s1 toattrssliced for book in books with self subtestbookbook self assertequalbook ssliced listbook s all1 skipunlessdbfeaturesupportsoverclause def testm2mreverseself books book objects orderbytitle with self assertnumqueries3 s list objects prefetchrelated prefetchbooks books prefetchbooks books1 2 toattrbookssliced for in s with self subtest self assertequal bookssliced list books all1 2 skipunlessdbfeaturesupportsoverclause def testforeignkeyreverseself s objects orderbyname with self assertnumqueries3 books list book objects prefetchrelated prefetch firsttimes s prefetch firsttimes s1 toattrfirsttimessliced for book in books with self subtestbookbook self assertequal book firsttimessliced listbook firsttimes all1 skipunlessdbfeaturesupportsoverclause def testreverseorderingself s objects reverse reverse meta ordering with self assertnumqueries3 books list book objects prefetchrelated prefetchs s prefetchs s1 toattrssliced for book in books with self subtestbookbook self assertequalbook ssliced listbook s all1 skipifdbfeaturesupportsoverclause def testwindownotsupportedself s objects all msg prefetching from a limited queryset is only supported on backends that support window functions with self assertraisesmessagenotsupportederror msg listbook objects prefetchrelatedprefetchs s1 class deprecationteststestcase def testgetcurrentquerysetwarningself msg prefetch getcurrentqueryset is deprecated use getcurrentquerysets instead s objects all with self assertwarnsmessageremovedindjango60warning msg self assertequal prefetchs s getcurrentqueryset1 s with self assertwarnsmessageremovedindjango60warning msg self assertisnoneprefetchs getcurrentqueryset1 ignorewarningscategoryremovedindjango60warning def testprefetchonelevelfallbackself class nogetprefetchquerysetsdescriptorforwardmanytoonedescriptor def getprefetchquerysetself instances querysetnone if queryset is none return super getprefetchquerysetsinstances return super getprefetchquerysetsinstances queryset def getattributeself name if name getprefetchquerysets raise attributeerror return super getattributename house house objects create room room objects createhousehouse house mainroom room house save prefetchonelevel fallbacks to getprefetchqueryset prefetcher nogetprefetchquerysetsdescriptorroom meta getfieldhouse objlist additionallookups prefetchonelevel room prefetcher prefetchhouse house objects all 0 self assertequalobjlist house self assertequaladditionallookups objlist additionallookups prefetchonelevel room prefetcher prefetchhouse 0 self assertequalobjlist house self assertequaladditionallookups regression for 17439 a model bio with a onetoonefield primary key that references a non pk field name on the related model is prefetchable regression test for 20242 queryset in didn t work the first time when using prefetch_related this was fixed by the removal of chunked reads from queryset iteration in 70679243d1786e03557c28929f9762a119e3ac14 a m2m can be followed through another m2m charlotte poems jane eyre anne poems emily poems wuthering heights jane sense and sense charlotte poems jane eyre anne poems emily poems wuthering heights jane sense and sense charlotte poems jane eyre anne poems emily poems wuthering heights jane sense and sense objects retrieved with get get the prefetch behavior need a double poems jane eyre a m2m relation can be followed after a relation like foreignkey that doesn t have many objects a m2m relation can be followed after going through the select_related reverse of an o2o without the valueerror an was deleted due to the implicit save of the relation assignment without the valueerror a book was deleted due to the implicit save of reverse relation assignment related filtering of prefetched querysets is deferred on m2m and reverse m2o relations until necessary helper method that returns a list containing a list of the objects in the obj_iter then for each object in the obj_iter the path will be recursively travelled and the found objects are added to the return value set main_room for each house before creating the next one for databases where supports_nullable_unique_constraints is false ambiguous lookup was already seen with a different queryset lookup queryset shouldn t be evaluated ambiguous lookup houses_lst doesn t yet exist when performing houses_lst__rooms not ambiguous control lookups test lookups control lookups test lookups control lookups test lookups control lookups test lookups control lookups test lookups control lookups test lookups control lookups test lookups control lookups test lookups test basic test queryset filtering test flattened test inner select_related test inner prefetch test forwardmanytoonedescriptor test reverseonetoonedescriptor the custom queryset filters should be applied to the queryset instance returned by the manager regression test for 24873 queryset must be evaluated once to reproduce the bug nested prefetches whose name clashes with descriptor names person houses here are allowed that error doesn t affect managers with custom modeliterable subclasses to bypass caching at the related descriptor level don t use person houses all here related filtering of prefetched querysets is deferred until necessary when we prefetch the teachers and force the query we don t want the default manager on teachers to immediately get all the related qualifications since this will do one query per teacher 1 for taggeditem table 1 for book table 1 for reader table 1 for comment table 1 for book table a content_object can be traversed with prefetch_related and get to related objects on the other side assuming it is suitably filtered we get 3 queries 1 for main query 1 for content_objects since they all use the same table and 1 for the read_by relation if we limit to books we know that they will have read_by attributes so the following makes sense the custom queryset filters should be applied to the queryset instance returned by the manager regression for 18090 the prefetching query must include an in clause note that on oracle the table name is upper case in the generated sql thus the lower call test cases that demonstrate that ordering of lookups is important and ensure it is preserved set main_room for each house before creating the next one for databases where supports_nullable_unique_constraints is false the following two queries must be done in the same order as written otherwise primary_house will cause non prefetched lookups because we use select_related for boss it doesn t need to be prefetched but we can still traverse it although it contains some nulls one for main employee one for boss one for serfs in bulk does correctly prefetch objects by not using iterator directly prefetch is done and it does not cause any errors forward reverse forward reverse parent link child link implicit hinting explicit using on the same db explicit using on a different db set main_room for each house before creating the next one for databases where supports_nullable_unique_constraints is false prefetch_related reuses objects fetched in _prefetched_objects_cache when objects are prefetched and not stored as an instance attribute often intermediary relationships they are saved to the _prefetched_objects_cache attribute prefetch_related takes _prefetched_objects_cache into account when determining whether an object has been fetched 1 and retrieves results from it when it is populated 2 1 25546 duplicate queries on nested prefetch 2 27554 queryset evaluation fails with a mix of nested and flattened prefetches nested prefetch_related shouldn t trigger duplicate queries for the same lookup when intermediary results are prefetched without a destination attribute they are saved in the relatedmanager s cache _prefetched_objects_cache prefetch_related uses this cache 27554 results are saved in the relatedmanager s cache _prefetched_objects_cache and do not replace the relatedmanager on instances favorite_s withage favorites book the prefetched relationship is used rather than populating the reverse relationship from the parent when prefetching a set of child objects related to a set of parent objects and the child queryset itself specifies a prefetch back to the parent meta ordering reverse meta ordering prefetch_one_level fallbacks to get_prefetch_queryset
from unittest import mock

from django.contrib.contenttypes.models import ContentType
from django.core.exceptions import ObjectDoesNotExist
from django.db import NotSupportedError, connection
from django.db.models import Prefetch, QuerySet, prefetch_related_objects
from django.db.models.fields.related import ForwardManyToOneDescriptor
from django.db.models.query import get_prefetcher, prefetch_one_level
from django.db.models.sql import Query
from django.test import (
    TestCase,
    ignore_warnings,
    override_settings,
    skipIfDBFeature,
    skipUnlessDBFeature,
)
from django.test.utils import CaptureQueriesContext
from django.utils.deprecation import RemovedInDjango60Warning

from .models import (
    Article,
    Author,
    Author2,
    AuthorAddress,
    AuthorWithAge,
    Bio,
    Book,
    Bookmark,
    BookReview,
    BookWithYear,
    Comment,
    Department,
    Employee,
    FavoriteAuthors,
    House,
    LessonEntry,
    ModelIterableSubclass,
    Person,
    Qualification,
    Reader,
    Room,
    TaggedItem,
    Teacher,
    WordEntry,
)


class TestDataMixin:
    @classmethod
    def setUpTestData(cls):
        cls.book1 = Book.objects.create(title="Poems")
        cls.book2 = Book.objects.create(title="Jane Eyre")
        cls.book3 = Book.objects.create(title="Wuthering Heights")
        cls.book4 = Book.objects.create(title="Sense and Sensibility")

        cls.author1 = Author.objects.create(name="Charlotte", first_book=cls.book1)
        cls.author2 = Author.objects.create(name="Anne", first_book=cls.book1)
        cls.author3 = Author.objects.create(name="Emily", first_book=cls.book1)
        cls.author4 = Author.objects.create(name="Jane", first_book=cls.book4)

        cls.book1.authors.add(cls.author1, cls.author2, cls.author3)
        cls.book2.authors.add(cls.author1)
        cls.book3.authors.add(cls.author3)
        cls.book4.authors.add(cls.author4)

        cls.reader1 = Reader.objects.create(name="Amy")
        cls.reader2 = Reader.objects.create(name="Belinda")

        cls.reader1.books_read.add(cls.book1, cls.book4)
        cls.reader2.books_read.add(cls.book2, cls.book4)


class PrefetchRelatedTests(TestDataMixin, TestCase):
    def assertWhereContains(self, sql, needle):
        where_idx = sql.index("WHERE")
        self.assertEqual(
            sql.count(str(needle), where_idx),
            1,
            msg="WHERE clause doesn't contain %s, actual SQL: %s"
            % (needle, sql[where_idx:]),
        )

    def test_m2m_forward(self):
        with self.assertNumQueries(2):
            lists = [
                list(b.authors.all()) for b in Book.objects.prefetch_related("authors")
            ]

        normal_lists = [list(b.authors.all()) for b in Book.objects.all()]
        self.assertEqual(lists, normal_lists)

    def test_m2m_reverse(self):
        with self.assertNumQueries(2):
            lists = [
                list(a.books.all()) for a in Author.objects.prefetch_related("books")
            ]

        normal_lists = [list(a.books.all()) for a in Author.objects.all()]
        self.assertEqual(lists, normal_lists)

    def test_foreignkey_forward(self):
        with self.assertNumQueries(2):
            books = [
                a.first_book for a in Author.objects.prefetch_related("first_book")
            ]

        normal_books = [a.first_book for a in Author.objects.all()]
        self.assertEqual(books, normal_books)

    def test_foreignkey_reverse(self):
        with self.assertNumQueries(2):
            [
                list(b.first_time_authors.all())
                for b in Book.objects.prefetch_related("first_time_authors")
            ]

        self.assertSequenceEqual(self.book2.authors.all(), [self.author1])

    def test_onetoone_reverse_no_match(self):
        
        with self.assertNumQueries(2):
            book = Book.objects.prefetch_related("bookwithyear").all()[0]
        with self.assertNumQueries(0):
            with self.assertRaises(BookWithYear.DoesNotExist):
                book.bookwithyear

    def test_onetoone_reverse_with_to_field_pk(self):
        
        Bio.objects.bulk_create(
            [
                Bio(author=self.author1),
                Bio(author=self.author2),
                Bio(author=self.author3),
            ]
        )
        authors = Author.objects.filter(
            name__in=[self.author1, self.author2, self.author3],
        ).prefetch_related("bio")
        with self.assertNumQueries(2):
            for author in authors:
                self.assertEqual(author.name, author.bio.author.name)

    def test_survives_clone(self):
        with self.assertNumQueries(2):
            [
                list(b.first_time_authors.all())
                for b in Book.objects.prefetch_related("first_time_authors").exclude(
                    id=1000
                )
            ]

    def test_len(self):
        with self.assertNumQueries(2):
            qs = Book.objects.prefetch_related("first_time_authors")
            len(qs)
            [list(b.first_time_authors.all()) for b in qs]

    def test_bool(self):
        with self.assertNumQueries(2):
            qs = Book.objects.prefetch_related("first_time_authors")
            bool(qs)
            [list(b.first_time_authors.all()) for b in qs]

    def test_count(self):
        with self.assertNumQueries(2):
            qs = Book.objects.prefetch_related("first_time_authors")
            [b.first_time_authors.count() for b in qs]

    def test_exists(self):
        with self.assertNumQueries(2):
            qs = Book.objects.prefetch_related("first_time_authors")
            [b.first_time_authors.exists() for b in qs]

    def test_in_and_prefetch_related(self):
        
        qs = Book.objects.prefetch_related("first_time_authors")
        self.assertIn(qs[0], qs)

    def test_clear(self):
        with self.assertNumQueries(5):
            with_prefetch = Author.objects.prefetch_related("books")
            without_prefetch = with_prefetch.prefetch_related(None)
            [list(a.books.all()) for a in without_prefetch]

    def test_m2m_then_m2m(self):
        
        with self.assertNumQueries(3):
            qs = Author.objects.prefetch_related("books__read_by")
            lists = [
                [[str(r) for r in b.read_by.all()] for b in a.books.all()] for a in qs
            ]
            self.assertEqual(
                lists,
                [
                    [["Amy"], ["Belinda"]],  
                    [["Amy"]],  
                    [["Amy"], []],  
                    [["Amy", "Belinda"]],  
                ],
            )

    def test_overriding_prefetch(self):
        with self.assertNumQueries(3):
            qs = Author.objects.prefetch_related("books", "books__read_by")
            lists = [
                [[str(r) for r in b.read_by.all()] for b in a.books.all()] for a in qs
            ]
            self.assertEqual(
                lists,
                [
                    [["Amy"], ["Belinda"]],  
                    [["Amy"]],  
                    [["Amy"], []],  
                    [["Amy", "Belinda"]],  
                ],
            )
        with self.assertNumQueries(3):
            qs = Author.objects.prefetch_related("books__read_by", "books")
            lists = [
                [[str(r) for r in b.read_by.all()] for b in a.books.all()] for a in qs
            ]
            self.assertEqual(
                lists,
                [
                    [["Amy"], ["Belinda"]],  
                    [["Amy"]],  
                    [["Amy"], []],  
                    [["Amy", "Belinda"]],  
                ],
            )

    def test_get(self):
        
        
        with self.assertNumQueries(3):
            author = Author.objects.prefetch_related("books__read_by").get(
                name="Charlotte"
            )
            lists = [[str(r) for r in b.read_by.all()] for b in author.books.all()]
            self.assertEqual(lists, [["Amy"], ["Belinda"]])  

    def test_foreign_key_then_m2m(self):
        
        with self.assertNumQueries(2):
            qs = Author.objects.select_related("first_book").prefetch_related(
                "first_book__read_by"
            )
            lists = [[str(r) for r in a.first_book.read_by.all()] for a in qs]
            self.assertEqual(lists, [["Amy"], ["Amy"], ["Amy"], ["Amy", "Belinda"]])

    def test_reverse_one_to_one_then_m2m(self):
        
        qs = Author.objects.prefetch_related("bio__books").select_related("bio")

        with self.assertNumQueries(1):
            list(qs.all())

        Bio.objects.create(author=self.author1)
        with self.assertNumQueries(2):
            list(qs.all())

    def test_attribute_error(self):
        qs = Reader.objects.prefetch_related("books_read__xyz")
        msg = (
            "Cannot find 'xyz' on Book object, 'books_read__xyz' "
            "is an invalid parameter to prefetch_related()"
        )
        with self.assertRaisesMessage(AttributeError, msg) as cm:
            list(qs)

        self.assertIn("prefetch_related", str(cm.exception))

    def test_invalid_final_lookup(self):
        qs = Book.objects.prefetch_related("authors__name")
        msg = (
            "'authors__name' does not resolve to an item that supports "
            "prefetching - this is an invalid parameter to prefetch_related()."
        )
        with self.assertRaisesMessage(ValueError, msg) as cm:
            list(qs)

        self.assertIn("prefetch_related", str(cm.exception))
        self.assertIn("name", str(cm.exception))

    def test_prefetch_eq(self):
        prefetch_1 = Prefetch("authors", queryset=Author.objects.all())
        prefetch_2 = Prefetch("books", queryset=Book.objects.all())
        self.assertEqual(prefetch_1, prefetch_1)
        self.assertEqual(prefetch_1, mock.ANY)
        self.assertNotEqual(prefetch_1, prefetch_2)

    def test_forward_m2m_to_attr_conflict(self):
        msg = "to_attr=authors conflicts with a field on the Book model."
        authors = Author.objects.all()
        with self.assertRaisesMessage(ValueError, msg):
            list(
                Book.objects.prefetch_related(
                    Prefetch("authors", queryset=authors, to_attr="authors"),
                )
            )
        
        
        self.assertEqual(self.book1.authors.count(), 3)

    def test_reverse_m2m_to_attr_conflict(self):
        msg = "to_attr=books conflicts with a field on the Author model."
        poems = Book.objects.filter(title="Poems")
        with self.assertRaisesMessage(ValueError, msg):
            list(
                Author.objects.prefetch_related(
                    Prefetch("books", queryset=poems, to_attr="books"),
                )
            )
        
        
        self.assertEqual(self.author1.books.count(), 2)

    def test_m2m_then_reverse_fk_object_ids(self):
        with CaptureQueriesContext(connection) as queries:
            list(Book.objects.prefetch_related("authors__addresses"))

        sql = queries[-1]["sql"]
        self.assertWhereContains(sql, self.author1.name)

    def test_m2m_then_m2m_object_ids(self):
        with CaptureQueriesContext(connection) as queries:
            list(Book.objects.prefetch_related("authors__favorite_authors"))

        sql = queries[-1]["sql"]
        self.assertWhereContains(sql, self.author1.name)

    def test_m2m_then_reverse_one_to_one_object_ids(self):
        with CaptureQueriesContext(connection) as queries:
            list(Book.objects.prefetch_related("authors__authorwithage"))

        sql = queries[-1]["sql"]
        self.assertWhereContains(sql, self.author1.id)

    def test_filter_deferred(self):
        
        add_q = Query.add_q
        for relation in ["authors", "first_time_authors"]:
            with self.subTest(relation=relation):
                with mock.patch.object(
                    Query,
                    "add_q",
                    autospec=True,
                    side_effect=lambda self, q: add_q(self, q),
                ) as add_q_mock:
                    list(Book.objects.prefetch_related(relation))
                    self.assertEqual(add_q_mock.call_count, 1)

    def test_named_values_list(self):
        qs = Author.objects.prefetch_related("books")
        self.assertCountEqual(
            [value.name for value in qs.values_list("name", named=True)],
            ["Anne", "Charlotte", "Emily", "Jane"],
        )

    def test_m2m_prefetching_iterator_with_chunks(self):
        with self.assertNumQueries(3):
            authors = [
                b.authors.first()
                for b in Book.objects.prefetch_related("authors").iterator(chunk_size=2)
            ]
        self.assertEqual(
            authors,
            [self.author1, self.author1, self.author3, self.author4],
        )

    def test_m2m_prefetching_iterator_without_chunks_error(self):
        msg = (
            "chunk_size must be provided when using QuerySet.iterator() after "
            "prefetch_related()."
        )
        with self.assertRaisesMessage(ValueError, msg):
            Book.objects.prefetch_related("authors").iterator()


class RawQuerySetTests(TestDataMixin, TestCase):
    def test_basic(self):
        with self.assertNumQueries(2):
            books = Book.objects.raw(
                "SELECT * FROM prefetch_related_book WHERE id = %s", (self.book1.id,)
            ).prefetch_related("authors")
            book1 = list(books)[0]

        with self.assertNumQueries(0):
            self.assertCountEqual(
                book1.authors.all(), [self.author1, self.author2, self.author3]
            )

    def test_prefetch_before_raw(self):
        with self.assertNumQueries(2):
            books = Book.objects.prefetch_related("authors").raw(
                "SELECT * FROM prefetch_related_book WHERE id = %s", (self.book1.id,)
            )
            book1 = list(books)[0]

        with self.assertNumQueries(0):
            self.assertCountEqual(
                book1.authors.all(), [self.author1, self.author2, self.author3]
            )

    def test_clear(self):
        with self.assertNumQueries(5):
            with_prefetch = Author.objects.raw(
                "SELECT * FROM prefetch_related_author"
            ).prefetch_related("books")
            without_prefetch = with_prefetch.prefetch_related(None)
            [list(a.books.all()) for a in without_prefetch]


class CustomPrefetchTests(TestCase):
    @classmethod
    def traverse_qs(cls, obj_iter, path):
        
        ret_val = []

        if hasattr(obj_iter, "all"):
            obj_iter = obj_iter.all()

        try:
            iter(obj_iter)
        except TypeError:
            obj_iter = [obj_iter]

        for obj in obj_iter:
            rel_objs = []
            for part in path:
                if not part:
                    continue
                try:
                    related = getattr(obj, part[0])
                except ObjectDoesNotExist:
                    continue
                if related is not None:
                    rel_objs.extend(cls.traverse_qs(related, [part[1:]]))
            ret_val.append((obj, rel_objs))
        return ret_val

    @classmethod
    def setUpTestData(cls):
        cls.person1 = Person.objects.create(name="Joe")
        cls.person2 = Person.objects.create(name="Mary")

        
        

        cls.house1 = House.objects.create(
            name="House 1", address="123 Main St", owner=cls.person1
        )
        cls.room1_1 = Room.objects.create(name="Dining room", house=cls.house1)
        cls.room1_2 = Room.objects.create(name="Lounge", house=cls.house1)
        cls.room1_3 = Room.objects.create(name="Kitchen", house=cls.house1)
        cls.house1.main_room = cls.room1_1
        cls.house1.save()
        cls.person1.houses.add(cls.house1)

        cls.house2 = House.objects.create(
            name="House 2", address="45 Side St", owner=cls.person1
        )
        cls.room2_1 = Room.objects.create(name="Dining room", house=cls.house2)
        cls.room2_2 = Room.objects.create(name="Lounge", house=cls.house2)
        cls.room2_3 = Room.objects.create(name="Kitchen", house=cls.house2)
        cls.house2.main_room = cls.room2_1
        cls.house2.save()
        cls.person1.houses.add(cls.house2)

        cls.house3 = House.objects.create(
            name="House 3", address="6 Downing St", owner=cls.person2
        )
        cls.room3_1 = Room.objects.create(name="Dining room", house=cls.house3)
        cls.room3_2 = Room.objects.create(name="Lounge", house=cls.house3)
        cls.room3_3 = Room.objects.create(name="Kitchen", house=cls.house3)
        cls.house3.main_room = cls.room3_1
        cls.house3.save()
        cls.person2.houses.add(cls.house3)

        cls.house4 = House.objects.create(
            name="house 4", address="7 Regents St", owner=cls.person2
        )
        cls.room4_1 = Room.objects.create(name="Dining room", house=cls.house4)
        cls.room4_2 = Room.objects.create(name="Lounge", house=cls.house4)
        cls.room4_3 = Room.objects.create(name="Kitchen", house=cls.house4)
        cls.house4.main_room = cls.room4_1
        cls.house4.save()
        cls.person2.houses.add(cls.house4)

    def test_traverse_qs(self):
        qs = Person.objects.prefetch_related("houses")
        related_objs_normal = ([list(p.houses.all()) for p in qs],)
        related_objs_from_traverse = [
            [inner[0] for inner in o[1]] for o in self.traverse_qs(qs, [["houses"]])
        ]
        self.assertEqual(related_objs_normal, (related_objs_from_traverse,))

    def test_ambiguous(self):
        
        msg = (
            "'houses' lookup was already seen with a different queryset. You "
            "may need to adjust the ordering of your lookups."
        )
        
        with self.assertNumQueries(3):
            with self.assertRaisesMessage(ValueError, msg):
                self.traverse_qs(
                    Person.objects.prefetch_related(
                        "houses__rooms",
                        Prefetch("houses", queryset=House.objects.all()),
                    ),
                    [["houses", "rooms"]],
                )

        
        
        msg = (
            "Cannot find 'houses_lst' on Person object, 'houses_lst__rooms' is "
            "an invalid parameter to prefetch_related()"
        )
        with self.assertRaisesMessage(AttributeError, msg):
            self.traverse_qs(
                Person.objects.prefetch_related(
                    "houses_lst__rooms",
                    Prefetch(
                        "houses", queryset=House.objects.all(), to_attr="houses_lst"
                    ),
                ),
                [["houses", "rooms"]],
            )

        
        self.traverse_qs(
            Person.objects.prefetch_related("houses__rooms", "houses"),
            [["houses", "rooms"]],
        )

        self.traverse_qs(
            Person.objects.prefetch_related(
                "houses__rooms",
                Prefetch("houses", queryset=House.objects.all(), to_attr="houses_lst"),
            ),
            [["houses", "rooms"]],
        )

    def test_m2m(self):
        
        with self.assertNumQueries(2):
            lst1 = self.traverse_qs(
                Person.objects.prefetch_related("houses"), [["houses"]]
            )

        
        with self.assertNumQueries(2):
            lst2 = self.traverse_qs(
                Person.objects.prefetch_related(Prefetch("houses")), [["houses"]]
            )
        self.assertEqual(lst1, lst2)
        with self.assertNumQueries(2):
            lst2 = self.traverse_qs(
                Person.objects.prefetch_related(
                    Prefetch("houses", to_attr="houses_lst")
                ),
                [["houses_lst"]],
            )
        self.assertEqual(lst1, lst2)

    def test_reverse_m2m(self):
        
        with self.assertNumQueries(2):
            lst1 = self.traverse_qs(
                House.objects.prefetch_related("occupants"), [["occupants"]]
            )

        
        with self.assertNumQueries(2):
            lst2 = self.traverse_qs(
                House.objects.prefetch_related(Prefetch("occupants")), [["occupants"]]
            )
        self.assertEqual(lst1, lst2)
        with self.assertNumQueries(2):
            lst2 = self.traverse_qs(
                House.objects.prefetch_related(
                    Prefetch("occupants", to_attr="occupants_lst")
                ),
                [["occupants_lst"]],
            )
        self.assertEqual(lst1, lst2)

    def test_m2m_through_fk(self):
        
        with self.assertNumQueries(3):
            lst1 = self.traverse_qs(
                Room.objects.prefetch_related("house__occupants"),
                [["house", "occupants"]],
            )

        
        with self.assertNumQueries(3):
            lst2 = self.traverse_qs(
                Room.objects.prefetch_related(Prefetch("house__occupants")),
                [["house", "occupants"]],
            )
        self.assertEqual(lst1, lst2)
        with self.assertNumQueries(3):
            lst2 = self.traverse_qs(
                Room.objects.prefetch_related(
                    Prefetch("house__occupants", to_attr="occupants_lst")
                ),
                [["house", "occupants_lst"]],
            )
        self.assertEqual(lst1, lst2)

    def test_m2m_through_gfk(self):
        TaggedItem.objects.create(tag="houses", content_object=self.house1)
        TaggedItem.objects.create(tag="houses", content_object=self.house2)

        
        with self.assertNumQueries(3):
            lst1 = self.traverse_qs(
                TaggedItem.objects.filter(tag="houses").prefetch_related(
                    "content_object__rooms"
                ),
                [["content_object", "rooms"]],
            )

        
        with self.assertNumQueries(3):
            lst2 = self.traverse_qs(
                TaggedItem.objects.prefetch_related(
                    Prefetch("content_object"),
                    Prefetch("content_object__rooms", to_attr="rooms_lst"),
                ),
                [["content_object", "rooms_lst"]],
            )
        self.assertEqual(lst1, lst2)

    def test_o2m_through_m2m(self):
        
        with self.assertNumQueries(3):
            lst1 = self.traverse_qs(
                Person.objects.prefetch_related("houses", "houses__rooms"),
                [["houses", "rooms"]],
            )

        
        with self.assertNumQueries(3):
            lst2 = self.traverse_qs(
                Person.objects.prefetch_related(Prefetch("houses"), "houses__rooms"),
                [["houses", "rooms"]],
            )
        self.assertEqual(lst1, lst2)
        with self.assertNumQueries(3):
            lst2 = self.traverse_qs(
                Person.objects.prefetch_related(
                    Prefetch("houses"), Prefetch("houses__rooms")
                ),
                [["houses", "rooms"]],
            )
        self.assertEqual(lst1, lst2)
        with self.assertNumQueries(3):
            lst2 = self.traverse_qs(
                Person.objects.prefetch_related(
                    Prefetch("houses", to_attr="houses_lst"), "houses_lst__rooms"
                ),
                [["houses_lst", "rooms"]],
            )
        self.assertEqual(lst1, lst2)
        with self.assertNumQueries(3):
            lst2 = self.traverse_qs(
                Person.objects.prefetch_related(
                    Prefetch("houses", to_attr="houses_lst"),
                    Prefetch("houses_lst__rooms", to_attr="rooms_lst"),
                ),
                [["houses_lst", "rooms_lst"]],
            )
        self.assertEqual(lst1, lst2)

    def test_generic_rel(self):
        bookmark = Bookmark.objects.create(url="http://www.djangoproject.com/")
        TaggedItem.objects.create(content_object=bookmark, tag="django")
        TaggedItem.objects.create(
            content_object=bookmark, favorite=bookmark, tag="python"
        )

        
        with self.assertNumQueries(4):
            lst1 = self.traverse_qs(
                Bookmark.objects.prefetch_related(
                    "tags", "tags__content_object", "favorite_tags"
                ),
                [["tags", "content_object"], ["favorite_tags"]],
            )

        
        with self.assertNumQueries(4):
            lst2 = self.traverse_qs(
                Bookmark.objects.prefetch_related(
                    Prefetch("tags", to_attr="tags_lst"),
                    Prefetch("tags_lst__content_object"),
                    Prefetch("favorite_tags"),
                ),
                [["tags_lst", "content_object"], ["favorite_tags"]],
            )
        self.assertEqual(lst1, lst2)

    def test_traverse_single_item_property(self):
        
        with self.assertNumQueries(5):
            lst1 = self.traverse_qs(
                Person.objects.prefetch_related(
                    "houses__rooms",
                    "primary_house__occupants__houses",
                ),
                [["primary_house", "occupants", "houses"]],
            )

        
        with self.assertNumQueries(5):
            lst2 = self.traverse_qs(
                Person.objects.prefetch_related(
                    "houses__rooms",
                    Prefetch("primary_house__occupants", to_attr="occupants_lst"),
                    "primary_house__occupants_lst__houses",
                ),
                [["primary_house", "occupants_lst", "houses"]],
            )
        self.assertEqual(lst1, lst2)

    def test_traverse_multiple_items_property(self):
        
        with self.assertNumQueries(4):
            lst1 = self.traverse_qs(
                Person.objects.prefetch_related(
                    "houses",
                    "all_houses__occupants__houses",
                ),
                [["all_houses", "occupants", "houses"]],
            )

        
        with self.assertNumQueries(4):
            lst2 = self.traverse_qs(
                Person.objects.prefetch_related(
                    "houses",
                    Prefetch("all_houses__occupants", to_attr="occupants_lst"),
                    "all_houses__occupants_lst__houses",
                ),
                [["all_houses", "occupants_lst", "houses"]],
            )
        self.assertEqual(lst1, lst2)

    def test_custom_qs(self):
        
        with self.assertNumQueries(2):
            lst1 = list(Person.objects.prefetch_related("houses"))
        with self.assertNumQueries(2):
            lst2 = list(
                Person.objects.prefetch_related(
                    Prefetch(
                        "houses", queryset=House.objects.all(), to_attr="houses_lst"
                    )
                )
            )
        self.assertEqual(
            self.traverse_qs(lst1, [["houses"]]),
            self.traverse_qs(lst2, [["houses_lst"]]),
        )

        
        with self.assertNumQueries(2):
            lst2 = list(
                Person.objects.prefetch_related(
                    Prefetch(
                        "houses",
                        queryset=House.objects.filter(
                            pk__in=[self.house1.pk, self.house3.pk]
                        ),
                        to_attr="houses_lst",
                    )
                )
            )
        self.assertEqual(len(lst2[0].houses_lst), 1)
        self.assertEqual(lst2[0].houses_lst[0], self.house1)
        self.assertEqual(len(lst2[1].houses_lst), 1)
        self.assertEqual(lst2[1].houses_lst[0], self.house3)

        
        with self.assertNumQueries(3):
            lst1 = list(Person.objects.prefetch_related("houses__rooms"))
        with self.assertNumQueries(3):
            lst2 = list(
                Person.objects.prefetch_related(
                    Prefetch(
                        "houses__rooms",
                        queryset=Room.objects.all(),
                        to_attr="rooms_lst",
                    )
                )
            )
        self.assertEqual(
            self.traverse_qs(lst1, [["houses", "rooms"]]),
            self.traverse_qs(lst2, [["houses", "rooms_lst"]]),
        )

        
        with self.assertNumQueries(3):
            lst1 = list(Person.objects.prefetch_related("houses__owner"))
        with self.assertNumQueries(2):
            lst2 = list(
                Person.objects.prefetch_related(
                    Prefetch("houses", queryset=House.objects.select_related("owner"))
                )
            )
        self.assertEqual(
            self.traverse_qs(lst1, [["houses", "owner"]]),
            self.traverse_qs(lst2, [["houses", "owner"]]),
        )

        
        inner_rooms_qs = Room.objects.filter(pk__in=[self.room1_1.pk, self.room1_2.pk])
        houses_qs_prf = House.objects.prefetch_related(
            Prefetch("rooms", queryset=inner_rooms_qs, to_attr="rooms_lst")
        )
        with self.assertNumQueries(4):
            lst2 = list(
                Person.objects.prefetch_related(
                    Prefetch(
                        "houses",
                        queryset=houses_qs_prf.filter(pk=self.house1.pk),
                        to_attr="houses_lst",
                    ),
                    Prefetch("houses_lst__rooms_lst__main_room_of"),
                )
            )

        self.assertEqual(len(lst2[0].houses_lst[0].rooms_lst), 2)
        self.assertEqual(lst2[0].houses_lst[0].rooms_lst[0], self.room1_1)
        self.assertEqual(lst2[0].houses_lst[0].rooms_lst[1], self.room1_2)
        self.assertEqual(lst2[0].houses_lst[0].rooms_lst[0].main_room_of, self.house1)
        self.assertEqual(len(lst2[1].houses_lst), 0)

        
        houses = House.objects.select_related("owner")
        with self.assertNumQueries(6):
            rooms = Room.objects.prefetch_related("house")
            lst1 = self.traverse_qs(rooms, [["house", "owner"]])
        with self.assertNumQueries(2):
            rooms = Room.objects.prefetch_related(Prefetch("house", queryset=houses))
            lst2 = self.traverse_qs(rooms, [["house", "owner"]])
        self.assertEqual(lst1, lst2)
        with self.assertNumQueries(2):
            houses = House.objects.select_related("owner")
            rooms = Room.objects.prefetch_related(
                Prefetch("house", queryset=houses, to_attr="house_attr")
            )
            lst2 = self.traverse_qs(rooms, [["house_attr", "owner"]])
        self.assertEqual(lst1, lst2)
        room = Room.objects.prefetch_related(
            Prefetch("house", queryset=houses.filter(address="DoesNotExist"))
        ).first()
        with self.assertRaises(ObjectDoesNotExist):
            getattr(room, "house")
        room = Room.objects.prefetch_related(
            Prefetch(
                "house",
                queryset=houses.filter(address="DoesNotExist"),
                to_attr="house_attr",
            )
        ).first()
        self.assertIsNone(room.house_attr)
        rooms = Room.objects.prefetch_related(
            Prefetch("house", queryset=House.objects.only("name"))
        )
        with self.assertNumQueries(2):
            getattr(rooms.first().house, "name")
        with self.assertNumQueries(3):
            getattr(rooms.first().house, "address")

        
        houses = House.objects.select_related("owner")
        with self.assertNumQueries(6):
            rooms = Room.objects.prefetch_related("main_room_of")
            lst1 = self.traverse_qs(rooms, [["main_room_of", "owner"]])
        with self.assertNumQueries(2):
            rooms = Room.objects.prefetch_related(
                Prefetch("main_room_of", queryset=houses)
            )
            lst2 = self.traverse_qs(rooms, [["main_room_of", "owner"]])
        self.assertEqual(lst1, lst2)
        with self.assertNumQueries(2):
            rooms = list(
                Room.objects.prefetch_related(
                    Prefetch(
                        "main_room_of",
                        queryset=houses,
                        to_attr="main_room_of_attr",
                    )
                )
            )
            lst2 = self.traverse_qs(rooms, [["main_room_of_attr", "owner"]])
        self.assertEqual(lst1, lst2)
        room = (
            Room.objects.filter(main_room_of__isnull=False)
            .prefetch_related(
                Prefetch("main_room_of", queryset=houses.filter(address="DoesNotExist"))
            )
            .first()
        )
        with self.assertRaises(ObjectDoesNotExist):
            getattr(room, "main_room_of")
        room = (
            Room.objects.filter(main_room_of__isnull=False)
            .prefetch_related(
                Prefetch(
                    "main_room_of",
                    queryset=houses.filter(address="DoesNotExist"),
                    to_attr="main_room_of_attr",
                )
            )
            .first()
        )
        self.assertIsNone(room.main_room_of_attr)

        
        
        person = Person.objects.prefetch_related(
            Prefetch("houses", queryset=House.objects.filter(name="House 1")),
        ).get(pk=self.person1.pk)
        self.assertEqual(
            list(person.houses.all()),
            list(person.houses.all().all()),
        )

    def test_nested_prefetch_related_are_not_overwritten(self):
        
        houses_2 = House.objects.prefetch_related(Prefetch("rooms"))
        persons = Person.objects.prefetch_related(Prefetch("houses", queryset=houses_2))
        houses = House.objects.prefetch_related(Prefetch("occupants", queryset=persons))
        list(houses)  
        self.assertEqual(
            houses.all()[0].occupants.all()[0].houses.all()[1].rooms.all()[0],
            self.room2_1,
        )

    def test_nested_prefetch_related_with_duplicate_prefetcher(self):
        
        occupants = Person.objects.prefetch_related(
            Prefetch("houses", to_attr="some_attr_name"),
            Prefetch("houses", queryset=House.objects.prefetch_related("main_room")),
        )
        houses = House.objects.prefetch_related(
            Prefetch("occupants", queryset=occupants)
        )
        with self.assertNumQueries(5):
            self.traverse_qs(list(houses), [["occupants", "houses", "main_room"]])

    def test_nested_prefetch_related_with_duplicate_prefetch_and_depth(self):
        people = Person.objects.prefetch_related(
            Prefetch(
                "houses__main_room",
                queryset=Room.objects.filter(name="Dining room"),
                to_attr="dining_room",
            ),
            "houses__main_room",
        )
        with self.assertNumQueries(4):
            main_room = people[0].houses.all()[0]

        people = Person.objects.prefetch_related(
            "houses__main_room",
            Prefetch(
                "houses__main_room",
                queryset=Room.objects.filter(name="Dining room"),
                to_attr="dining_room",
            ),
        )
        with self.assertNumQueries(4):
            main_room = people[0].houses.all()[0]

        self.assertEqual(main_room.main_room, self.room1_1)

    def test_values_queryset(self):
        msg = "Prefetch querysets cannot use raw(), values(), and values_list()."
        with self.assertRaisesMessage(ValueError, msg):
            Prefetch("houses", House.objects.values("pk"))
        with self.assertRaisesMessage(ValueError, msg):
            Prefetch("houses", House.objects.values_list("pk"))
        
        self.assertIs(
            Teacher.objects_custom.all()._iterable_class, ModelIterableSubclass
        )
        Prefetch("teachers", Teacher.objects_custom.all())

    def test_raw_queryset(self):
        msg = "Prefetch querysets cannot use raw(), values(), and values_list()."
        with self.assertRaisesMessage(ValueError, msg):
            Prefetch("houses", House.objects.raw("select pk from house"))

    def test_to_attr_doesnt_cache_through_attr_as_list(self):
        house = House.objects.prefetch_related(
            Prefetch("rooms", queryset=Room.objects.all(), to_attr="to_rooms"),
        ).get(pk=self.house3.pk)
        self.assertIsInstance(house.rooms.all(), QuerySet)

    def test_to_attr_cached_property(self):
        persons = Person.objects.prefetch_related(
            Prefetch("houses", House.objects.all(), to_attr="cached_all_houses"),
        )
        for person in persons:
            
            
            all_houses = list(House.objects.filter(occupants=person))
            with self.assertNumQueries(0):
                self.assertEqual(person.cached_all_houses, all_houses)

    def test_filter_deferred(self):
        
        add_q = Query.add_q
        with mock.patch.object(
            Query,
            "add_q",
            autospec=True,
            side_effect=lambda self, q: add_q(self, q),
        ) as add_q_mock:
            list(
                House.objects.prefetch_related(
                    Prefetch("occupants", queryset=Person.objects.all())
                )
            )
            self.assertEqual(add_q_mock.call_count, 1)


class DefaultManagerTests(TestCase):
    @classmethod
    def setUpTestData(cls):
        cls.qual1 = Qualification.objects.create(name="BA")
        cls.qual2 = Qualification.objects.create(name="BSci")
        cls.qual3 = Qualification.objects.create(name="MA")
        cls.qual4 = Qualification.objects.create(name="PhD")

        cls.teacher1 = Teacher.objects.create(name="Mr Cleese")
        cls.teacher2 = Teacher.objects.create(name="Mr Idle")
        cls.teacher3 = Teacher.objects.create(name="Mr Chapman")
        cls.teacher1.qualifications.add(cls.qual1, cls.qual2, cls.qual3, cls.qual4)
        cls.teacher2.qualifications.add(cls.qual1)
        cls.teacher3.qualifications.add(cls.qual2)

        cls.dept1 = Department.objects.create(name="English")
        cls.dept2 = Department.objects.create(name="Physics")
        cls.dept1.teachers.add(cls.teacher1, cls.teacher2)
        cls.dept2.teachers.add(cls.teacher1, cls.teacher3)

    def test_m2m_then_m2m(self):
        with self.assertNumQueries(3):
            
            
            
            qs = Department.objects.prefetch_related("teachers")
            depts = "".join(
                "%s department: %s\n"
                % (dept.name, ", ".join(str(t) for t in dept.teachers.all()))
                for dept in qs
            )

            self.assertEqual(
                depts,
                "English department: Mr Cleese (BA, BSci, MA, PhD), Mr Idle (BA)\n"
                "Physics department: Mr Cleese (BA, BSci, MA, PhD), Mr Chapman "
                "(BSci)\n",
            )


class GenericRelationTests(TestCase):
    @classmethod
    def setUpTestData(cls):
        book1 = Book.objects.create(title="Winnie the Pooh")
        book2 = Book.objects.create(title="Do you like green eggs and spam?")
        book3 = Book.objects.create(title="Three Men In A Boat")

        reader1 = Reader.objects.create(name="me")
        reader2 = Reader.objects.create(name="you")
        reader3 = Reader.objects.create(name="someone")

        book1.read_by.add(reader1, reader2)
        book2.read_by.add(reader2)
        book3.read_by.add(reader3)

        cls.book1, cls.book2, cls.book3 = book1, book2, book3
        cls.reader1, cls.reader2, cls.reader3 = reader1, reader2, reader3

    def test_prefetch_GFK(self):
        TaggedItem.objects.create(tag="awesome", content_object=self.book1)
        TaggedItem.objects.create(tag="great", content_object=self.reader1)
        TaggedItem.objects.create(tag="outstanding", content_object=self.book2)
        TaggedItem.objects.create(tag="amazing", content_object=self.reader3)

        
        with self.assertNumQueries(3):
            qs = TaggedItem.objects.prefetch_related("content_object")
            list(qs)

    def test_prefetch_GFK_nonint_pk(self):
        Comment.objects.create(comment="awesome", content_object=self.book1)

        
        with self.assertNumQueries(2):
            qs = Comment.objects.prefetch_related("content_object")
            [c.content_object for c in qs]

    def test_prefetch_GFK_uuid_pk(self):
        article = Article.objects.create(name="Django")
        Comment.objects.create(comment="awesome", content_object_uuid=article)
        qs = Comment.objects.prefetch_related("content_object_uuid")
        self.assertEqual([c.content_object_uuid for c in qs], [article])

    def test_prefetch_GFK_fk_pk(self):
        book = Book.objects.create(title="Poems")
        book_with_year = BookWithYear.objects.create(book=book, published_year=2019)
        Comment.objects.create(comment="awesome", content_object=book_with_year)
        qs = Comment.objects.prefetch_related("content_object")
        self.assertEqual([c.content_object for c in qs], [book_with_year])

    def test_traverse_GFK(self):
        
        TaggedItem.objects.create(tag="awesome", content_object=self.book1)
        TaggedItem.objects.create(tag="awesome", content_object=self.book2)
        TaggedItem.objects.create(tag="awesome", content_object=self.book3)
        TaggedItem.objects.create(tag="awesome", content_object=self.reader1)
        TaggedItem.objects.create(tag="awesome", content_object=self.reader2)

        ct = ContentType.objects.get_for_model(Book)

        
        
        with self.assertNumQueries(3):
            
            
            qs = TaggedItem.objects.filter(
                content_type=ct, tag="awesome"
            ).prefetch_related("content_object__read_by")
            readers_of_awesome_books = {
                r.name for tag in qs for r in tag.content_object.read_by.all()
            }
            self.assertEqual(readers_of_awesome_books, {"me", "you", "someone"})

    def test_nullable_GFK(self):
        TaggedItem.objects.create(
            tag="awesome", content_object=self.book1, created_by=self.reader1
        )
        TaggedItem.objects.create(tag="great", content_object=self.book2)
        TaggedItem.objects.create(tag="rubbish", content_object=self.book3)

        with self.assertNumQueries(2):
            result = [
                t.created_by for t in TaggedItem.objects.prefetch_related("created_by")
            ]

        self.assertEqual(result, [t.created_by for t in TaggedItem.objects.all()])

    def test_generic_relation(self):
        bookmark = Bookmark.objects.create(url="http://www.djangoproject.com/")
        TaggedItem.objects.create(content_object=bookmark, tag="django")
        TaggedItem.objects.create(content_object=bookmark, tag="python")

        with self.assertNumQueries(2):
            tags = [
                t.tag
                for b in Bookmark.objects.prefetch_related("tags")
                for t in b.tags.all()
            ]
            self.assertEqual(sorted(tags), ["django", "python"])

    def test_charfield_GFK(self):
        b = Bookmark.objects.create(url="http://www.djangoproject.com/")
        TaggedItem.objects.create(content_object=b, tag="django")
        TaggedItem.objects.create(content_object=b, favorite=b, tag="python")

        with self.assertNumQueries(3):
            bookmark = Bookmark.objects.filter(pk=b.pk).prefetch_related(
                "tags", "favorite_tags"
            )[0]
            self.assertEqual(
                sorted(i.tag for i in bookmark.tags.all()), ["django", "python"]
            )
            self.assertEqual([i.tag for i in bookmark.favorite_tags.all()], ["python"])

    def test_custom_queryset(self):
        bookmark = Bookmark.objects.create(url="http://www.djangoproject.com/")
        django_tag = TaggedItem.objects.create(content_object=bookmark, tag="django")
        TaggedItem.objects.create(content_object=bookmark, tag="python")

        with self.assertNumQueries(2):
            bookmark = Bookmark.objects.prefetch_related(
                Prefetch("tags", TaggedItem.objects.filter(tag="django")),
            ).get()

        with self.assertNumQueries(0):
            self.assertEqual(list(bookmark.tags.all()), [django_tag])

        
        
        self.assertEqual(list(bookmark.tags.all()), list(bookmark.tags.all().all()))

    def test_deleted_GFK(self):
        TaggedItem.objects.create(tag="awesome", content_object=self.book1)
        TaggedItem.objects.create(tag="awesome", content_object=self.book2)
        ct = ContentType.objects.get_for_model(Book)

        book1_pk = self.book1.pk
        self.book1.delete()

        with self.assertNumQueries(2):
            qs = TaggedItem.objects.filter(tag="awesome").prefetch_related(
                "content_object"
            )
            result = [
                (tag.object_id, tag.content_type_id, tag.content_object) for tag in qs
            ]
            self.assertEqual(
                result,
                [
                    (book1_pk, ct.pk, None),
                    (self.book2.pk, ct.pk, self.book2),
                ],
            )


class MultiTableInheritanceTest(TestCase):
    @classmethod
    def setUpTestData(cls):
        cls.book1 = BookWithYear.objects.create(title="Poems", published_year=2010)
        cls.book2 = BookWithYear.objects.create(title="More poems", published_year=2011)
        cls.author1 = AuthorWithAge.objects.create(
            name="Jane", first_book=cls.book1, age=50
        )
        cls.author2 = AuthorWithAge.objects.create(
            name="Tom", first_book=cls.book1, age=49
        )
        cls.author3 = AuthorWithAge.objects.create(
            name="Robert", first_book=cls.book2, age=48
        )
        cls.author_address = AuthorAddress.objects.create(
            author=cls.author1, address="SomeStreet 1"
        )
        cls.book2.aged_authors.add(cls.author2, cls.author3)
        cls.br1 = BookReview.objects.create(book=cls.book1, notes="review book1")
        cls.br2 = BookReview.objects.create(book=cls.book2, notes="review book2")

    def test_foreignkey(self):
        with self.assertNumQueries(2):
            qs = AuthorWithAge.objects.prefetch_related("addresses")
            addresses = [
                [str(address) for address in obj.addresses.all()] for obj in qs
            ]
        self.assertEqual(addresses, [[str(self.author_address)], [], []])

    def test_foreignkey_to_inherited(self):
        with self.assertNumQueries(2):
            qs = BookReview.objects.prefetch_related("book")
            titles = [obj.book.title for obj in qs]
        self.assertCountEqual(titles, ["Poems", "More poems"])

    def test_m2m_to_inheriting_model(self):
        qs = AuthorWithAge.objects.prefetch_related("books_with_year")
        with self.assertNumQueries(2):
            lst = [
                [str(book) for book in author.books_with_year.all()] for author in qs
            ]
        qs = AuthorWithAge.objects.all()
        lst2 = [[str(book) for book in author.books_with_year.all()] for author in qs]
        self.assertEqual(lst, lst2)

        qs = BookWithYear.objects.prefetch_related("aged_authors")
        with self.assertNumQueries(2):
            lst = [[str(author) for author in book.aged_authors.all()] for book in qs]
        qs = BookWithYear.objects.all()
        lst2 = [[str(author) for author in book.aged_authors.all()] for book in qs]
        self.assertEqual(lst, lst2)

    def test_parent_link_prefetch(self):
        with self.assertNumQueries(2):
            [a.author for a in AuthorWithAge.objects.prefetch_related("author")]

    @override_settings(DEBUG=True)
    def test_child_link_prefetch(self):
        with self.assertNumQueries(2):
            authors = [
                a.authorwithage
                for a in Author.objects.prefetch_related("authorwithage")
            ]

        
        
        
        self.assertIn("authorwithage", connection.queries[-1]["sql"].lower())
        self.assertIn(" IN ", connection.queries[-1]["sql"])

        self.assertEqual(authors, [a.authorwithage for a in Author.objects.all()])


class ForeignKeyToFieldTest(TestCase):
    @classmethod
    def setUpTestData(cls):
        cls.book = Book.objects.create(title="Poems")
        cls.author1 = Author.objects.create(name="Jane", first_book=cls.book)
        cls.author2 = Author.objects.create(name="Tom", first_book=cls.book)
        cls.author3 = Author.objects.create(name="Robert", first_book=cls.book)
        cls.author_address = AuthorAddress.objects.create(
            author=cls.author1, address="SomeStreet 1"
        )
        FavoriteAuthors.objects.create(author=cls.author1, likes_author=cls.author2)
        FavoriteAuthors.objects.create(author=cls.author2, likes_author=cls.author3)
        FavoriteAuthors.objects.create(author=cls.author3, likes_author=cls.author1)

    def test_foreignkey(self):
        with self.assertNumQueries(2):
            qs = Author.objects.prefetch_related("addresses")
            addresses = [
                [str(address) for address in obj.addresses.all()] for obj in qs
            ]
        self.assertEqual(addresses, [[str(self.author_address)], [], []])

    def test_m2m(self):
        with self.assertNumQueries(3):
            qs = Author.objects.prefetch_related("favorite_authors", "favors_me")
            favorites = [
                (
                    [str(i_like) for i_like in author.favorite_authors.all()],
                    [str(likes_me) for likes_me in author.favors_me.all()],
                )
                for author in qs
            ]
            self.assertEqual(
                favorites,
                [
                    ([str(self.author2)], [str(self.author3)]),
                    ([str(self.author3)], [str(self.author1)]),
                    ([str(self.author1)], [str(self.author2)]),
                ],
            )


class LookupOrderingTest(TestCase):
    

    @classmethod
    def setUpTestData(cls):
        person1 = Person.objects.create(name="Joe")
        person2 = Person.objects.create(name="Mary")

        
        
        house1 = House.objects.create(address="123 Main St")
        room1_1 = Room.objects.create(name="Dining room", house=house1)
        Room.objects.create(name="Lounge", house=house1)
        Room.objects.create(name="Kitchen", house=house1)
        house1.main_room = room1_1
        house1.save()
        person1.houses.add(house1)

        house2 = House.objects.create(address="45 Side St")
        room2_1 = Room.objects.create(name="Dining room", house=house2)
        Room.objects.create(name="Lounge", house=house2)
        house2.main_room = room2_1
        house2.save()
        person1.houses.add(house2)

        house3 = House.objects.create(address="6 Downing St")
        room3_1 = Room.objects.create(name="Dining room", house=house3)
        Room.objects.create(name="Lounge", house=house3)
        Room.objects.create(name="Kitchen", house=house3)
        house3.main_room = room3_1
        house3.save()
        person2.houses.add(house3)

        house4 = House.objects.create(address="7 Regents St")
        room4_1 = Room.objects.create(name="Dining room", house=house4)
        Room.objects.create(name="Lounge", house=house4)
        house4.main_room = room4_1
        house4.save()
        person2.houses.add(house4)

    def test_order(self):
        with self.assertNumQueries(4):
            
            
            qs = Person.objects.prefetch_related(
                "houses__rooms", "primary_house__occupants"
            )
            [list(p.primary_house.occupants.all()) for p in qs]


class NullableTest(TestCase):
    @classmethod
    def setUpTestData(cls):
        boss = Employee.objects.create(name="Peter")
        Employee.objects.create(name="Joe", boss=boss)
        Employee.objects.create(name="Angela", boss=boss)

    def test_traverse_nullable(self):
        
        
        with self.assertNumQueries(2):
            qs = Employee.objects.select_related("boss").prefetch_related("boss__serfs")
            co_serfs = [
                list(e.boss.serfs.all()) if e.boss is not None else [] for e in qs
            ]

        qs2 = Employee.objects.select_related("boss")
        co_serfs2 = [
            list(e.boss.serfs.all()) if e.boss is not None else [] for e in qs2
        ]

        self.assertEqual(co_serfs, co_serfs2)

    def test_prefetch_nullable(self):
        
        with self.assertNumQueries(3):
            qs = Employee.objects.prefetch_related("boss__serfs")
            co_serfs = [
                list(e.boss.serfs.all()) if e.boss is not None else [] for e in qs
            ]

        qs2 = Employee.objects.all()
        co_serfs2 = [
            list(e.boss.serfs.all()) if e.boss is not None else [] for e in qs2
        ]

        self.assertEqual(co_serfs, co_serfs2)

    def test_in_bulk(self):
        
        boss1 = Employee.objects.create(name="Peter")
        boss2 = Employee.objects.create(name="Jack")
        with self.assertNumQueries(2):
            
            bulk = Employee.objects.prefetch_related("serfs").in_bulk(
                [boss1.pk, boss2.pk]
            )
            for b in bulk.values():
                list(b.serfs.all())


class MultiDbTests(TestCase):
    databases = {"default", "other"}

    def test_using_is_honored_m2m(self):
        B = Book.objects.using("other")
        A = Author.objects.using("other")
        book1 = B.create(title="Poems")
        book2 = B.create(title="Jane Eyre")
        book3 = B.create(title="Wuthering Heights")
        book4 = B.create(title="Sense and Sensibility")

        author1 = A.create(name="Charlotte", first_book=book1)
        author2 = A.create(name="Anne", first_book=book1)
        author3 = A.create(name="Emily", first_book=book1)
        author4 = A.create(name="Jane", first_book=book4)

        book1.authors.add(author1, author2, author3)
        book2.authors.add(author1)
        book3.authors.add(author3)
        book4.authors.add(author4)

        
        qs1 = B.prefetch_related("authors")
        with self.assertNumQueries(2, using="other"):
            books = "".join(
                "%s (%s)\n"
                % (book.title, ", ".join(a.name for a in book.authors.all()))
                for book in qs1
            )
        self.assertEqual(
            books,
            "Poems (Charlotte, Anne, Emily)\n"
            "Jane Eyre (Charlotte)\n"
            "Wuthering Heights (Emily)\n"
            "Sense and Sensibility (Jane)\n",
        )

        
        qs2 = A.prefetch_related("books")
        with self.assertNumQueries(2, using="other"):
            authors = "".join(
                "%s: %s\n"
                % (author.name, ", ".join(b.title for b in author.books.all()))
                for author in qs2
            )
        self.assertEqual(
            authors,
            "Charlotte: Poems, Jane Eyre\n"
            "Anne: Poems\n"
            "Emily: Poems, Wuthering Heights\n"
            "Jane: Sense and Sensibility\n",
        )

    def test_using_is_honored_fkey(self):
        B = Book.objects.using("other")
        A = Author.objects.using("other")
        book1 = B.create(title="Poems")
        book2 = B.create(title="Sense and Sensibility")

        A.create(name="Charlotte Bronte", first_book=book1)
        A.create(name="Jane Austen", first_book=book2)

        
        with self.assertNumQueries(2, using="other"):
            books = ", ".join(
                a.first_book.title for a in A.prefetch_related("first_book")
            )
        self.assertEqual("Poems, Sense and Sensibility", books)

        
        with self.assertNumQueries(2, using="other"):
            books = "".join(
                "%s (%s)\n"
                % (b.title, ", ".join(a.name for a in b.first_time_authors.all()))
                for b in B.prefetch_related("first_time_authors")
            )
        self.assertEqual(
            books,
            "Poems (Charlotte Bronte)\nSense and Sensibility (Jane Austen)\n",
        )

    def test_using_is_honored_inheritance(self):
        B = BookWithYear.objects.using("other")
        A = AuthorWithAge.objects.using("other")
        book1 = B.create(title="Poems", published_year=2010)
        B.create(title="More poems", published_year=2011)
        A.create(name="Jane", first_book=book1, age=50)
        A.create(name="Tom", first_book=book1, age=49)

        
        with self.assertNumQueries(2, using="other"):
            authors = ", ".join(a.author.name for a in A.prefetch_related("author"))

        self.assertEqual(authors, "Jane, Tom")

        
        with self.assertNumQueries(2, using="other"):
            ages = ", ".join(
                str(a.authorwithage.age) for a in A.prefetch_related("authorwithage")
            )

        self.assertEqual(ages, "50, 49")

    def test_using_is_honored_custom_qs(self):
        B = Book.objects.using("other")
        A = Author.objects.using("other")
        book1 = B.create(title="Poems")
        book2 = B.create(title="Sense and Sensibility")

        A.create(name="Charlotte Bronte", first_book=book1)
        A.create(name="Jane Austen", first_book=book2)

        
        with self.assertNumQueries(2, using="other"):
            prefetch = Prefetch("first_time_authors", queryset=Author.objects.all())
            books = "".join(
                "%s (%s)\n"
                % (b.title, ", ".join(a.name for a in b.first_time_authors.all()))
                for b in B.prefetch_related(prefetch)
            )
        self.assertEqual(
            books,
            "Poems (Charlotte Bronte)\nSense and Sensibility (Jane Austen)\n",
        )
        
        with self.assertNumQueries(2, using="other"):
            prefetch = Prefetch(
                "first_time_authors", queryset=Author.objects.using("other")
            )
            books = "".join(
                "%s (%s)\n"
                % (b.title, ", ".join(a.name for a in b.first_time_authors.all()))
                for b in B.prefetch_related(prefetch)
            )
        self.assertEqual(
            books,
            "Poems (Charlotte Bronte)\nSense and Sensibility (Jane Austen)\n",
        )

        
        with self.assertNumQueries(1, using="default"), self.assertNumQueries(
            1, using="other"
        ):
            prefetch = Prefetch(
                "first_time_authors", queryset=Author.objects.using("default")
            )
            books = "".join(
                "%s (%s)\n"
                % (b.title, ", ".join(a.name for a in b.first_time_authors.all()))
                for b in B.prefetch_related(prefetch)
            )
        self.assertEqual(books, "Poems ()\n" "Sense and Sensibility ()\n")


class Ticket19607Tests(TestCase):
    @classmethod
    def setUpTestData(cls):
        LessonEntry.objects.bulk_create(
            LessonEntry(id=id_, name1=name1, name2=name2)
            for id_, name1, name2 in [
                (1, "einfach", "simple"),
                (2, "schwierig", "difficult"),
            ]
        )
        WordEntry.objects.bulk_create(
            WordEntry(id=id_, lesson_entry_id=lesson_entry_id, name=name)
            for id_, lesson_entry_id, name in [
                (1, 1, "einfach"),
                (2, 1, "simple"),
                (3, 2, "schwierig"),
                (4, 2, "difficult"),
            ]
        )

    def test_bug(self):
        list(
            WordEntry.objects.prefetch_related(
                "lesson_entry", "lesson_entry__wordentry_set"
            )
        )


class Ticket21410Tests(TestCase):
    @classmethod
    def setUpTestData(cls):
        book1 = Book.objects.create(title="Poems")
        book2 = Book.objects.create(title="Jane Eyre")
        book3 = Book.objects.create(title="Wuthering Heights")
        book4 = Book.objects.create(title="Sense and Sensibility")

        author1 = Author2.objects.create(name="Charlotte", first_book=book1)
        author2 = Author2.objects.create(name="Anne", first_book=book1)
        author3 = Author2.objects.create(name="Emily", first_book=book1)
        author4 = Author2.objects.create(name="Jane", first_book=book4)

        author1.favorite_books.add(book1, book2, book3)
        author2.favorite_books.add(book1)
        author3.favorite_books.add(book2)
        author4.favorite_books.add(book3)

    def test_bug(self):
        list(Author2.objects.prefetch_related("first_book", "favorite_books"))


class Ticket21760Tests(TestCase):
    @classmethod
    def setUpTestData(cls):
        cls.rooms = []
        for _ in range(3):
            house = House.objects.create()
            for _ in range(3):
                cls.rooms.append(Room.objects.create(house=house))
            
            
            house.main_room = cls.rooms[-3]
            house.save()

    def test_bug(self):
        prefetcher = get_prefetcher(self.rooms[0], "house", "house")[0]
        queryset = prefetcher.get_prefetch_querysets(list(Room.objects.all()))[0]
        self.assertNotIn(" JOIN ", str(queryset.query))


class DirectPrefetchedObjectCacheReuseTests(TestCase):
    

    @classmethod
    def setUpTestData(cls):
        cls.book1, cls.book2 = [
            Book.objects.create(title="book1"),
            Book.objects.create(title="book2"),
        ]
        cls.author11, cls.author12, cls.author21 = [
            Author.objects.create(first_book=cls.book1, name="Author11"),
            Author.objects.create(first_book=cls.book1, name="Author12"),
            Author.objects.create(first_book=cls.book2, name="Author21"),
        ]
        cls.author1_address1, cls.author1_address2, cls.author2_address1 = [
            AuthorAddress.objects.create(author=cls.author11, address="Happy place"),
            AuthorAddress.objects.create(author=cls.author12, address="Haunted house"),
            AuthorAddress.objects.create(author=cls.author21, address="Happy place"),
        ]
        cls.bookwithyear1 = BookWithYear.objects.create(
            title="Poems", published_year=2010
        )
        cls.bookreview1 = BookReview.objects.create(book=cls.bookwithyear1)

    def test_detect_is_fetched(self):
        
        with self.assertNumQueries(3):
            books = Book.objects.filter(title__in=["book1", "book2"]).prefetch_related(
                Prefetch(
                    "first_time_authors",
                    Author.objects.prefetch_related(
                        Prefetch(
                            "addresses",
                            AuthorAddress.objects.filter(address="Happy place"),
                        )
                    ),
                ),
            )
            book1, book2 = list(books)

        with self.assertNumQueries(0):
            self.assertSequenceEqual(
                book1.first_time_authors.all(), [self.author11, self.author12]
            )
            self.assertSequenceEqual(book2.first_time_authors.all(), [self.author21])

            self.assertSequenceEqual(
                book1.first_time_authors.all()[0].addresses.all(),
                [self.author1_address1],
            )
            self.assertSequenceEqual(
                book1.first_time_authors.all()[1].addresses.all(), []
            )
            self.assertSequenceEqual(
                book2.first_time_authors.all()[0].addresses.all(),
                [self.author2_address1],
            )

        self.assertEqual(
            list(book1.first_time_authors.all()),
            list(book1.first_time_authors.all().all()),
        )
        self.assertEqual(
            list(book2.first_time_authors.all()),
            list(book2.first_time_authors.all().all()),
        )
        self.assertEqual(
            list(book1.first_time_authors.all()[0].addresses.all()),
            list(book1.first_time_authors.all()[0].addresses.all().all()),
        )
        self.assertEqual(
            list(book1.first_time_authors.all()[1].addresses.all()),
            list(book1.first_time_authors.all()[1].addresses.all().all()),
        )
        self.assertEqual(
            list(book2.first_time_authors.all()[0].addresses.all()),
            list(book2.first_time_authors.all()[0].addresses.all().all()),
        )

    def test_detect_is_fetched_with_to_attr(self):
        with self.assertNumQueries(3):
            books = Book.objects.filter(title__in=["book1", "book2"]).prefetch_related(
                Prefetch(
                    "first_time_authors",
                    Author.objects.prefetch_related(
                        Prefetch(
                            "addresses",
                            AuthorAddress.objects.filter(address="Happy place"),
                            to_attr="happy_place",
                        )
                    ),
                    to_attr="first_authors",
                ),
            )
            book1, book2 = list(books)

        with self.assertNumQueries(0):
            self.assertEqual(book1.first_authors, [self.author11, self.author12])
            self.assertEqual(book2.first_authors, [self.author21])

            self.assertEqual(
                book1.first_authors[0].happy_place, [self.author1_address1]
            )
            self.assertEqual(book1.first_authors[1].happy_place, [])
            self.assertEqual(
                book2.first_authors[0].happy_place, [self.author2_address1]
            )

    def test_prefetch_reverse_foreign_key(self):
        with self.assertNumQueries(2):
            (bookwithyear1,) = BookWithYear.objects.prefetch_related("bookreview_set")
        with self.assertNumQueries(0):
            self.assertCountEqual(
                bookwithyear1.bookreview_set.all(), [self.bookreview1]
            )
        with self.assertNumQueries(0):
            prefetch_related_objects([bookwithyear1], "bookreview_set")

    def test_add_clears_prefetched_objects(self):
        bookwithyear = BookWithYear.objects.get(pk=self.bookwithyear1.pk)
        prefetch_related_objects([bookwithyear], "bookreview_set")
        self.assertCountEqual(bookwithyear.bookreview_set.all(), [self.bookreview1])
        new_review = BookReview.objects.create()
        bookwithyear.bookreview_set.add(new_review)
        self.assertCountEqual(
            bookwithyear.bookreview_set.all(), [self.bookreview1, new_review]
        )

    def test_remove_clears_prefetched_objects(self):
        bookwithyear = BookWithYear.objects.get(pk=self.bookwithyear1.pk)
        prefetch_related_objects([bookwithyear], "bookreview_set")
        self.assertCountEqual(bookwithyear.bookreview_set.all(), [self.bookreview1])
        bookwithyear.bookreview_set.remove(self.bookreview1)
        self.assertCountEqual(bookwithyear.bookreview_set.all(), [])


class ReadPrefetchedObjectsCacheTests(TestCase):
    @classmethod
    def setUpTestData(cls):
        cls.book1 = Book.objects.create(title="Les confessions Volume I")
        cls.book2 = Book.objects.create(title="Candide")
        cls.author1 = AuthorWithAge.objects.create(
            name="Rousseau", first_book=cls.book1, age=70
        )
        cls.author2 = AuthorWithAge.objects.create(
            name="Voltaire", first_book=cls.book2, age=65
        )
        cls.book1.authors.add(cls.author1)
        cls.book2.authors.add(cls.author2)
        FavoriteAuthors.objects.create(author=cls.author1, likes_author=cls.author2)

    def test_retrieves_results_from_prefetched_objects_cache(self):
        
        authors = AuthorWithAge.objects.prefetch_related(
            Prefetch(
                "author",
                queryset=Author.objects.prefetch_related(
                    
                    
                    
                    Prefetch("favorite_authors__first_book"),
                ),
            ),
        )
        with self.assertNumQueries(4):
            
            self.assertSequenceEqual(authors, [self.author1, self.author2])


class NestedPrefetchTests(TestCase):
    @classmethod
    def setUpTestData(cls):
        house = House.objects.create(name="Big house", address="123 Main St")
        cls.room = Room.objects.create(name="Kitchen", house=house)

    def test_nested_prefetch_is_not_overwritten_by_related_object(self):
        
        queryset = House.objects.only("name").prefetch_related(
            Prefetch(
                "rooms",
                queryset=Room.objects.prefetch_related(
                    Prefetch("house", queryset=House.objects.only("address")),
                ),
            ),
        )
        with self.assertNumQueries(3):
            house = queryset.first()

        self.assertIs(Room.house.is_cached(self.room), True)
        with self.assertNumQueries(0):
            house.rooms.first().house.address


class PrefetchLimitTests(TestDataMixin, TestCase):
    @skipUnlessDBFeature("supports_over_clause")
    def test_m2m_forward(self):
        authors = Author.objects.all()  
        with self.assertNumQueries(3):
            books = list(
                Book.objects.prefetch_related(
                    Prefetch("authors", authors),
                    Prefetch("authors", authors[1:], to_attr="authors_sliced"),
                )
            )
        for book in books:
            with self.subTest(book=book):
                self.assertEqual(book.authors_sliced, list(book.authors.all())[1:])

    @skipUnlessDBFeature("supports_over_clause")
    def test_m2m_reverse(self):
        books = Book.objects.order_by("title")
        with self.assertNumQueries(3):
            authors = list(
                Author.objects.prefetch_related(
                    Prefetch("books", books),
                    Prefetch("books", books[1:2], to_attr="books_sliced"),
                )
            )
        for author in authors:
            with self.subTest(author=author):
                self.assertEqual(author.books_sliced, list(author.books.all())[1:2])

    @skipUnlessDBFeature("supports_over_clause")
    def test_foreignkey_reverse(self):
        authors = Author.objects.order_by("-name")
        with self.assertNumQueries(3):
            books = list(
                Book.objects.prefetch_related(
                    Prefetch(
                        "first_time_authors",
                        authors,
                    ),
                    Prefetch(
                        "first_time_authors",
                        authors[1:],
                        to_attr="first_time_authors_sliced",
                    ),
                )
            )
        for book in books:
            with self.subTest(book=book):
                self.assertEqual(
                    book.first_time_authors_sliced,
                    list(book.first_time_authors.all())[1:],
                )

    @skipUnlessDBFeature("supports_over_clause")
    def test_reverse_ordering(self):
        authors = Author.objects.reverse()  
        with self.assertNumQueries(3):
            books = list(
                Book.objects.prefetch_related(
                    Prefetch("authors", authors),
                    Prefetch("authors", authors[1:], to_attr="authors_sliced"),
                )
            )
        for book in books:
            with self.subTest(book=book):
                self.assertEqual(book.authors_sliced, list(book.authors.all())[1:])

    @skipIfDBFeature("supports_over_clause")
    def test_window_not_supported(self):
        authors = Author.objects.all()
        msg = (
            "Prefetching from a limited queryset is only supported on backends that "
            "support window functions."
        )
        with self.assertRaisesMessage(NotSupportedError, msg):
            list(Book.objects.prefetch_related(Prefetch("authors", authors[1:])))


class DeprecationTests(TestCase):
    def test_get_current_queryset_warning(self):
        msg = (
            "Prefetch.get_current_queryset() is deprecated. Use "
            "get_current_querysets() instead."
        )
        authors = Author.objects.all()
        with self.assertWarnsMessage(RemovedInDjango60Warning, msg):
            self.assertEqual(
                Prefetch("authors", authors).get_current_queryset(1),
                authors,
            )
        with self.assertWarnsMessage(RemovedInDjango60Warning, msg):
            self.assertIsNone(Prefetch("authors").get_current_queryset(1))

    @ignore_warnings(category=RemovedInDjango60Warning)
    def test_prefetch_one_level_fallback(self):
        class NoGetPrefetchQuerySetsDescriptor(ForwardManyToOneDescriptor):
            def get_prefetch_queryset(self, instances, queryset=None):
                if queryset is None:
                    return super().get_prefetch_querysets(instances)
                return super().get_prefetch_querysets(instances, [queryset])

            def __getattribute__(self, name):
                if name == "get_prefetch_querysets":
                    raise AttributeError
                return super().__getattribute__(name)

        house = House.objects.create()
        room = Room.objects.create(house=house)
        house.main_room = room
        house.save()

        
        prefetcher = NoGetPrefetchQuerySetsDescriptor(Room._meta.get_field("house"))
        obj_list, additional_lookups = prefetch_one_level(
            [room],
            prefetcher,
            Prefetch("house", House.objects.all()),
            0,
        )
        self.assertEqual(obj_list, [house])
        self.assertEqual(additional_lookups, [])

        obj_list, additional_lookups = prefetch_one_level(
            [room],
            prefetcher,
            Prefetch("house"),
            0,
        )
        self.assertEqual(obj_list, [house])
        self.assertEqual(additional_lookups, [])