mirror of
				https://github.com/django/django.git
				synced 2025-10-31 01:25:32 +00:00 
			
		
		
		
	This is a big internal change, but mostly backwards compatible with existing code. Also adds a couple of new features. Fixed #245, #1050, #1656, #1801, #2076, #2091, #2150, #2253, #2306, #2400, #2430, #2482, #2496, #2676, #2737, #2874, #2902, #2939, #3037, #3141, #3288, #3440, #3592, #3739, #4088, #4260, #4289, #4306, #4358, #4464, #4510, #4858, #5012, #5020, #5261, #5295, #5321, #5324, #5325, #5555, #5707, #5796, #5817, #5987, #6018, #6074, #6088, #6154, #6177, #6180, #6203, #6658 git-svn-id: http://code.djangoproject.com/svn/django/trunk@7477 bcc190cf-cafb-0310-a4f2-bffc1f526a37
		
			
				
	
	
		
			190 lines
		
	
	
		
			6.0 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			190 lines
		
	
	
		
			6.0 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| """
 | |
| 41. Tests for select_related()
 | |
| 
 | |
| ``select_related()`` follows all relationships and pre-caches any foreign key
 | |
| values so that complex trees can be fetched in a single query. However, this
 | |
| isn't always a good idea, so the ``depth`` argument control how many "levels"
 | |
| the select-related behavior will traverse.
 | |
| """
 | |
| 
 | |
| from django.db import models
 | |
| 
 | |
| # Who remembers high school biology?
 | |
| 
 | |
| class Domain(models.Model):
 | |
|     name = models.CharField(max_length=50)
 | |
|     def __unicode__(self):
 | |
|         return self.name
 | |
| 
 | |
| class Kingdom(models.Model):
 | |
|     name = models.CharField(max_length=50)
 | |
|     domain = models.ForeignKey(Domain)
 | |
|     def __unicode__(self):
 | |
|         return self.name
 | |
| 
 | |
| class Phylum(models.Model):
 | |
|     name = models.CharField(max_length=50)
 | |
|     kingdom = models.ForeignKey(Kingdom)
 | |
|     def __unicode__(self):
 | |
|         return self.name
 | |
| 
 | |
| class Klass(models.Model):
 | |
|     name = models.CharField(max_length=50)
 | |
|     phylum = models.ForeignKey(Phylum)
 | |
|     def __unicode__(self):
 | |
|         return self.name
 | |
| 
 | |
| class Order(models.Model):
 | |
|     name = models.CharField(max_length=50)
 | |
|     klass = models.ForeignKey(Klass)
 | |
|     def __unicode__(self):
 | |
|         return self.name
 | |
| 
 | |
| class Family(models.Model):
 | |
|     name = models.CharField(max_length=50)
 | |
|     order = models.ForeignKey(Order)
 | |
|     def __unicode__(self):
 | |
|         return self.name
 | |
| 
 | |
| class Genus(models.Model):
 | |
|     name = models.CharField(max_length=50)
 | |
|     family = models.ForeignKey(Family)
 | |
|     def __unicode__(self):
 | |
|         return self.name
 | |
| 
 | |
| class Species(models.Model):
 | |
|     name = models.CharField(max_length=50)
 | |
|     genus = models.ForeignKey(Genus)
 | |
|     def __unicode__(self):
 | |
|         return self.name
 | |
| 
 | |
| def create_tree(stringtree):
 | |
|     """Helper to create a complete tree"""
 | |
|     names = stringtree.split()
 | |
|     models = [Domain, Kingdom, Phylum, Klass, Order, Family, Genus, Species]
 | |
|     assert len(names) == len(models), (names, models)
 | |
| 
 | |
|     parent = None
 | |
|     for name, model in zip(names, models):
 | |
|         try:
 | |
|             obj = model.objects.get(name=name)
 | |
|         except model.DoesNotExist:
 | |
|             obj = model(name=name)
 | |
|         if parent:
 | |
|             setattr(obj, parent.__class__.__name__.lower(), parent)
 | |
|         obj.save()
 | |
|         parent = obj
 | |
| 
 | |
| __test__ = {'API_TESTS':"""
 | |
| 
 | |
| # Set up.
 | |
| # The test runner sets settings.DEBUG to False, but we want to gather queries
 | |
| # so we'll set it to True here and reset it at the end of the test suite.
 | |
| >>> from django.conf import settings
 | |
| >>> settings.DEBUG = True
 | |
| 
 | |
| >>> create_tree("Eukaryota Animalia Anthropoda Insecta Diptera Drosophilidae Drosophila melanogaster")
 | |
| >>> create_tree("Eukaryota Animalia Chordata Mammalia Primates Hominidae Homo sapiens")
 | |
| >>> create_tree("Eukaryota Plantae Magnoliophyta Magnoliopsida Fabales Fabaceae Pisum sativum")
 | |
| >>> create_tree("Eukaryota Fungi Basidiomycota Homobasidiomycatae Agaricales Amanitacae Amanita muscaria")
 | |
| 
 | |
| >>> from django import db
 | |
| 
 | |
| # Normally, accessing FKs doesn't fill in related objects:
 | |
| >>> db.reset_queries()
 | |
| >>> fly = Species.objects.get(name="melanogaster")
 | |
| >>> fly.genus.family.order.klass.phylum.kingdom.domain
 | |
| <Domain: Eukaryota>
 | |
| >>> len(db.connection.queries)
 | |
| 8
 | |
| 
 | |
| # However, a select_related() call will fill in those related objects without any extra queries:
 | |
| >>> db.reset_queries()
 | |
| >>> person = Species.objects.select_related(depth=10).get(name="sapiens")
 | |
| >>> person.genus.family.order.klass.phylum.kingdom.domain
 | |
| <Domain: Eukaryota>
 | |
| >>> len(db.connection.queries)
 | |
| 1
 | |
| 
 | |
| # select_related() also of course applies to entire lists, not just items.
 | |
| # Without select_related()
 | |
| >>> db.reset_queries()
 | |
| >>> world = Species.objects.all()
 | |
| >>> [o.genus.family for o in world]
 | |
| [<Family: Drosophilidae>, <Family: Hominidae>, <Family: Fabaceae>, <Family: Amanitacae>]
 | |
| >>> len(db.connection.queries)
 | |
| 9
 | |
| 
 | |
| # With select_related():
 | |
| >>> db.reset_queries()
 | |
| >>> world = Species.objects.all().select_related()
 | |
| >>> [o.genus.family for o in world]
 | |
| [<Family: Drosophilidae>, <Family: Hominidae>, <Family: Fabaceae>, <Family: Amanitacae>]
 | |
| >>> len(db.connection.queries)
 | |
| 1
 | |
| 
 | |
| # The "depth" argument to select_related() will stop the descent at a particular level:
 | |
| >>> db.reset_queries()
 | |
| >>> pea = Species.objects.select_related(depth=1).get(name="sativum")
 | |
| >>> pea.genus.family.order.klass.phylum.kingdom.domain
 | |
| <Domain: Eukaryota>
 | |
| 
 | |
| # Notice: one fewer queries than above because of depth=1
 | |
| >>> len(db.connection.queries)
 | |
| 7
 | |
| 
 | |
| >>> db.reset_queries()
 | |
| >>> pea = Species.objects.select_related(depth=5).get(name="sativum")
 | |
| >>> pea.genus.family.order.klass.phylum.kingdom.domain
 | |
| <Domain: Eukaryota>
 | |
| >>> len(db.connection.queries)
 | |
| 3
 | |
| 
 | |
| >>> db.reset_queries()
 | |
| >>> world = Species.objects.all().select_related(depth=2)
 | |
| >>> [o.genus.family.order for o in world]
 | |
| [<Order: Diptera>, <Order: Primates>, <Order: Fabales>, <Order: Agaricales>]
 | |
| >>> len(db.connection.queries)
 | |
| 5
 | |
| 
 | |
| >>> s = Species.objects.all().select_related(depth=1).extra(select={'a': 'select_related_species.id + 10'})[0]
 | |
| >>> s.id + 10 == s.a
 | |
| True
 | |
| 
 | |
| # The optional fields passed to select_related() control which related models
 | |
| # we pull in. This allows for smaller queries and can act as an alternative
 | |
| # (or, in addition to) the depth parameter.
 | |
| 
 | |
| # In the next two cases, we explicitly say to select the 'genus' and
 | |
| # 'genus.family' models, leading to the same number of queries as before.
 | |
| >>> db.reset_queries()
 | |
| >>> world = Species.objects.select_related('genus__family')
 | |
| >>> [o.genus.family for o in world]
 | |
| [<Family: Drosophilidae>, <Family: Hominidae>, <Family: Fabaceae>, <Family: Amanitacae>]
 | |
| >>> len(db.connection.queries)
 | |
| 1
 | |
| 
 | |
| >>> db.reset_queries()
 | |
| >>> world = Species.objects.filter(genus__name='Amanita').select_related('genus__family')
 | |
| >>> [o.genus.family.order for o in world]
 | |
| [<Order: Agaricales>]
 | |
| >>> len(db.connection.queries)
 | |
| 2
 | |
| 
 | |
| >>> db.reset_queries()
 | |
| >>> Species.objects.all().select_related('genus__family__order').order_by('id')[0:1].get().genus.family.order.name
 | |
| u'Diptera'
 | |
| >>> len(db.connection.queries)
 | |
| 1
 | |
| 
 | |
| # Specifying both "depth" and fields is an error.
 | |
| >>> Species.objects.select_related('genus__family__order', depth=4)
 | |
| Traceback (most recent call last):
 | |
| ...
 | |
| TypeError: Cannot pass both "depth" and fields to select_related()
 | |
| 
 | |
| # Reset DEBUG to where we found it.
 | |
| >>> settings.DEBUG = False
 | |
| """}
 | |
| 
 |