mirror of
				https://github.com/django/django.git
				synced 2025-10-31 09:41:08 +00:00 
			
		
		
		
	Large portions of this are needed for #5420, so I implemented it fully. Thanks to Ryan Kelly for an initial patch to get this started. Refs #5420. git-svn-id: http://code.djangoproject.com/svn/django/trunk@10083 bcc190cf-cafb-0310-a4f2-bffc1f526a37
		
			
				
	
	
		
			177 lines
		
	
	
		
			4.8 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			177 lines
		
	
	
		
			4.8 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| """
 | |
| By specifying the 'proxy' Meta attribute, model subclasses can specify that
 | |
| they will take data directly from the table of their base class table rather
 | |
| than using a new table of their own. This allows them to act as simple proxies,
 | |
| providing a modified interface to the data from the base class.
 | |
| """
 | |
| 
 | |
| from django.db import models
 | |
| 
 | |
| 
 | |
| # A couple of managers for testing managing overriding in proxy model cases.
 | |
| 
 | |
| class PersonManager(models.Manager):
 | |
|     def get_query_set(self):
 | |
|         return super(PersonManager, self).get_query_set().exclude(name="fred")
 | |
| 
 | |
| class SubManager(models.Manager):
 | |
|     def get_query_set(self):
 | |
|         return super(SubManager, self).get_query_set().exclude(name="wilma")
 | |
| 
 | |
| class Person(models.Model):
 | |
|     """
 | |
|     A simple concrete base class.
 | |
|     """
 | |
|     name = models.CharField(max_length=50)
 | |
| 
 | |
|     objects = PersonManager()
 | |
| 
 | |
|     def __unicode__(self):
 | |
|         return self.name
 | |
| 
 | |
| class Abstract(models.Model):
 | |
|     """
 | |
|     A simple abstract base class, to be used for error checking.
 | |
|     """
 | |
|     data = models.CharField(max_length=10)
 | |
| 
 | |
|     class Meta:
 | |
|         abstract = True
 | |
| 
 | |
| class MyPerson(Person):
 | |
|     """
 | |
|     A proxy subclass, this should not get a new table. Overrides the default
 | |
|     manager.
 | |
|     """
 | |
|     class Meta:
 | |
|         proxy = True
 | |
|         ordering = ["name"]
 | |
| 
 | |
|     objects = SubManager()
 | |
|     other = PersonManager()
 | |
| 
 | |
|     def has_special_name(self):
 | |
|         return self.name.lower() == "special"
 | |
| 
 | |
| class ManagerMixin(models.Model):
 | |
|     excluder = SubManager()
 | |
| 
 | |
|     class Meta:
 | |
|         abstract = True
 | |
| 
 | |
| class OtherPerson(Person, ManagerMixin):
 | |
|     """
 | |
|     A class with the default manager from Person, plus an secondary manager.
 | |
|     """
 | |
|     class Meta:
 | |
|         proxy = True
 | |
|         ordering = ["name"]
 | |
| 
 | |
| class StatusPerson(MyPerson):
 | |
|     """
 | |
|     A non-proxy subclass of a proxy, it should get a new table.
 | |
|     """
 | |
|     status = models.CharField(max_length=80)
 | |
| 
 | |
| # We can even have proxies of proxies (and subclass of those).
 | |
| class MyPersonProxy(MyPerson):
 | |
|     class Meta:
 | |
|         proxy = True
 | |
| 
 | |
| class LowerStatusPerson(MyPersonProxy):
 | |
|     status = models.CharField(max_length=80)
 | |
| 
 | |
| __test__ = {'API_TESTS' : """
 | |
| # The MyPerson model should be generating the same database queries as the
 | |
| # Person model (when the same manager is used in each case).
 | |
| >>> MyPerson.other.all().query.as_sql() == Person.objects.order_by("name").query.as_sql()
 | |
| True
 | |
| 
 | |
| # The StatusPerson models should have its own table (it's using ORM-level
 | |
| # inheritance).
 | |
| >>> StatusPerson.objects.all().query.as_sql() == Person.objects.all().query.as_sql()
 | |
| False
 | |
| 
 | |
| # Creating a Person makes them accessible through the MyPerson proxy.
 | |
| >>> _ = Person.objects.create(name="Foo McBar")
 | |
| >>> len(Person.objects.all())
 | |
| 1
 | |
| >>> len(MyPerson.objects.all())
 | |
| 1
 | |
| >>> MyPerson.objects.get(name="Foo McBar").id
 | |
| 1
 | |
| >>> MyPerson.objects.get(id=1).has_special_name()
 | |
| False
 | |
| 
 | |
| # Person is not proxied by StatusPerson subclass, however.
 | |
| >>> StatusPerson.objects.all()
 | |
| []
 | |
| 
 | |
| # A new MyPerson also shows up as a standard Person
 | |
| >>> _ = MyPerson.objects.create(name="Bazza del Frob")
 | |
| >>> len(MyPerson.objects.all())
 | |
| 2
 | |
| >>> len(Person.objects.all())
 | |
| 2
 | |
| 
 | |
| >>> _ = LowerStatusPerson.objects.create(status="low", name="homer")
 | |
| >>> LowerStatusPerson.objects.all()
 | |
| [<LowerStatusPerson: homer>]
 | |
| 
 | |
| # And now for some things that shouldn't work...
 | |
| #
 | |
| # All base classes must be non-abstract
 | |
| >>> class NoAbstract(Abstract):
 | |
| ...     class Meta:
 | |
| ...         proxy = True
 | |
| Traceback (most recent call last):
 | |
|     ....
 | |
| TypeError: Abstract base class containing model fields not permitted for proxy model 'NoAbstract'.
 | |
| 
 | |
| # The proxy must actually have one concrete base class
 | |
| >>> class TooManyBases(Person, Abstract):
 | |
| ...     class Meta:
 | |
| ...         proxy = True
 | |
| Traceback (most recent call last):
 | |
|     ....
 | |
| TypeError: Abstract base class containing model fields not permitted for proxy model 'TooManyBases'.
 | |
| 
 | |
| >>> class NoBaseClasses(models.Model):
 | |
| ...     class Meta:
 | |
| ...         proxy = True
 | |
| Traceback (most recent call last):
 | |
|     ....
 | |
| TypeError: Proxy model 'NoBaseClasses' has no non-abstract model base class.
 | |
| 
 | |
| 
 | |
| # A proxy cannot introduce any new fields
 | |
| >>> class NoNewFields(Person):
 | |
| ...     newfield = models.BooleanField()
 | |
| ...     class Meta:
 | |
| ...         proxy = True
 | |
| Traceback (most recent call last):
 | |
|     ....
 | |
| FieldError: Proxy model 'NoNewFields' contains model fields.
 | |
| 
 | |
| # Manager tests.
 | |
| 
 | |
| >>> Person.objects.all().delete()
 | |
| >>> _ = Person.objects.create(name="fred")
 | |
| >>> _ = Person.objects.create(name="wilma")
 | |
| >>> _ = Person.objects.create(name="barney")
 | |
| 
 | |
| >>> MyPerson.objects.all()
 | |
| [<MyPerson: barney>, <MyPerson: fred>]
 | |
| >>> MyPerson._default_manager.all()
 | |
| [<MyPerson: barney>, <MyPerson: fred>]
 | |
| 
 | |
| >>> OtherPerson.objects.all()
 | |
| [<OtherPerson: barney>, <OtherPerson: wilma>]
 | |
| >>> OtherPerson.excluder.all()
 | |
| [<OtherPerson: barney>, <OtherPerson: fred>]
 | |
| >>> OtherPerson._default_manager.all()
 | |
| [<OtherPerson: barney>, <OtherPerson: wilma>]
 | |
| """}
 | |
| 
 | |
| 
 |