mirror of
https://github.com/django/django.git
synced 2025-10-24 06:06:09 +00:00
@@ -500,8 +500,7 @@ class ModelState(object):
|
||||
else:
|
||||
# Force this manager to be the first and thus default
|
||||
managers_mapping[default_manager_name] = (0, models.Manager())
|
||||
# Sort all managers by their creation counter
|
||||
for _, manager, _ in sorted(model._meta.managers):
|
||||
for manager in model._meta.managers:
|
||||
if manager.name == "_base_manager" or not manager.use_in_migrations:
|
||||
continue
|
||||
reconstruct_manager(manager)
|
||||
|
||||
@@ -151,18 +151,6 @@ class ModelBase(type):
|
||||
if is_proxy and base_meta and base_meta.swapped:
|
||||
raise TypeError("%s cannot proxy the swapped model '%s'." % (name, base_meta.swapped))
|
||||
|
||||
if getattr(new_class, '_default_manager', None):
|
||||
if not is_proxy:
|
||||
# Multi-table inheritance doesn't inherit default manager from
|
||||
# parents.
|
||||
new_class._default_manager = None
|
||||
new_class._base_manager = None
|
||||
else:
|
||||
# Proxy classes do inherit parent's default manager, if none is
|
||||
# set explicitly.
|
||||
new_class._default_manager = new_class._default_manager._copy_to_model(new_class)
|
||||
new_class._base_manager = new_class._base_manager._copy_to_model(new_class)
|
||||
|
||||
# Add all attributes to the class.
|
||||
for obj_name, obj in attrs.items():
|
||||
new_class.add_to_class(obj_name, obj)
|
||||
@@ -217,7 +205,6 @@ class ModelBase(type):
|
||||
inherited_attributes = set()
|
||||
# Do the appropriate setup for any model parents.
|
||||
for base in new_class.mro():
|
||||
original_base = base
|
||||
if base not in parents or not hasattr(base, '_meta'):
|
||||
# Things without _meta aren't functional models, so they're
|
||||
# uninteresting parents.
|
||||
@@ -294,14 +281,6 @@ class ModelBase(type):
|
||||
# Pass any non-abstract parent classes onto child.
|
||||
new_class._meta.parents.update(base_parents)
|
||||
|
||||
# Inherit managers from the abstract base classes.
|
||||
new_class.copy_managers(base._meta.abstract_managers)
|
||||
|
||||
# Proxy models inherit the non-abstract managers from their base,
|
||||
# unless they have redefined any of them.
|
||||
if is_proxy:
|
||||
new_class.copy_managers(original_base._meta.concrete_managers)
|
||||
|
||||
# Inherit private fields (like GenericForeignKey) from the parent
|
||||
# class
|
||||
for field in base._meta.private_fields:
|
||||
@@ -330,15 +309,6 @@ class ModelBase(type):
|
||||
new_class._meta.apps.register_model(new_class._meta.app_label, new_class)
|
||||
return new_class
|
||||
|
||||
def copy_managers(cls, base_managers):
|
||||
# This is in-place sorting of an Options attribute, but that's fine.
|
||||
base_managers.sort()
|
||||
for _, mgr_name, manager in base_managers: # NOQA (redefinition of _)
|
||||
val = getattr(cls, mgr_name, None)
|
||||
if not val or val is manager:
|
||||
new_manager = manager._copy_to_model(cls)
|
||||
cls.add_to_class(mgr_name, new_manager)
|
||||
|
||||
def add_to_class(cls, name, value):
|
||||
# We should call the contribute_to_class method only if it's bound
|
||||
if not inspect.isclass(value) and hasattr(value, 'contribute_to_class'):
|
||||
@@ -376,6 +346,7 @@ class ModelBase(type):
|
||||
setattr(cls, 'get_absolute_url', get_absolute_url_override)
|
||||
|
||||
ensure_default_manager(cls)
|
||||
|
||||
signals.class_prepared.send(sender=cls)
|
||||
|
||||
|
||||
@@ -1263,7 +1234,7 @@ class Model(six.with_metaclass(ModelBase)):
|
||||
""" Perform all manager checks. """
|
||||
|
||||
errors = []
|
||||
for __, manager, __ in cls._meta.managers:
|
||||
for manager in cls._meta.managers:
|
||||
errors.extend(manager.check(**kwargs))
|
||||
return errors
|
||||
|
||||
|
||||
@@ -8,43 +8,40 @@ from django.utils import six
|
||||
from django.utils.encoding import python_2_unicode_compatible
|
||||
|
||||
|
||||
def ensure_default_manager(cls):
|
||||
def can_use_for_related_field(manager_class):
|
||||
return manager_class is Manager or getattr(manager_class, 'use_for_related_fields', False)
|
||||
|
||||
|
||||
def ensure_default_manager(model):
|
||||
"""
|
||||
Ensures that a Model subclass contains a default manager and sets the
|
||||
_default_manager attribute on the class. Also sets up the _base_manager
|
||||
points to a plain Manager instance (which could be the same as
|
||||
_default_manager if it's not a subclass of Manager).
|
||||
Ensures that a Model subclass contains a default manager and sets the
|
||||
_default_manager and _base_manager attributes on the class.
|
||||
"""
|
||||
if cls._meta.swapped:
|
||||
setattr(cls, 'objects', SwappedManagerDescriptor(cls))
|
||||
return
|
||||
if not getattr(cls, '_default_manager', None):
|
||||
if any(f.name == 'objects' for f in cls._meta.fields):
|
||||
|
||||
if not model._meta.managers:
|
||||
if any(f.name == 'objects' for f in model._meta.fields):
|
||||
raise ValueError(
|
||||
"Model %s must specify a custom Manager, because it has a "
|
||||
"field named 'objects'" % cls.__name__
|
||||
"field named 'objects'" % model.__name__
|
||||
)
|
||||
# Create the default manager, if needed.
|
||||
cls.add_to_class('objects', Manager())
|
||||
cls._base_manager = cls.objects
|
||||
elif not getattr(cls, '_base_manager', None):
|
||||
default_mgr = cls._default_manager.__class__
|
||||
if (default_mgr is Manager or
|
||||
getattr(default_mgr, "use_for_related_fields", False)):
|
||||
cls._base_manager = cls._default_manager
|
||||
model.add_to_class('objects', Manager())
|
||||
|
||||
model._default_manager = model._meta.managers[0]
|
||||
|
||||
# Just alias _base_manager if default manager is suitable.
|
||||
if can_use_for_related_field(model._default_manager.__class__):
|
||||
model._base_manager = model._default_manager
|
||||
|
||||
# Otherwise search for a suitable manager type in the default manager MRO.
|
||||
else:
|
||||
for base_manager_class in model._default_manager.__class__.mro()[1:]:
|
||||
if can_use_for_related_field(base_manager_class):
|
||||
model._base_manager = base_manager_class()
|
||||
model._base_manager.name = '_base_manager'
|
||||
model._base_manager.model = model
|
||||
break
|
||||
else:
|
||||
# Default manager isn't a plain Manager class, or a suitable
|
||||
# replacement, so we walk up the base class hierarchy until we hit
|
||||
# something appropriate.
|
||||
for base_class in default_mgr.mro()[1:]:
|
||||
if (base_class is Manager or
|
||||
getattr(base_class, "use_for_related_fields", False)):
|
||||
cls.add_to_class('_base_manager', base_class())
|
||||
return
|
||||
raise AssertionError(
|
||||
"Should never get here. Please report a bug, including your "
|
||||
"model and model manager setup."
|
||||
)
|
||||
raise ValueError("Could not find a suitable base manager.")
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
@@ -67,7 +64,6 @@ class BaseManager(object):
|
||||
self._set_creation_counter()
|
||||
self.model = None
|
||||
self.name = None
|
||||
self._inherited = False
|
||||
self._db = None
|
||||
self._hints = {}
|
||||
|
||||
@@ -150,26 +146,13 @@ class BaseManager(object):
|
||||
return type(class_name, (cls,), class_dict)
|
||||
|
||||
def contribute_to_class(self, model, name):
|
||||
# TODO: Use weakref because of possible memory leak / circular reference.
|
||||
self.model = model
|
||||
if not self.name:
|
||||
self.name = name
|
||||
# Only contribute the manager if the model is concrete
|
||||
if model._meta.abstract:
|
||||
setattr(model, name, AbstractManagerDescriptor(model))
|
||||
elif model._meta.swapped:
|
||||
setattr(model, name, SwappedManagerDescriptor(model))
|
||||
else:
|
||||
# if not model._meta.abstract and not model._meta.swapped:
|
||||
setattr(model, name, ManagerDescriptor(self))
|
||||
if (not getattr(model, '_default_manager', None) or
|
||||
self.creation_counter < model._default_manager.creation_counter):
|
||||
model._default_manager = self
|
||||
self.model = model
|
||||
|
||||
abstract = False
|
||||
if model._meta.abstract or (self._inherited and not self.model._meta.proxy):
|
||||
abstract = True
|
||||
model._meta.managers.append((self.creation_counter, self, abstract))
|
||||
setattr(model, name, ManagerDescriptor(self))
|
||||
|
||||
model._meta.add_manager(self)
|
||||
|
||||
def _set_creation_counter(self):
|
||||
"""
|
||||
@@ -179,19 +162,6 @@ class BaseManager(object):
|
||||
self.creation_counter = BaseManager.creation_counter
|
||||
BaseManager.creation_counter += 1
|
||||
|
||||
def _copy_to_model(self, model):
|
||||
"""
|
||||
Makes a copy of the manager and assigns it to 'model', which should be
|
||||
a child of the existing model (used when inheriting a manager from an
|
||||
abstract base class).
|
||||
"""
|
||||
assert issubclass(model, self.model)
|
||||
mgr = copy.copy(self)
|
||||
mgr._set_creation_counter()
|
||||
mgr.model = model
|
||||
mgr._inherited = True
|
||||
return mgr
|
||||
|
||||
def db_manager(self, using=None, hints=None):
|
||||
obj = copy.copy(self)
|
||||
obj._db = using or self._db
|
||||
@@ -240,43 +210,29 @@ class Manager(BaseManager.from_queryset(QuerySet)):
|
||||
|
||||
|
||||
class ManagerDescriptor(object):
|
||||
# This class ensures managers aren't accessible via model instances.
|
||||
# For example, Poll.objects works, but poll_obj.objects raises AttributeError.
|
||||
|
||||
def __init__(self, manager):
|
||||
self.manager = manager
|
||||
|
||||
def __get__(self, instance, cls=None):
|
||||
if instance is not None:
|
||||
raise AttributeError("Manager isn't accessible via %s instances" % cls.__name__)
|
||||
return self.manager
|
||||
|
||||
if cls._meta.abstract:
|
||||
raise AttributeError("Manager isn't available; %s is abstract" % (
|
||||
cls._meta.object_name,
|
||||
))
|
||||
|
||||
class AbstractManagerDescriptor(object):
|
||||
# This class provides a better error message when you try to access a
|
||||
# manager on an abstract model.
|
||||
def __init__(self, model):
|
||||
self.model = model
|
||||
|
||||
def __get__(self, instance, cls=None):
|
||||
raise AttributeError("Manager isn't available; %s is abstract" % (
|
||||
self.model._meta.object_name,
|
||||
))
|
||||
|
||||
|
||||
class SwappedManagerDescriptor(object):
|
||||
# This class provides a better error message when you try to access a
|
||||
# manager on a swapped model.
|
||||
def __init__(self, model):
|
||||
self.model = model
|
||||
|
||||
def __get__(self, instance, cls=None):
|
||||
raise AttributeError(
|
||||
"Manager isn't available; '%s.%s' has been swapped for '%s'" % (
|
||||
self.model._meta.app_label,
|
||||
self.model._meta.object_name,
|
||||
self.model._meta.swapped,
|
||||
if cls._meta.swapped:
|
||||
raise AttributeError(
|
||||
"Manager isn't available; '%s.%s' has been swapped for '%s'" % (
|
||||
cls._meta.app_label,
|
||||
cls._meta.object_name,
|
||||
cls._meta.swapped,
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
return cls._meta.managers_map[self.manager.name]
|
||||
|
||||
|
||||
class EmptyManager(Manager):
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import copy
|
||||
import warnings
|
||||
from bisect import bisect
|
||||
from collections import OrderedDict, defaultdict
|
||||
@@ -73,7 +74,8 @@ def make_immutable_fields_list(name, data):
|
||||
@python_2_unicode_compatible
|
||||
class Options(object):
|
||||
FORWARD_PROPERTIES = {'fields', 'many_to_many', 'concrete_fields',
|
||||
'local_concrete_fields', '_forward_fields_map'}
|
||||
'local_concrete_fields', '_forward_fields_map',
|
||||
'managers', 'managers_map'}
|
||||
REVERSE_PROPERTIES = {'related_objects', 'fields_map', '_relation_tree'}
|
||||
|
||||
default_apps = apps
|
||||
@@ -83,6 +85,7 @@ class Options(object):
|
||||
self.local_fields = []
|
||||
self.local_many_to_many = []
|
||||
self.private_fields = []
|
||||
self.local_managers = []
|
||||
self.model_name = None
|
||||
self.verbose_name = None
|
||||
self.verbose_name_plural = None
|
||||
@@ -122,12 +125,6 @@ class Options(object):
|
||||
self.parents = OrderedDict()
|
||||
self.auto_created = False
|
||||
|
||||
# To handle various inheritance situations, we need to track where
|
||||
# managers came from (concrete or abstract base classes). `managers`
|
||||
# keeps a list of 3-tuples of the form:
|
||||
# (creation_counter, instance, abstract(=True))
|
||||
self.managers = []
|
||||
|
||||
# List of all lookups defined in ForeignKey 'limit_choices_to' options
|
||||
# from *other* models. Needed for some admin checks. Internal use only.
|
||||
self.related_fkey_lookups = []
|
||||
@@ -154,20 +151,6 @@ class Options(object):
|
||||
def installed(self):
|
||||
return self.app_config is not None
|
||||
|
||||
@property
|
||||
def abstract_managers(self):
|
||||
return [
|
||||
(counter, instance.name, instance) for counter, instance, abstract
|
||||
in self.managers if abstract
|
||||
]
|
||||
|
||||
@property
|
||||
def concrete_managers(self):
|
||||
return [
|
||||
(counter, instance.name, instance) for counter, instance, abstract
|
||||
in self.managers if not abstract
|
||||
]
|
||||
|
||||
def contribute_to_class(self, cls, name):
|
||||
from django.db import connection
|
||||
from django.db.backends.utils import truncate_name
|
||||
@@ -264,6 +247,10 @@ class Options(object):
|
||||
auto = AutoField(verbose_name='ID', primary_key=True, auto_created=True)
|
||||
model.add_to_class('id', auto)
|
||||
|
||||
def add_manager(self, manager):
|
||||
self.local_managers.append(manager)
|
||||
self._expire_cache()
|
||||
|
||||
def add_field(self, field, private=False, virtual=NOT_PROVIDED):
|
||||
if virtual is not NOT_PROVIDED:
|
||||
warnings.warn(
|
||||
@@ -371,6 +358,25 @@ class Options(object):
|
||||
return swapped_for
|
||||
return None
|
||||
|
||||
@cached_property
|
||||
def managers(self):
|
||||
managers = []
|
||||
bases = (b for b in self.model.mro() if hasattr(b, '_meta'))
|
||||
for depth, base in enumerate(bases):
|
||||
for manager in base._meta.local_managers:
|
||||
manager = copy.copy(manager)
|
||||
manager.model = self.model
|
||||
managers.append((depth, manager.creation_counter, manager))
|
||||
|
||||
return make_immutable_fields_list(
|
||||
"managers",
|
||||
(m[2] for m in sorted(managers)),
|
||||
)
|
||||
|
||||
@cached_property
|
||||
def managers_map(self):
|
||||
return {manager.name: manager for manager in reversed(self.managers)}
|
||||
|
||||
@cached_property
|
||||
def fields(self):
|
||||
"""
|
||||
|
||||
Reference in New Issue
Block a user