@@ -196,6 +196,90 @@ def test_model_foreign_key_recursive_parent():
196196 assert expected == actual
197197
198198
199+ @pytest .mark .django_db
200+ def test_model_many_to_many (django_assert_num_queries ):
201+ flavor_one = Flavor (name = "name1" , label = "label1" )
202+ flavor_one .save ()
203+
204+ taste1 = Taste (name = "Bitter1" )
205+ taste1 .save ()
206+ taste2 = Taste (name = "Bitter2" )
207+ taste2 .save ()
208+ taste3 = Taste (name = "Bitter3" )
209+ taste3 .save ()
210+
211+ flavor_one .taste_set .add (taste1 )
212+ flavor_one .taste_set .add (taste2 )
213+ flavor_one .taste_set .add (taste3 )
214+
215+ with django_assert_num_queries (2 ):
216+ actual = serializer .dumps (flavor_one )
217+
218+ expected = {
219+ "name" : "name1" ,
220+ "label" : "label1" ,
221+ "parent" : None ,
222+ "float_value" : None ,
223+ "decimal_value" : None ,
224+ "uuid" : str (flavor_one .uuid ),
225+ "datetime" : None ,
226+ "date" : None ,
227+ "time" : None ,
228+ "duration" : None ,
229+ "pk" : 1 ,
230+ "taste_set" : [taste1 .pk , taste2 .pk , taste3 .pk ],
231+ "origins" : [],
232+ }
233+
234+ assert expected == json .loads (actual )
235+
236+
237+ @pytest .mark .django_db
238+ def test_model_many_to_many_with_excludes (django_assert_num_queries ):
239+ flavor_one = Flavor (name = "name1" , label = "label1" )
240+ flavor_one .save ()
241+
242+ taste1 = Taste (name = "Bitter1" )
243+ taste1 .save ()
244+ taste2 = Taste (name = "Bitter2" )
245+ taste2 .save ()
246+ taste3 = Taste (name = "Bitter3" )
247+ taste3 .save ()
248+
249+ flavor_one .taste_set .add (taste1 )
250+ flavor_one .taste_set .add (taste2 )
251+ flavor_one .taste_set .add (taste3 )
252+
253+ # This shouldn't make any database calls because the many-to-manys are excluded and
254+ # all of the other data is already set
255+ with django_assert_num_queries (0 ):
256+ actual = serializer .dumps (
257+ {"flavor" : flavor_one },
258+ exclude_field_attributes = (
259+ "flavor.taste_set" ,
260+ "flavor.origins" ,
261+ ),
262+ )
263+
264+ expected = {
265+ "flavor" : {
266+ "name" : "name1" ,
267+ "label" : "label1" ,
268+ "parent" : None ,
269+ "float_value" : None ,
270+ "decimal_value" : None ,
271+ "uuid" : str (flavor_one .uuid ),
272+ "datetime" : None ,
273+ "date" : None ,
274+ "time" : None ,
275+ "duration" : None ,
276+ "pk" : 1 ,
277+ }
278+ }
279+
280+ assert expected == json .loads (actual )
281+
282+
199283@pytest .mark .django_db
200284def test_dumps_queryset (db ):
201285 flavor_one = Flavor (name = "name1" , label = "label1" )
@@ -269,13 +353,62 @@ def test_get_model_dict():
269353
270354
271355@pytest .mark .django_db
272- def test_get_model_dict_many_to_many_is_referenced ():
273- taste = Taste (name = "Bitter" )
274- taste .save ()
356+ def test_get_model_dict_many_to_many_is_referenced (django_assert_num_queries ):
275357 flavor_one = Flavor (name = "name1" , label = "label1" )
276358 flavor_one .save ()
277- flavor_one .taste_set .add (taste )
278- actual = serializer ._get_model_dict (flavor_one )
359+
360+ taste1 = Taste (name = "Bitter" )
361+ taste1 .save ()
362+ taste2 = Taste (name = "Bitter2" )
363+ taste2 .save ()
364+ taste3 = Taste (name = "Bitter3" )
365+ taste3 .save ()
366+
367+ flavor_one .taste_set .add (taste1 )
368+ flavor_one .taste_set .add (taste2 )
369+ flavor_one .taste_set .add (taste3 )
370+
371+ expected = {
372+ "pk" : 1 ,
373+ "name" : "name1" ,
374+ "label" : "label1" ,
375+ "parent" : None ,
376+ "decimal_value" : None ,
377+ "float_value" : None ,
378+ "uuid" : str (flavor_one .uuid ),
379+ "date" : None ,
380+ "datetime" : None ,
381+ "time" : None ,
382+ "duration" : None ,
383+ "taste_set" : [taste1 .pk , taste2 .pk , taste3 .pk ],
384+ "origins" : [],
385+ }
386+
387+ flavor_one = Flavor .objects .filter (id = flavor_one .id ).first ()
388+
389+ with django_assert_num_queries (2 ):
390+ actual = serializer ._get_model_dict (flavor_one )
391+
392+ assert expected == actual
393+
394+
395+ @pytest .mark .django_db
396+ def test_get_model_dict_many_to_many_is_referenced_prefetched (
397+ django_assert_num_queries ,
398+ ):
399+ flavor_one = Flavor (name = "name1" , label = "label1" )
400+ flavor_one .save ()
401+
402+ taste1 = Taste (name = "Bitter" )
403+ taste1 .save ()
404+ taste2 = Taste (name = "Bitter2" )
405+ taste2 .save ()
406+ taste3 = Taste (name = "Bitter3" )
407+ taste3 .save ()
408+
409+ flavor_one .taste_set .add (taste1 )
410+ flavor_one .taste_set .add (taste2 )
411+ flavor_one .taste_set .add (taste3 )
279412
280413 expected = {
281414 "pk" : 1 ,
@@ -289,10 +422,18 @@ def test_get_model_dict_many_to_many_is_referenced():
289422 "datetime" : None ,
290423 "time" : None ,
291424 "duration" : None ,
292- "taste_set" : [taste .pk ],
425+ "taste_set" : [taste1 . pk , taste2 . pk , taste3 .pk ],
293426 "origins" : [],
294427 }
295428
429+ flavor_one = (
430+ Flavor .objects .prefetch_related ("taste_set" ).filter (id = flavor_one .id ).first ()
431+ )
432+
433+ # prefetch_related should reduce the database calls
434+ with django_assert_num_queries (1 ):
435+ actual = serializer ._get_model_dict (flavor_one )
436+
296437 assert expected == actual
297438
298439
0 commit comments