Skip to content

Commit

Permalink
Wip
Browse files Browse the repository at this point in the history
  • Loading branch information
charettes committed Nov 18, 2018
1 parent 0cf85e6 commit ccc58bf
Show file tree
Hide file tree
Showing 2 changed files with 66 additions and 24 deletions.
2 changes: 1 addition & 1 deletion django/db/models/fields/related.py
Original file line number Diff line number Diff line change
Expand Up @@ -725,7 +725,7 @@ def get_reverse_path_info(self, filtered_relation=None):

@classmethod
@functools.lru_cache(maxsize=None)
def get_lookups(cls):
def get_class_lookups(cls):
bases = inspect.getmro(cls)
bases = bases[:bases.index(ForeignObject) + 1]
class_lookups = [parent.__dict__.get('class_lookups', {}) for parent in bases]
Expand Down
88 changes: 65 additions & 23 deletions django/db/models/query_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

from django.db.models.constants import LOOKUP_SEP
from django.utils import tree
from django.utils.functional import cached_property

# PathInfo is used when converting lookups (fk__somecol). The contents
# describe the relation in Model terms (model Options and Fields for both
Expand Down Expand Up @@ -151,18 +152,48 @@ def _check_parent_chain(self, instance, name):
return None


class classorinstancemethod(object):
def __init__(self, class_method, instance_method):
self.class_method = class_method
self.instance_method = instance_method

def __get__(self, instance, owner):
if instance is None:
return functools.partial(self.class_method, owner)
return functools.partial(self.instance_method, self=instance)


class RegisterLookupMixin:

@classmethod
def _get_lookup(cls, lookup_name):
return cls.get_lookups().get(lookup_name, None)
@staticmethod
def merge_dicts(dicts):
"""
Merge dicts in reverse to preference the order of the original list. e.g.,
merge_dicts([a, b]) will preference the keys in 'a' over those in 'b'.
"""
merged = {}
for d in reversed(dicts):
merged.update(d)
return merged

@classmethod
@functools.lru_cache(maxsize=None)
def get_lookups(cls):
def get_class_lookups(cls):
class_lookups = [parent.__dict__.get('class_lookups', {}) for parent in inspect.getmro(cls)]
return cls.merge_dicts(class_lookups)

def get_lookups(self):
lookups = self.get_class_lookups()
instance_lookups = getattr(self, 'instance_lookups', None)
if instance_lookups:
lookups = {**lookups, **instance_lookups}
return lookups

get_lookups = classorinstancemethod(get_class_lookups, get_lookups)
get_class_lookups = classmethod(get_class_lookups)

def _get_lookup(self, lookup_name):
return self.get_lookups().get(lookup_name, None)

def get_lookup(self, lookup_name):
from django.db.models.lookups import Lookup
found = self._get_lookup(lookup_name)
Expand All @@ -181,41 +212,52 @@ def get_transform(self, lookup_name):
return None
return found

@staticmethod
def merge_dicts(dicts):
"""
Merge dicts in reverse to preference the order of the original list. e.g.,
merge_dicts([a, b]) will preference the keys in 'a' over those in 'b'.
"""
merged = {}
for d in reversed(dicts):
merged.update(d)
return merged

@classmethod
def _clear_cached_lookups(cls):
def _clear_cached_class_lookups(cls):
for subclass in subclasses(cls):
subclass.get_lookups.cache_clear()
subclass.get_class_lookups.cache_clear()

@classmethod
def register_lookup(cls, lookup, lookup_name=None):
def register_class_lookup(cls, lookup, lookup_name=None):
if lookup_name is None:
lookup_name = lookup.lookup_name
if 'class_lookups' not in cls.__dict__:
cls.class_lookups = {}
cls.class_lookups[lookup_name] = lookup
cls._clear_cached_lookups()
cls._clear_cached_class_lookups()
return lookup

@classmethod
def _unregister_lookup(cls, lookup, lookup_name=None):
def register_lookup(self, lookup, lookup_name=None):
if lookup_name is None:
lookup_name = lookup.lookup_name
if 'instance_lookups' not in self.__dict__:
self.instance_lookups = {}
self.instance_lookups[lookup_name] = lookup
return lookup

register_lookup = classorinstancemethod(register_class_lookup, register_lookup)
register_class_lookup = classmethod(register_class_lookup)

def _unregister_class_lookup(cls, lookup, lookup_name=None):
"""
Remove given lookup from cls lookups. For use in tests only as it's
not thread-safe.
"""
if lookup_name is None:
lookup_name = lookup.lookup_name
del cls.class_lookups[lookup_name]
cls._clear_cached_class_lookups()

def _unregister_lookup(self, lookup, lookup_name=None):
"""
Remove given lookup from instance lookups. For use in tests only as
it's not thread-safe.
"""
if lookup_name is None:
lookup_name = lookup.lookup_name
del self.instance_lookups[lookup_name]

_unregister_lookup = classorinstancemethod(_unregister_class_lookup, _unregister_lookup)
_unregister_class_lookup = classmethod(_unregister_class_lookup)


def select_related_descend(field, restricted, requested, load_fields, reverse=False):
Expand Down

0 comments on commit ccc58bf

Please sign in to comment.