1
0
mirror of https://github.com/django/django.git synced 2024-11-20 00:14:08 +00:00
django/tests/foreign_object/models/empty_join.py
Aymeric Augustin e542e81b39 Renamed descriptor classes for related objects.
The old names were downright confusing. Some seemed to mean the opposite
of what the class actually did.

The new names follow a consistent nomenclature:

    (Forward|Reverse)(ManyToOne|OneToOne|ManyToMany)Descriptor.

I mentioned combinations that do not exist in the docstring in order to
help people who would search for them in the code base.
2015-09-21 22:20:42 +02:00

101 lines
3.1 KiB
Python

from django.db import models
from django.db.models.fields.related import (
ForeignObjectRel, ReverseManyToOneDescriptor,
)
from django.db.models.lookups import StartsWith
from django.db.models.query_utils import PathInfo
from django.utils.encoding import python_2_unicode_compatible
class CustomForeignObjectRel(ForeignObjectRel):
"""
Define some extra Field methods so this Rel acts more like a Field, which
lets us use ReverseManyToOneDescriptor in both directions.
"""
@property
def foreign_related_fields(self):
return tuple(lhs_field for lhs_field, rhs_field in self.field.related_fields)
def get_attname(self):
return self.name
class StartsWithRelation(models.ForeignObject):
"""
A ForeignObject that uses StartsWith operator in its joins instead of
the default equality operator. This is logically a many-to-many relation
and creates a ReverseManyToOneDescriptor in both directions.
"""
auto_created = False
many_to_many = False
many_to_one = True
one_to_many = False
one_to_one = False
rel_class = CustomForeignObjectRel
def __init__(self, *args, **kwargs):
kwargs['on_delete'] = models.DO_NOTHING
super(StartsWithRelation, self).__init__(*args, **kwargs)
@property
def field(self):
"""
Makes ReverseManyToOneDescriptor work in both directions.
"""
return self.remote_field
def get_extra_restriction(self, where_class, alias, related_alias):
to_field = self.remote_field.model._meta.get_field(self.to_fields[0])
from_field = self.model._meta.get_field(self.from_fields[0])
return StartsWith(to_field.get_col(alias), from_field.get_col(related_alias))
def get_joining_columns(self, reverse_join=False):
return tuple()
def get_path_info(self):
to_opts = self.remote_field.model._meta
from_opts = self.model._meta
return [PathInfo(from_opts, to_opts, (to_opts.pk,), self, False, False)]
def get_reverse_path_info(self):
to_opts = self.model._meta
from_opts = self.remote_field.model._meta
return [PathInfo(from_opts, to_opts, (to_opts.pk,), self.remote_field, False, False)]
def contribute_to_class(self, cls, name, virtual_only=False):
super(StartsWithRelation, self).contribute_to_class(cls, name, virtual_only)
setattr(cls, self.name, ReverseManyToOneDescriptor(self))
class BrokenContainsRelation(StartsWithRelation):
"""
This model is designed to yield no join conditions and
raise an exception in ``Join.as_sql()``.
"""
def get_extra_restriction(self, where_class, alias, related_alias):
return None
@python_2_unicode_compatible
class SlugPage(models.Model):
slug = models.CharField(max_length=20)
descendants = StartsWithRelation(
'self',
from_fields=['slug'],
to_fields=['slug'],
related_name='ascendants',
)
containers = BrokenContainsRelation(
'self',
from_fields=['slug'],
to_fields=['slug'],
)
class Meta:
ordering = ['slug']
def __str__(self):
return 'SlugPage %s' % self.slug