Removed Manager.use_for_related_fields and Meta.manager_inheritance_from_future.

Per deprecation timeline. Refs ed0ff913c6.
This commit is contained in:
Tim Graham 2016-12-31 13:07:35 -05:00
parent 60ca37d2e5
commit 631f4ab061
10 changed files with 21 additions and 547 deletions

View File

@ -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:

View File

@ -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

View File

@ -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:

View File

@ -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

View File

@ -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.

View File

@ -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()

View File

@ -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)

View File

@ -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:

View File

@ -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:

View File

@ -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