Removed Manager.use_for_related_fields and Meta.manager_inheritance_from_future.
Per deprecation timeline. Refs ed0ff913c6
.
This commit is contained in:
parent
60ca37d2e5
commit
631f4ab061
|
@ -1,7 +1,6 @@
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
import copy
|
import copy
|
||||||
import warnings
|
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
from contextlib import contextmanager
|
from contextlib import contextmanager
|
||||||
|
|
||||||
|
@ -14,7 +13,6 @@ from django.db.models.fields.related import RECURSIVE_RELATIONSHIP_CONSTANT
|
||||||
from django.db.models.options import DEFAULT_NAMES, normalize_together
|
from django.db.models.options import DEFAULT_NAMES, normalize_together
|
||||||
from django.db.models.utils import make_model_tuple
|
from django.db.models.utils import make_model_tuple
|
||||||
from django.utils import six
|
from django.utils import six
|
||||||
from django.utils.deprecation import RemovedInDjango20Warning
|
|
||||||
from django.utils.encoding import force_text
|
from django.utils.encoding import force_text
|
||||||
from django.utils.functional import cached_property
|
from django.utils.functional import cached_property
|
||||||
from django.utils.module_loading import import_string
|
from django.utils.module_loading import import_string
|
||||||
|
@ -604,18 +602,8 @@ class ModelState(object):
|
||||||
|
|
||||||
# Restore managers
|
# Restore managers
|
||||||
body.update(self.construct_managers())
|
body.update(self.construct_managers())
|
||||||
|
# Then, make a Model object (apps.register_model is called in __new__)
|
||||||
with warnings.catch_warnings():
|
return type(str(self.name), bases, body)
|
||||||
warnings.filterwarnings(
|
|
||||||
"ignore", "Managers from concrete parents will soon qualify as default managers",
|
|
||||||
RemovedInDjango20Warning)
|
|
||||||
|
|
||||||
# Then, make a Model object (apps.register_model is called in __new__)
|
|
||||||
return type(
|
|
||||||
str(self.name),
|
|
||||||
bases,
|
|
||||||
body,
|
|
||||||
)
|
|
||||||
|
|
||||||
def get_field_by_name(self, name):
|
def get_field_by_name(self, name):
|
||||||
for fname, field in self.fields:
|
for fname, field in self.fields:
|
||||||
|
|
|
@ -29,7 +29,6 @@ from django.db.models.signals import (
|
||||||
)
|
)
|
||||||
from django.db.models.utils import make_model_tuple
|
from django.db.models.utils import make_model_tuple
|
||||||
from django.utils import six
|
from django.utils import six
|
||||||
from django.utils.deprecation import RemovedInDjango20Warning
|
|
||||||
from django.utils.encoding import (
|
from django.utils.encoding import (
|
||||||
force_str, force_text, python_2_unicode_compatible,
|
force_str, force_text, python_2_unicode_compatible,
|
||||||
)
|
)
|
||||||
|
@ -358,7 +357,7 @@ class ModelBase(type):
|
||||||
if get_absolute_url_override:
|
if get_absolute_url_override:
|
||||||
setattr(cls, 'get_absolute_url', get_absolute_url_override)
|
setattr(cls, 'get_absolute_url', get_absolute_url_override)
|
||||||
|
|
||||||
if not opts.managers or cls._requires_legacy_default_manager():
|
if not opts.managers:
|
||||||
if any(f.name == 'objects' for f in opts.fields):
|
if any(f.name == 'objects' for f in opts.fields):
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
"Model %s must specify a custom Manager, because it has a "
|
"Model %s must specify a custom Manager, because it has a "
|
||||||
|
@ -370,79 +369,6 @@ class ModelBase(type):
|
||||||
|
|
||||||
class_prepared.send(sender=cls)
|
class_prepared.send(sender=cls)
|
||||||
|
|
||||||
def _requires_legacy_default_manager(cls): # RemovedInDjango20Warning
|
|
||||||
opts = cls._meta
|
|
||||||
|
|
||||||
if opts.manager_inheritance_from_future:
|
|
||||||
return False
|
|
||||||
|
|
||||||
future_default_manager = opts.default_manager
|
|
||||||
|
|
||||||
# Step 1: Locate a manager that would have been promoted
|
|
||||||
# to default manager with the legacy system.
|
|
||||||
for manager in opts.managers:
|
|
||||||
originating_model = manager._originating_model
|
|
||||||
if (cls is originating_model or cls._meta.proxy or
|
|
||||||
originating_model._meta.abstract):
|
|
||||||
|
|
||||||
if manager is not cls._default_manager and not opts.default_manager_name:
|
|
||||||
warnings.warn(
|
|
||||||
"Managers from concrete parents will soon qualify as default "
|
|
||||||
"managers if they appear before any other managers in the "
|
|
||||||
"MRO. As a result, '{legacy_default_manager}' declared on "
|
|
||||||
"'{legacy_default_manager_model}' will no longer be the "
|
|
||||||
"default manager for '{model}' in favor of "
|
|
||||||
"'{future_default_manager}' declared on "
|
|
||||||
"'{future_default_manager_model}'. "
|
|
||||||
"You can redeclare '{legacy_default_manager}' on '{cls}' "
|
|
||||||
"to keep things the way they are or you can switch to the new "
|
|
||||||
"behavior right away by setting "
|
|
||||||
"`Meta.manager_inheritance_from_future` to `True`.".format(
|
|
||||||
cls=cls.__name__,
|
|
||||||
model=opts.label,
|
|
||||||
legacy_default_manager=manager.name,
|
|
||||||
legacy_default_manager_model=manager._originating_model._meta.label,
|
|
||||||
future_default_manager=future_default_manager.name,
|
|
||||||
future_default_manager_model=future_default_manager._originating_model._meta.label,
|
|
||||||
),
|
|
||||||
RemovedInDjango20Warning, 2
|
|
||||||
)
|
|
||||||
|
|
||||||
opts.default_manager_name = manager.name
|
|
||||||
opts._expire_cache()
|
|
||||||
|
|
||||||
break
|
|
||||||
|
|
||||||
# Step 2: Since there are managers but none of them qualified as
|
|
||||||
# default managers under the legacy system (meaning that there are
|
|
||||||
# managers from concrete parents that would be promoted under the
|
|
||||||
# new system), we need to create a new Manager instance for the
|
|
||||||
# 'objects' attribute as a deprecation shim.
|
|
||||||
else:
|
|
||||||
# If the "future" default manager was auto created there is no
|
|
||||||
# point warning the user since it's basically the same manager.
|
|
||||||
if not future_default_manager.auto_created:
|
|
||||||
warnings.warn(
|
|
||||||
"Managers from concrete parents will soon qualify as "
|
|
||||||
"default managers. As a result, the 'objects' manager "
|
|
||||||
"won't be created (or recreated) automatically "
|
|
||||||
"anymore on '{model}' and '{future_default_manager}' "
|
|
||||||
"declared on '{future_default_manager_model}' will be "
|
|
||||||
"promoted to default manager. You can declare "
|
|
||||||
"explicitly `objects = models.Manager()` on '{cls}' "
|
|
||||||
"to keep things the way they are or you can switch "
|
|
||||||
"to the new behavior right away by setting "
|
|
||||||
"`Meta.manager_inheritance_from_future` to `True`.".format(
|
|
||||||
cls=cls.__name__,
|
|
||||||
model=opts.label,
|
|
||||||
future_default_manager=future_default_manager.name,
|
|
||||||
future_default_manager_model=future_default_manager._originating_model._meta.label,
|
|
||||||
),
|
|
||||||
RemovedInDjango20Warning, 2
|
|
||||||
)
|
|
||||||
|
|
||||||
return True
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def _base_manager(cls):
|
def _base_manager(cls):
|
||||||
return cls._meta.base_manager
|
return cls._meta.base_manager
|
||||||
|
|
|
@ -65,13 +65,11 @@ and two directions (forward and reverse) for a total of six combinations.
|
||||||
|
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
import warnings
|
|
||||||
from operator import attrgetter
|
from operator import attrgetter
|
||||||
|
|
||||||
from django.db import connections, router, transaction
|
from django.db import connections, router, transaction
|
||||||
from django.db.models import Q, signals
|
from django.db.models import Q, signals
|
||||||
from django.db.models.query import QuerySet
|
from django.db.models.query import QuerySet
|
||||||
from django.utils.deprecation import RemovedInDjango20Warning
|
|
||||||
from django.utils.functional import cached_property
|
from django.utils.functional import cached_property
|
||||||
|
|
||||||
|
|
||||||
|
@ -107,20 +105,7 @@ class ForwardManyToOneDescriptor(object):
|
||||||
return hasattr(instance, self.cache_name)
|
return hasattr(instance, self.cache_name)
|
||||||
|
|
||||||
def get_queryset(self, **hints):
|
def get_queryset(self, **hints):
|
||||||
related_model = self.field.remote_field.model
|
return self.field.remote_field.model._base_manager.db_manager(hints=hints).all()
|
||||||
|
|
||||||
if getattr(related_model._default_manager, 'use_for_related_fields', False):
|
|
||||||
if not getattr(related_model._default_manager, 'silence_use_for_related_fields_deprecation', False):
|
|
||||||
warnings.warn(
|
|
||||||
"use_for_related_fields is deprecated, instead "
|
|
||||||
"set Meta.base_manager_name on '{}'.".format(related_model._meta.label),
|
|
||||||
RemovedInDjango20Warning, 2
|
|
||||||
)
|
|
||||||
manager = related_model._default_manager
|
|
||||||
else:
|
|
||||||
manager = related_model._base_manager
|
|
||||||
|
|
||||||
return manager.db_manager(hints=hints).all()
|
|
||||||
|
|
||||||
def get_prefetch_queryset(self, instances, queryset=None):
|
def get_prefetch_queryset(self, instances, queryset=None):
|
||||||
if queryset is None:
|
if queryset is None:
|
||||||
|
@ -323,20 +308,7 @@ class ReverseOneToOneDescriptor(object):
|
||||||
return hasattr(instance, self.cache_name)
|
return hasattr(instance, self.cache_name)
|
||||||
|
|
||||||
def get_queryset(self, **hints):
|
def get_queryset(self, **hints):
|
||||||
related_model = self.related.related_model
|
return self.related.related_model._base_manager.db_manager(hints=hints).all()
|
||||||
|
|
||||||
if getattr(related_model._default_manager, 'use_for_related_fields', False):
|
|
||||||
if not getattr(related_model._default_manager, 'silence_use_for_related_fields_deprecation', False):
|
|
||||||
warnings.warn(
|
|
||||||
"use_for_related_fields is deprecated, instead "
|
|
||||||
"set Meta.base_manager_name on '{}'.".format(related_model._meta.label),
|
|
||||||
RemovedInDjango20Warning, 2
|
|
||||||
)
|
|
||||||
manager = related_model._default_manager
|
|
||||||
else:
|
|
||||||
manager = related_model._base_manager
|
|
||||||
|
|
||||||
return manager.db_manager(hints=hints).all()
|
|
||||||
|
|
||||||
def get_prefetch_queryset(self, instances, queryset=None):
|
def get_prefetch_queryset(self, instances, queryset=None):
|
||||||
if queryset is None:
|
if queryset is None:
|
||||||
|
|
|
@ -17,9 +17,7 @@ from django.db.models.fields.related import OneToOneField
|
||||||
from django.db.models.query_utils import PathInfo
|
from django.db.models.query_utils import PathInfo
|
||||||
from django.utils import six
|
from django.utils import six
|
||||||
from django.utils.datastructures import ImmutableList, OrderedSet
|
from django.utils.datastructures import ImmutableList, OrderedSet
|
||||||
from django.utils.deprecation import (
|
from django.utils.deprecation import RemovedInDjango21Warning
|
||||||
RemovedInDjango20Warning, RemovedInDjango21Warning,
|
|
||||||
)
|
|
||||||
from django.utils.encoding import force_text, python_2_unicode_compatible
|
from django.utils.encoding import force_text, python_2_unicode_compatible
|
||||||
from django.utils.functional import cached_property
|
from django.utils.functional import cached_property
|
||||||
from django.utils.text import camel_case_to_spaces, format_lazy
|
from django.utils.text import camel_case_to_spaces, format_lazy
|
||||||
|
@ -41,7 +39,7 @@ DEFAULT_NAMES = (
|
||||||
'auto_created', 'index_together', 'apps', 'default_permissions',
|
'auto_created', 'index_together', 'apps', 'default_permissions',
|
||||||
'select_on_save', 'default_related_name', 'required_db_features',
|
'select_on_save', 'default_related_name', 'required_db_features',
|
||||||
'required_db_vendor', 'base_manager_name', 'default_manager_name',
|
'required_db_vendor', 'base_manager_name', 'default_manager_name',
|
||||||
'manager_inheritance_from_future', 'indexes',
|
'indexes',
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -87,7 +85,6 @@ class Options(object):
|
||||||
self.local_fields = []
|
self.local_fields = []
|
||||||
self.local_many_to_many = []
|
self.local_many_to_many = []
|
||||||
self.private_fields = []
|
self.private_fields = []
|
||||||
self.manager_inheritance_from_future = False
|
|
||||||
self.local_managers = []
|
self.local_managers = []
|
||||||
self.base_manager_name = None
|
self.base_manager_name = None
|
||||||
self.default_manager_name = None
|
self.default_manager_name = None
|
||||||
|
@ -375,10 +372,6 @@ class Options(object):
|
||||||
seen_managers.add(manager.name)
|
seen_managers.add(manager.name)
|
||||||
managers.append((depth, manager.creation_counter, manager))
|
managers.append((depth, manager.creation_counter, manager))
|
||||||
|
|
||||||
# Used for deprecation of legacy manager inheritance,
|
|
||||||
# remove afterwards. (RemovedInDjango20Warning)
|
|
||||||
manager._originating_model = base
|
|
||||||
|
|
||||||
return make_immutable_fields_list(
|
return make_immutable_fields_list(
|
||||||
"managers",
|
"managers",
|
||||||
(m[2] for m in sorted(managers)),
|
(m[2] for m in sorted(managers)),
|
||||||
|
@ -410,25 +403,6 @@ class Options(object):
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
# Deprecation shim for `use_for_related_fields`.
|
|
||||||
for i, base_manager_class in enumerate(self.default_manager.__class__.mro()):
|
|
||||||
if getattr(base_manager_class, 'use_for_related_fields', False):
|
|
||||||
if not getattr(base_manager_class, 'silence_use_for_related_fields_deprecation', False):
|
|
||||||
warnings.warn(
|
|
||||||
"use_for_related_fields is deprecated, instead "
|
|
||||||
"set Meta.base_manager_name on '{}'.".format(self.model._meta.label),
|
|
||||||
RemovedInDjango20Warning, 2
|
|
||||||
)
|
|
||||||
|
|
||||||
if i == 0:
|
|
||||||
manager = self.default_manager
|
|
||||||
else:
|
|
||||||
manager = base_manager_class()
|
|
||||||
manager.name = '_base_manager'
|
|
||||||
manager.model = self.model
|
|
||||||
|
|
||||||
return manager
|
|
||||||
|
|
||||||
manager = Manager()
|
manager = Manager()
|
||||||
manager.name = '_base_manager'
|
manager.name = '_base_manager'
|
||||||
manager.model = self.model
|
manager.model = self.model
|
||||||
|
|
|
@ -384,3 +384,9 @@ these features.
|
||||||
removed.
|
removed.
|
||||||
|
|
||||||
* The ``escape`` filter now uses ``django.utils.html.conditional_escape()``.
|
* The ``escape`` filter now uses ``django.utils.html.conditional_escape()``.
|
||||||
|
|
||||||
|
* ``Manager.use_for_related_fields`` is removed.
|
||||||
|
|
||||||
|
* Model ``Manager`` inheritance follows MRO inheritance rules. The requirement
|
||||||
|
to use ``Meta.manager_inheritance_from_future`` to opt-in to the behavior is
|
||||||
|
removed.
|
||||||
|
|
|
@ -118,9 +118,6 @@ class Child5(AbstractBase3):
|
||||||
class Child6(Child4):
|
class Child6(Child4):
|
||||||
value = models.IntegerField()
|
value = models.IntegerField()
|
||||||
|
|
||||||
class Meta:
|
|
||||||
manager_inheritance_from_future = True
|
|
||||||
|
|
||||||
|
|
||||||
class Child7(Parent):
|
class Child7(Parent):
|
||||||
objects = models.Manager()
|
objects = models.Manager()
|
||||||
|
|
|
@ -1,13 +1,9 @@
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
import warnings
|
|
||||||
|
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.db.utils import DatabaseError
|
|
||||||
from django.template import Context, Template
|
from django.template import Context, Template
|
||||||
from django.test import TestCase, override_settings
|
from django.test import TestCase, override_settings
|
||||||
from django.test.utils import isolate_apps
|
from django.test.utils import isolate_apps
|
||||||
from django.utils.deprecation import RemovedInDjango20Warning
|
|
||||||
from django.utils.encoding import force_text
|
from django.utils.encoding import force_text
|
||||||
|
|
||||||
from .models import (
|
from .models import (
|
||||||
|
@ -185,23 +181,20 @@ class TestManagerInheritance(TestCase):
|
||||||
self.assertIsInstance(PlainModel._default_manager, CustomManager)
|
self.assertIsInstance(PlainModel._default_manager, CustomManager)
|
||||||
|
|
||||||
class ModelWithAbstractParent(AbstractModel):
|
class ModelWithAbstractParent(AbstractModel):
|
||||||
class Meta:
|
pass
|
||||||
manager_inheritance_from_future = True
|
|
||||||
|
|
||||||
self.assertIsInstance(ModelWithAbstractParent._base_manager, models.Manager)
|
self.assertIsInstance(ModelWithAbstractParent._base_manager, models.Manager)
|
||||||
self.assertIsInstance(ModelWithAbstractParent._default_manager, CustomManager)
|
self.assertIsInstance(ModelWithAbstractParent._default_manager, CustomManager)
|
||||||
|
|
||||||
class ProxyModel(PlainModel):
|
class ProxyModel(PlainModel):
|
||||||
class Meta:
|
class Meta:
|
||||||
manager_inheritance_from_future = True
|
|
||||||
proxy = True
|
proxy = True
|
||||||
|
|
||||||
self.assertIsInstance(ProxyModel._base_manager, models.Manager)
|
self.assertIsInstance(ProxyModel._base_manager, models.Manager)
|
||||||
self.assertIsInstance(ProxyModel._default_manager, CustomManager)
|
self.assertIsInstance(ProxyModel._default_manager, CustomManager)
|
||||||
|
|
||||||
class MTIModel(PlainModel):
|
class MTIModel(PlainModel):
|
||||||
class Meta:
|
pass
|
||||||
manager_inheritance_from_future = True
|
|
||||||
|
|
||||||
self.assertIsInstance(MTIModel._base_manager, models.Manager)
|
self.assertIsInstance(MTIModel._base_manager, models.Manager)
|
||||||
self.assertIsInstance(MTIModel._default_manager, CustomManager)
|
self.assertIsInstance(MTIModel._default_manager, CustomManager)
|
||||||
|
@ -228,21 +221,18 @@ class TestManagerInheritance(TestCase):
|
||||||
self.assertIsInstance(PlainModel._default_manager, CustomManager)
|
self.assertIsInstance(PlainModel._default_manager, CustomManager)
|
||||||
|
|
||||||
class ModelWithAbstractParent(AbstractModel):
|
class ModelWithAbstractParent(AbstractModel):
|
||||||
class Meta:
|
pass
|
||||||
manager_inheritance_from_future = True
|
|
||||||
|
|
||||||
self.assertIsInstance(ModelWithAbstractParent._default_manager, CustomManager)
|
self.assertIsInstance(ModelWithAbstractParent._default_manager, CustomManager)
|
||||||
|
|
||||||
class ProxyModel(PlainModel):
|
class ProxyModel(PlainModel):
|
||||||
class Meta:
|
class Meta:
|
||||||
manager_inheritance_from_future = True
|
|
||||||
proxy = True
|
proxy = True
|
||||||
|
|
||||||
self.assertIsInstance(ProxyModel._default_manager, CustomManager)
|
self.assertIsInstance(ProxyModel._default_manager, CustomManager)
|
||||||
|
|
||||||
class MTIModel(PlainModel):
|
class MTIModel(PlainModel):
|
||||||
class Meta:
|
pass
|
||||||
manager_inheritance_from_future = True
|
|
||||||
|
|
||||||
self.assertIsInstance(MTIModel._default_manager, CustomManager)
|
self.assertIsInstance(MTIModel._default_manager, CustomManager)
|
||||||
|
|
||||||
|
@ -268,21 +258,18 @@ class TestManagerInheritance(TestCase):
|
||||||
self.assertIsInstance(PlainModel._base_manager, CustomManager)
|
self.assertIsInstance(PlainModel._base_manager, CustomManager)
|
||||||
|
|
||||||
class ModelWithAbstractParent(AbstractModel):
|
class ModelWithAbstractParent(AbstractModel):
|
||||||
class Meta:
|
pass
|
||||||
manager_inheritance_from_future = True
|
|
||||||
|
|
||||||
self.assertIsInstance(ModelWithAbstractParent._base_manager, CustomManager)
|
self.assertIsInstance(ModelWithAbstractParent._base_manager, CustomManager)
|
||||||
|
|
||||||
class ProxyModel(PlainModel):
|
class ProxyModel(PlainModel):
|
||||||
class Meta:
|
class Meta:
|
||||||
manager_inheritance_from_future = True
|
|
||||||
proxy = True
|
proxy = True
|
||||||
|
|
||||||
self.assertIsInstance(ProxyModel._base_manager, CustomManager)
|
self.assertIsInstance(ProxyModel._base_manager, CustomManager)
|
||||||
|
|
||||||
class MTIModel(PlainModel):
|
class MTIModel(PlainModel):
|
||||||
class Meta:
|
pass
|
||||||
manager_inheritance_from_future = True
|
|
||||||
|
|
||||||
self.assertIsInstance(MTIModel._base_manager, CustomManager)
|
self.assertIsInstance(MTIModel._base_manager, CustomManager)
|
||||||
|
|
||||||
|
@ -301,342 +288,3 @@ class TestManagerInheritance(TestCase):
|
||||||
|
|
||||||
self.assertEqual(TestModel._meta.managers, (TestModel.custom_manager,))
|
self.assertEqual(TestModel._meta.managers, (TestModel.custom_manager,))
|
||||||
self.assertEqual(TestModel._meta.managers_map, {'custom_manager': TestModel.custom_manager})
|
self.assertEqual(TestModel._meta.managers_map, {'custom_manager': TestModel.custom_manager})
|
||||||
|
|
||||||
|
|
||||||
@isolate_apps('managers_regress')
|
|
||||||
class TestManagerDeprecations(TestCase):
|
|
||||||
|
|
||||||
def test_use_for_related_fields_for_base_manager(self):
|
|
||||||
class MyManager(models.Manager):
|
|
||||||
use_for_related_fields = True
|
|
||||||
|
|
||||||
class MyModel(models.Model):
|
|
||||||
objects = MyManager()
|
|
||||||
|
|
||||||
with warnings.catch_warnings(record=True) as warns:
|
|
||||||
warnings.simplefilter('always', RemovedInDjango20Warning)
|
|
||||||
MyModel._base_manager
|
|
||||||
self.assertEqual(len(warns), 1)
|
|
||||||
self.assertEqual(
|
|
||||||
str(warns[0].message),
|
|
||||||
"use_for_related_fields is deprecated, "
|
|
||||||
"instead set Meta.base_manager_name on "
|
|
||||||
"'managers_regress.MyModel'.",
|
|
||||||
)
|
|
||||||
|
|
||||||
# With the new base_manager_name API there shouldn't be any warnings.
|
|
||||||
class MyModel2(models.Model):
|
|
||||||
objects = MyManager()
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
base_manager_name = 'objects'
|
|
||||||
|
|
||||||
with warnings.catch_warnings(record=True) as warns:
|
|
||||||
warnings.simplefilter('always', RemovedInDjango20Warning)
|
|
||||||
MyModel2._base_manager
|
|
||||||
self.assertEqual(len(warns), 0)
|
|
||||||
|
|
||||||
def test_use_for_related_fields_for_many_to_one(self):
|
|
||||||
# Common objects
|
|
||||||
class MyManagerQuerySet(models.QuerySet):
|
|
||||||
pass
|
|
||||||
|
|
||||||
class MyLegacyManagerQuerySet(models.QuerySet):
|
|
||||||
pass
|
|
||||||
|
|
||||||
class MyManager(models.Manager):
|
|
||||||
def get_queryset(self):
|
|
||||||
return MyManagerQuerySet(model=self.model, using=self._db, hints=self._hints)
|
|
||||||
|
|
||||||
class MyLegacyManager(models.Manager):
|
|
||||||
use_for_related_fields = True
|
|
||||||
|
|
||||||
def get_queryset(self):
|
|
||||||
return MyLegacyManagerQuerySet(model=self.model, using=self._db, hints=self._hints)
|
|
||||||
|
|
||||||
# With legacy config there should be a deprecation warning
|
|
||||||
class MyRelModel(models.Model):
|
|
||||||
objects = MyLegacyManager()
|
|
||||||
|
|
||||||
class MyModel(models.Model):
|
|
||||||
fk = models.ForeignKey(MyRelModel, on_delete=models.DO_NOTHING)
|
|
||||||
|
|
||||||
with warnings.catch_warnings(record=True) as warns:
|
|
||||||
warnings.simplefilter('always', RemovedInDjango20Warning)
|
|
||||||
try:
|
|
||||||
MyModel(fk_id=42).fk
|
|
||||||
except DatabaseError:
|
|
||||||
pass
|
|
||||||
self.assertEqual(len(warns), 1)
|
|
||||||
self.assertEqual(
|
|
||||||
str(warns[0].message),
|
|
||||||
"use_for_related_fields is deprecated, "
|
|
||||||
"instead set Meta.base_manager_name on "
|
|
||||||
"'managers_regress.MyRelModel'.",
|
|
||||||
)
|
|
||||||
|
|
||||||
# With the new base_manager_name API there shouldn't be any warnings.
|
|
||||||
class MyRelModel2(models.Model):
|
|
||||||
objects = MyManager()
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
base_manager_name = 'objects'
|
|
||||||
|
|
||||||
class MyModel2(models.Model):
|
|
||||||
fk = models.ForeignKey(MyRelModel2, on_delete=models.DO_NOTHING)
|
|
||||||
|
|
||||||
with warnings.catch_warnings(record=True) as warns:
|
|
||||||
warnings.simplefilter('always', RemovedInDjango20Warning)
|
|
||||||
try:
|
|
||||||
MyModel2(fk_id=42).fk
|
|
||||||
except DatabaseError:
|
|
||||||
pass
|
|
||||||
self.assertEqual(len(warns), 0)
|
|
||||||
|
|
||||||
# When mixing base_manager_name and use_for_related_fields, there
|
|
||||||
# should be warnings.
|
|
||||||
class MyRelModel3(models.Model):
|
|
||||||
my_base_manager = MyManager()
|
|
||||||
my_default_manager = MyLegacyManager()
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
base_manager_name = 'my_base_manager'
|
|
||||||
default_manager_name = 'my_default_manager'
|
|
||||||
|
|
||||||
class MyModel3(models.Model):
|
|
||||||
fk = models.ForeignKey(MyRelModel3, on_delete=models.DO_NOTHING)
|
|
||||||
|
|
||||||
with warnings.catch_warnings(record=True) as warns:
|
|
||||||
warnings.simplefilter('always', RemovedInDjango20Warning)
|
|
||||||
try:
|
|
||||||
MyModel3(fk_id=42).fk
|
|
||||||
except DatabaseError:
|
|
||||||
pass
|
|
||||||
self.assertEqual(len(warns), 1)
|
|
||||||
self.assertEqual(
|
|
||||||
str(warns[0].message),
|
|
||||||
"use_for_related_fields is deprecated, "
|
|
||||||
"instead set Meta.base_manager_name on "
|
|
||||||
"'managers_regress.MyRelModel3'.",
|
|
||||||
)
|
|
||||||
with warnings.catch_warnings(record=True):
|
|
||||||
warnings.simplefilter('always', RemovedInDjango20Warning)
|
|
||||||
self.assertIsInstance(MyModel3.fk.get_queryset(), MyLegacyManagerQuerySet)
|
|
||||||
|
|
||||||
def test_use_for_related_fields_for_one_to_one(self):
|
|
||||||
# Common objects
|
|
||||||
class MyManagerQuerySet(models.QuerySet):
|
|
||||||
pass
|
|
||||||
|
|
||||||
class MyLegacyManagerQuerySet(models.QuerySet):
|
|
||||||
pass
|
|
||||||
|
|
||||||
class MyManager(models.Manager):
|
|
||||||
def get_queryset(self):
|
|
||||||
return MyManagerQuerySet(model=self.model, using=self._db, hints=self._hints)
|
|
||||||
|
|
||||||
class MyLegacyManager(models.Manager):
|
|
||||||
use_for_related_fields = True
|
|
||||||
|
|
||||||
def get_queryset(self):
|
|
||||||
return MyLegacyManagerQuerySet(model=self.model, using=self._db, hints=self._hints)
|
|
||||||
|
|
||||||
# With legacy config there should be a deprecation warning
|
|
||||||
class MyRelModel(models.Model):
|
|
||||||
objects = MyLegacyManager()
|
|
||||||
|
|
||||||
class MyModel(models.Model):
|
|
||||||
o2o = models.OneToOneField(MyRelModel, on_delete=models.DO_NOTHING)
|
|
||||||
objects = MyLegacyManager()
|
|
||||||
|
|
||||||
with warnings.catch_warnings(record=True) as warns:
|
|
||||||
warnings.simplefilter('always', RemovedInDjango20Warning)
|
|
||||||
try:
|
|
||||||
MyModel(o2o_id=42).o2o
|
|
||||||
except DatabaseError:
|
|
||||||
pass
|
|
||||||
self.assertEqual(len(warns), 1)
|
|
||||||
self.assertEqual(
|
|
||||||
str(warns[0].message),
|
|
||||||
"use_for_related_fields is deprecated, "
|
|
||||||
"instead set Meta.base_manager_name on "
|
|
||||||
"'managers_regress.MyRelModel'.",
|
|
||||||
)
|
|
||||||
|
|
||||||
with warnings.catch_warnings(record=True) as warns:
|
|
||||||
warnings.simplefilter('always', RemovedInDjango20Warning)
|
|
||||||
try:
|
|
||||||
MyRelModel(pk=42).mymodel
|
|
||||||
except DatabaseError:
|
|
||||||
pass
|
|
||||||
self.assertEqual(len(warns), 1)
|
|
||||||
self.assertEqual(
|
|
||||||
str(warns[0].message),
|
|
||||||
"use_for_related_fields is deprecated, "
|
|
||||||
"instead set Meta.base_manager_name on "
|
|
||||||
"'managers_regress.MyModel'.",
|
|
||||||
)
|
|
||||||
|
|
||||||
# With the new base_manager_name API there shouldn't be any warnings.
|
|
||||||
class MyRelModel2(models.Model):
|
|
||||||
objects = MyManager()
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
base_manager_name = 'objects'
|
|
||||||
|
|
||||||
class MyModel2(models.Model):
|
|
||||||
o2o = models.OneToOneField(MyRelModel2, on_delete=models.DO_NOTHING)
|
|
||||||
objects = MyManager()
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
base_manager_name = 'objects'
|
|
||||||
|
|
||||||
with warnings.catch_warnings(record=True) as warns:
|
|
||||||
warnings.simplefilter('always', RemovedInDjango20Warning)
|
|
||||||
try:
|
|
||||||
MyModel2(o2o_id=42).o2o
|
|
||||||
except DatabaseError:
|
|
||||||
pass
|
|
||||||
try:
|
|
||||||
MyRelModel2(pk=42).mymodel2
|
|
||||||
except DatabaseError:
|
|
||||||
pass
|
|
||||||
self.assertEqual(len(warns), 0)
|
|
||||||
|
|
||||||
# When mixing base_manager_name and use_for_related_fields, there
|
|
||||||
# should be warnings.
|
|
||||||
class MyRelModel3(models.Model):
|
|
||||||
my_base_manager = MyManager()
|
|
||||||
my_default_manager = MyLegacyManager()
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
base_manager_name = 'my_base_manager'
|
|
||||||
default_manager_name = 'my_default_manager'
|
|
||||||
|
|
||||||
class MyModel3(models.Model):
|
|
||||||
o2o = models.OneToOneField(MyRelModel3, on_delete=models.DO_NOTHING)
|
|
||||||
|
|
||||||
with warnings.catch_warnings(record=True) as warns:
|
|
||||||
warnings.simplefilter('always', RemovedInDjango20Warning)
|
|
||||||
try:
|
|
||||||
MyModel3(o2o_id=42).o2o
|
|
||||||
except DatabaseError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
self.assertEqual(len(warns), 1)
|
|
||||||
self.assertEqual(
|
|
||||||
str(warns[0].message),
|
|
||||||
"use_for_related_fields is deprecated, "
|
|
||||||
"instead set Meta.base_manager_name on "
|
|
||||||
"'managers_regress.MyRelModel3'.",
|
|
||||||
)
|
|
||||||
with warnings.catch_warnings(record=True):
|
|
||||||
warnings.simplefilter('always', RemovedInDjango20Warning)
|
|
||||||
self.assertIsInstance(MyModel3.o2o.get_queryset(), MyLegacyManagerQuerySet)
|
|
||||||
|
|
||||||
def test_legacy_objects_is_created(self):
|
|
||||||
class ConcreteParentWithoutManager(models.Model):
|
|
||||||
pass
|
|
||||||
|
|
||||||
class ConcreteParentWithManager(models.Model):
|
|
||||||
default = models.Manager()
|
|
||||||
|
|
||||||
class AbstractParent(models.Model):
|
|
||||||
default = models.Manager()
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
abstract = True
|
|
||||||
|
|
||||||
# Shouldn't complain since the inherited manager
|
|
||||||
# is basically the same that would have been created.
|
|
||||||
with warnings.catch_warnings(record=True) as warns:
|
|
||||||
warnings.simplefilter('always', RemovedInDjango20Warning)
|
|
||||||
|
|
||||||
class MyModel(ConcreteParentWithoutManager):
|
|
||||||
pass
|
|
||||||
self.assertEqual(len(warns), 0)
|
|
||||||
|
|
||||||
# Should create 'objects' (set as default) and warn that
|
|
||||||
# it will no longer be the case in the future.
|
|
||||||
with warnings.catch_warnings(record=True) as warns:
|
|
||||||
warnings.simplefilter('always', RemovedInDjango20Warning)
|
|
||||||
|
|
||||||
class MyModel2(ConcreteParentWithManager):
|
|
||||||
pass
|
|
||||||
self.assertEqual(len(warns), 1)
|
|
||||||
self.assertEqual(
|
|
||||||
str(warns[0].message),
|
|
||||||
"Managers from concrete parents will soon qualify as default "
|
|
||||||
"managers. As a result, the 'objects' manager won't be created "
|
|
||||||
"(or recreated) automatically anymore on "
|
|
||||||
"'managers_regress.MyModel2' and 'default' declared on "
|
|
||||||
"'managers_regress.ConcreteParentWithManager' will be promoted "
|
|
||||||
"to default manager. You can declare explicitly "
|
|
||||||
"`objects = models.Manager()` on 'MyModel2' to keep things the "
|
|
||||||
"way they are or you can switch to the new behavior right away "
|
|
||||||
"by setting `Meta.manager_inheritance_from_future` to `True`.",
|
|
||||||
)
|
|
||||||
|
|
||||||
self.assertIs(MyModel2.objects, MyModel2._default_manager)
|
|
||||||
|
|
||||||
# When there is a local manager we shouldn't get any warning
|
|
||||||
# and 'objects' shouldn't be created.
|
|
||||||
class MyModel3(ConcreteParentWithManager):
|
|
||||||
default = models.Manager()
|
|
||||||
self.assertIs(MyModel3.default, MyModel3._default_manager)
|
|
||||||
self.assertIsNone(getattr(MyModel3, 'objects', None))
|
|
||||||
|
|
||||||
# When there is an inherited manager we shouldn't get any warning
|
|
||||||
# and 'objects' shouldn't be created.
|
|
||||||
class MyModel4(AbstractParent, ConcreteParentWithManager):
|
|
||||||
pass
|
|
||||||
self.assertIs(MyModel4.default, MyModel4._default_manager)
|
|
||||||
self.assertIsNone(getattr(MyModel4, 'objects', None))
|
|
||||||
|
|
||||||
# With `manager_inheritance_from_future = True` 'objects'
|
|
||||||
# shouldn't be created.
|
|
||||||
class MyModel5(ConcreteParentWithManager):
|
|
||||||
class Meta:
|
|
||||||
manager_inheritance_from_future = True
|
|
||||||
self.assertIs(MyModel5.default, MyModel5._default_manager)
|
|
||||||
self.assertIsNone(getattr(MyModel5, 'objects', None))
|
|
||||||
|
|
||||||
def test_legacy_default_manager_promotion(self):
|
|
||||||
class ConcreteParent(models.Model):
|
|
||||||
concrete = models.Manager()
|
|
||||||
|
|
||||||
class AbstractParent(models.Model):
|
|
||||||
abstract = models.Manager()
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
abstract = True
|
|
||||||
|
|
||||||
with warnings.catch_warnings(record=True) as warns:
|
|
||||||
warnings.simplefilter('always', RemovedInDjango20Warning)
|
|
||||||
|
|
||||||
class MyModel(ConcreteParent, AbstractParent):
|
|
||||||
pass
|
|
||||||
self.assertEqual(len(warns), 1)
|
|
||||||
self.assertEqual(
|
|
||||||
str(warns[0].message),
|
|
||||||
"Managers from concrete parents will soon qualify as default "
|
|
||||||
"managers if they appear before any other managers in the "
|
|
||||||
"MRO. As a result, 'abstract' declared on "
|
|
||||||
"'managers_regress.AbstractParent' will no longer be the "
|
|
||||||
"default manager for 'managers_regress.MyModel' in favor of "
|
|
||||||
"'concrete' declared on 'managers_regress.ConcreteParent'. "
|
|
||||||
"You can redeclare 'abstract' on 'MyModel' to keep things the "
|
|
||||||
"way they are or you can switch to the new behavior right "
|
|
||||||
"away by setting `Meta.manager_inheritance_from_future` to "
|
|
||||||
"`True`.",
|
|
||||||
)
|
|
||||||
self.assertIs(MyModel.abstract, MyModel._default_manager)
|
|
||||||
|
|
||||||
class MyModel2(ConcreteParent, AbstractParent):
|
|
||||||
abstract = models.Manager()
|
|
||||||
self.assertIs(MyModel2.abstract, MyModel2._default_manager)
|
|
||||||
|
|
||||||
class MyModel3(ConcreteParent, AbstractParent):
|
|
||||||
class Meta:
|
|
||||||
manager_inheritance_from_future = True
|
|
||||||
self.assertIs(MyModel3.concrete, MyModel3._default_manager)
|
|
||||||
|
|
|
@ -4,8 +4,7 @@ from copy import deepcopy
|
||||||
from django.core.exceptions import FieldError, MultipleObjectsReturned
|
from django.core.exceptions import FieldError, MultipleObjectsReturned
|
||||||
from django.db import models, transaction
|
from django.db import models, transaction
|
||||||
from django.db.utils import IntegrityError
|
from django.db.utils import IntegrityError
|
||||||
from django.test import TestCase, ignore_warnings
|
from django.test import TestCase
|
||||||
from django.utils.deprecation import RemovedInDjango20Warning
|
|
||||||
from django.utils.translation import ugettext_lazy
|
from django.utils.translation import ugettext_lazy
|
||||||
|
|
||||||
from .models import (
|
from .models import (
|
||||||
|
@ -577,7 +576,6 @@ class ManyToOneTests(TestCase):
|
||||||
with self.assertNumQueries(1):
|
with self.assertNumQueries(1):
|
||||||
self.assertEqual(th.child_set.count(), 0)
|
self.assertEqual(th.child_set.count(), 0)
|
||||||
|
|
||||||
@ignore_warnings(category=RemovedInDjango20Warning) # for use_for_related_fields deprecation
|
|
||||||
def test_related_object(self):
|
def test_related_object(self):
|
||||||
public_school = School.objects.create(is_public=True)
|
public_school = School.objects.create(is_public=True)
|
||||||
public_student = Student.objects.create(school=public_school)
|
public_student = Student.objects.create(school=public_school)
|
||||||
|
@ -595,17 +593,6 @@ class ManyToOneTests(TestCase):
|
||||||
# allow it.
|
# allow it.
|
||||||
self.assertEqual(private_student.school, private_school)
|
self.assertEqual(private_student.school, private_school)
|
||||||
|
|
||||||
# If the manager is marked "use_for_related_fields", it'll get used instead
|
|
||||||
# of the "bare" queryset. Usually you'd define this as a property on the class,
|
|
||||||
# but this approximates that in a way that's easier in tests.
|
|
||||||
School._default_manager.use_for_related_fields = True
|
|
||||||
try:
|
|
||||||
private_student = Student.objects.get(pk=private_student.pk)
|
|
||||||
with self.assertRaises(School.DoesNotExist):
|
|
||||||
private_student.school
|
|
||||||
finally:
|
|
||||||
School._default_manager.use_for_related_fields = False
|
|
||||||
|
|
||||||
School._meta.base_manager_name = 'objects'
|
School._meta.base_manager_name = 'objects'
|
||||||
School._meta._expire_cache()
|
School._meta._expire_cache()
|
||||||
try:
|
try:
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
from django.db import IntegrityError, connection, transaction
|
from django.db import IntegrityError, connection, transaction
|
||||||
from django.test import TestCase, ignore_warnings
|
from django.test import TestCase
|
||||||
from django.utils.deprecation import RemovedInDjango20Warning
|
|
||||||
|
|
||||||
from .models import (
|
from .models import (
|
||||||
Bar, Director, Favorites, HiddenPointer, ManualPrimaryKey, MultiModel,
|
Bar, Director, Favorites, HiddenPointer, ManualPrimaryKey, MultiModel,
|
||||||
|
@ -419,7 +418,6 @@ class OneToOneTests(TestCase):
|
||||||
hasattr(Target, HiddenPointer._meta.get_field('target').remote_field.get_accessor_name())
|
hasattr(Target, HiddenPointer._meta.get_field('target').remote_field.get_accessor_name())
|
||||||
)
|
)
|
||||||
|
|
||||||
@ignore_warnings(category=RemovedInDjango20Warning) # for use_for_related_fields deprecation
|
|
||||||
def test_related_object(self):
|
def test_related_object(self):
|
||||||
public_school = School.objects.create(is_public=True)
|
public_school = School.objects.create(is_public=True)
|
||||||
public_director = Director.objects.create(school=public_school, is_temp=False)
|
public_director = Director.objects.create(school=public_school, is_temp=False)
|
||||||
|
@ -452,25 +450,6 @@ class OneToOneTests(TestCase):
|
||||||
# allow it.
|
# allow it.
|
||||||
self.assertEqual(private_school.director, private_director)
|
self.assertEqual(private_school.director, private_director)
|
||||||
|
|
||||||
# If the manager is marked "use_for_related_fields", it'll get used instead
|
|
||||||
# of the "bare" queryset. Usually you'd define this as a property on the class,
|
|
||||||
# but this approximates that in a way that's easier in tests.
|
|
||||||
School._default_manager.use_for_related_fields = True
|
|
||||||
try:
|
|
||||||
private_director = Director._base_manager.get(pk=private_director.pk)
|
|
||||||
with self.assertRaises(School.DoesNotExist):
|
|
||||||
private_director.school
|
|
||||||
finally:
|
|
||||||
School._default_manager.use_for_related_fields = False
|
|
||||||
|
|
||||||
Director._default_manager.use_for_related_fields = True
|
|
||||||
try:
|
|
||||||
private_school = School._base_manager.get(pk=private_school.pk)
|
|
||||||
with self.assertRaises(Director.DoesNotExist):
|
|
||||||
private_school.director
|
|
||||||
finally:
|
|
||||||
Director._default_manager.use_for_related_fields = False
|
|
||||||
|
|
||||||
School._meta.base_manager_name = 'objects'
|
School._meta.base_manager_name = 'objects'
|
||||||
School._meta._expire_cache()
|
School._meta._expire_cache()
|
||||||
try:
|
try:
|
||||||
|
|
|
@ -17,6 +17,3 @@ class Parent(models.Model):
|
||||||
|
|
||||||
class Child(Parent):
|
class Child(Parent):
|
||||||
child_data = models.CharField(max_length=30, unique=True)
|
child_data = models.CharField(max_length=30, unique=True)
|
||||||
|
|
||||||
class Meta:
|
|
||||||
manager_inheritance_from_future = True
|
|
||||||
|
|
Loading…
Reference in New Issue