Loading django/db/models/options.py +3 −156 Original line number Diff line number Diff line from __future__ import unicode_literals import warnings from bisect import bisect from collections import OrderedDict, defaultdict from itertools import chain Loading @@ -11,15 +10,12 @@ from django.core.exceptions import FieldDoesNotExist from django.db import connections from django.db.models.fields import AutoField from django.db.models.fields.proxy import OrderWrt from django.db.models.fields.related import ManyToManyField from django.utils import six from django.utils.datastructures import ImmutableList, OrderedSet from django.utils.deprecation import RemovedInDjango110Warning from django.utils.encoding import ( force_text, python_2_unicode_compatible, smart_text, ) from django.utils.functional import cached_property from django.utils.lru_cache import lru_cache from django.utils.text import camel_case_to_spaces from django.utils.translation import override, string_concat Loading @@ -41,24 +37,6 @@ DEFAULT_NAMES = ('verbose_name', 'verbose_name_plural', 'db_table', 'ordering', 'required_db_features', 'required_db_vendor') class raise_deprecation(object): def __init__(self, suggested_alternative): self.suggested_alternative = suggested_alternative def __call__(self, fn): def wrapper(*args, **kwargs): warnings.warn( "'%s is an unofficial API that has been deprecated. " "You may be able to replace it with '%s'" % ( fn.__name__, self.suggested_alternative, ), RemovedInDjango110Warning, stacklevel=2 ) return fn(*args, **kwargs) return wrapper def normalize_together(option_together): """ option_together can be either a tuple of tuples, or a single Loading Loading @@ -151,31 +129,6 @@ class Options(object): self.default_related_name = None @lru_cache(maxsize=None) def _map_model(self, link): # This helper function is used to allow backwards compatibility with # the previous API. No future methods should use this function. # It maps a field to (field, model or related_model,) depending on the # field type. model = link.model._meta.concrete_model if model is self.model: model = None return link, model @lru_cache(maxsize=None) def _map_model_details(self, link): # This helper function is used to allow backwards compatibility with # the previous API. No future methods should use this function. # This function maps a field to a tuple of: # (field, model or related_model, direct, is_m2m) depending on the # field type. direct = not link.auto_created or link.concrete model = link.model._meta.concrete_model if model is self.model: model = None m2m = link.is_relation and link.many_to_many return link, model, direct, m2m @property def label(self): return '%s.%s' % (self.app_label, self.object_name) Loading Loading @@ -455,14 +408,6 @@ class Options(object): "local_concrete_fields", (f for f in self.local_fields if f.concrete) ) @raise_deprecation(suggested_alternative="get_fields()") def get_fields_with_model(self): return [self._map_model(f) for f in self.get_fields()] @raise_deprecation(suggested_alternative="get_fields()") def get_concrete_fields_with_model(self): return [self._map_model(f) for f in self.concrete_fields] @cached_property def many_to_many(self): """ Loading Loading @@ -496,10 +441,6 @@ class Options(object): if not obj.hidden or obj.field.many_to_many) ) @raise_deprecation(suggested_alternative="get_fields()") def get_m2m_with_model(self): return [self._map_model(f) for f in self.many_to_many] @cached_property def _forward_fields_map(self): res = {} Loading Loading @@ -530,36 +471,14 @@ class Options(object): pass return res def get_field(self, field_name, many_to_many=None): def get_field(self, field_name): """ Returns a field instance given a field name. The field can be either a forward or reverse field, unless many_to_many is specified; if it is, only forward fields will be returned. The many_to_many argument exists for backwards compatibility reasons; it has been deprecated and will be removed in Django 1.10. Return a field instance given the name of a forward or reverse field. """ m2m_in_kwargs = many_to_many is not None if m2m_in_kwargs: # Always throw a warning if many_to_many is used regardless of # whether it alters the return type or not. warnings.warn( "The 'many_to_many' argument on get_field() is deprecated; " "use a filter on field.many_to_many instead.", RemovedInDjango110Warning ) try: # In order to avoid premature loading of the relation tree # (expensive) we prefer checking if the field is a forward field. field = self._forward_fields_map[field_name] if many_to_many is False and field.many_to_many: raise FieldDoesNotExist( '%s has no field named %r' % (self.object_name, field_name) ) return field return self._forward_fields_map[field_name] except KeyError: # If the app registry is not ready, reverse fields are # unavailable, therefore we throw a FieldDoesNotExist exception. Loading @@ -571,84 +490,12 @@ class Options(object): ) try: if m2m_in_kwargs: # Previous API does not allow searching reverse fields. raise FieldDoesNotExist('%s has no field named %r' % (self.object_name, field_name)) # Retrieve field instance by name from cached or just-computed # field map. return self.fields_map[field_name] except KeyError: raise FieldDoesNotExist('%s has no field named %r' % (self.object_name, field_name)) @raise_deprecation(suggested_alternative="get_field()") def get_field_by_name(self, name): return self._map_model_details(self.get_field(name)) @raise_deprecation(suggested_alternative="get_fields()") def get_all_field_names(self): names = set() fields = self.get_fields() for field in fields: # For backwards compatibility GenericForeignKey should not be # included in the results. if field.is_relation and field.many_to_one and field.related_model is None: continue # Relations to child proxy models should not be included. if (field.model != self.model and field.model._meta.concrete_model == self.concrete_model): continue names.add(field.name) if hasattr(field, 'attname'): names.add(field.attname) return list(names) @raise_deprecation(suggested_alternative="get_fields()") def get_all_related_objects(self, local_only=False, include_hidden=False, include_proxy_eq=False): include_parents = True if local_only is False else PROXY_PARENTS fields = self._get_fields( forward=False, reverse=True, include_parents=include_parents, include_hidden=include_hidden, ) fields = (obj for obj in fields if not isinstance(obj.field, ManyToManyField)) if include_proxy_eq: children = chain.from_iterable(c._relation_tree for c in self.concrete_model._meta.proxied_children if c is not self) relations = (f.remote_field for f in children if include_hidden or not f.remote_field.field.remote_field.is_hidden()) fields = chain(fields, relations) return list(fields) @raise_deprecation(suggested_alternative="get_fields()") def get_all_related_objects_with_model(self, local_only=False, include_hidden=False, include_proxy_eq=False): return [ self._map_model(f) for f in self.get_all_related_objects( local_only=local_only, include_hidden=include_hidden, include_proxy_eq=include_proxy_eq, ) ] @raise_deprecation(suggested_alternative="get_fields()") def get_all_related_many_to_many_objects(self, local_only=False): include_parents = True if local_only is not True else PROXY_PARENTS fields = self._get_fields( forward=False, reverse=True, include_parents=include_parents, include_hidden=True ) return [obj for obj in fields if isinstance(obj.field, ManyToManyField)] @raise_deprecation(suggested_alternative="get_fields()") def get_all_related_m2m_objects_with_model(self): fields = self._get_fields(forward=False, reverse=True, include_hidden=True) return [self._map_model(obj) for obj in fields if isinstance(obj.field, ManyToManyField)] def get_base_chain(self, model): """ Return a list of parent classes leading to `model` (ordered from Loading docs/ref/models/meta.txt +0 −12 Original line number Diff line number Diff line Loading @@ -70,18 +70,6 @@ Retrieving a single field instance of a model by name ... FieldDoesNotExist: User has no field named 'does_not_exist' .. deprecated:: 1.8 :meth:`Options.get_field()` previously accepted a ``many_to_many`` parameter which could be set to ``False`` to avoid searching ``ManyToManyField``\s. The old behavior has been preserved for backwards compatibility; however, the parameter and this behavior has been deprecated. If you wish to filter out ``ManyToManyField``\s, you can inspect the :attr:`Field.many_to_many <django.db.models.Field.many_to_many>` attribute after calling ``get_field()``. Retrieving all field instances of a model ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Loading tests/model_meta/test_legacy.pydeleted 100644 → 0 +0 −166 Original line number Diff line number Diff line import warnings from django import test from django.contrib.contenttypes.fields import GenericRelation from django.core.exceptions import FieldDoesNotExist from django.db.models.fields import CharField, related from django.utils.deprecation import RemovedInDjango110Warning from .models import BasePerson, Person from .results import TEST_RESULTS class OptionsBaseTests(test.SimpleTestCase): def _map_related_query_names(self, res): return tuple((o.field.related_query_name(), m) for o, m in res) def _map_names(self, res): return tuple((f.name, m) for f, m in res) class M2MTests(OptionsBaseTests): def test_many_to_many_with_model(self): for model, expected_result in TEST_RESULTS['many_to_many_with_model'].items(): with warnings.catch_warnings(record=True) as warning: warnings.simplefilter("always") models = [model for field, model in model._meta.get_m2m_with_model()] self.assertEqual([RemovedInDjango110Warning], [w.message.__class__ for w in warning]) self.assertEqual(models, expected_result) @test.ignore_warnings(category=RemovedInDjango110Warning) class RelatedObjectsTests(OptionsBaseTests): key_name = lambda self, r: r[0] def test_related_objects(self): result_key = 'get_all_related_objects_with_model_legacy' for model, expected in TEST_RESULTS[result_key].items(): objects = model._meta.get_all_related_objects_with_model() self.assertEqual(self._map_related_query_names(objects), expected) def test_related_objects_local(self): result_key = 'get_all_related_objects_with_model_local_legacy' for model, expected in TEST_RESULTS[result_key].items(): objects = model._meta.get_all_related_objects_with_model(local_only=True) self.assertEqual(self._map_related_query_names(objects), expected) def test_related_objects_include_hidden(self): result_key = 'get_all_related_objects_with_model_hidden_legacy' for model, expected in TEST_RESULTS[result_key].items(): objects = model._meta.get_all_related_objects_with_model(include_hidden=True) self.assertEqual( sorted(self._map_names(objects), key=self.key_name), sorted(expected, key=self.key_name) ) def test_related_objects_include_hidden_local_only(self): result_key = 'get_all_related_objects_with_model_hidden_local_legacy' for model, expected in TEST_RESULTS[result_key].items(): objects = model._meta.get_all_related_objects_with_model( include_hidden=True, local_only=True) self.assertEqual( sorted(self._map_names(objects), key=self.key_name), sorted(expected, key=self.key_name) ) def test_related_objects_proxy(self): result_key = 'get_all_related_objects_with_model_proxy_legacy' for model, expected in TEST_RESULTS[result_key].items(): objects = model._meta.get_all_related_objects_with_model( include_proxy_eq=True) self.assertEqual(self._map_related_query_names(objects), expected) def test_related_objects_proxy_hidden(self): result_key = 'get_all_related_objects_with_model_proxy_hidden_legacy' for model, expected in TEST_RESULTS[result_key].items(): objects = model._meta.get_all_related_objects_with_model( include_proxy_eq=True, include_hidden=True) self.assertEqual( sorted(self._map_names(objects), key=self.key_name), sorted(expected, key=self.key_name) ) @test.ignore_warnings(category=RemovedInDjango110Warning) class RelatedM2MTests(OptionsBaseTests): def test_related_m2m_with_model(self): result_key = 'get_all_related_many_to_many_with_model_legacy' for model, expected in TEST_RESULTS[result_key].items(): objects = model._meta.get_all_related_m2m_objects_with_model() self.assertEqual(self._map_related_query_names(objects), expected) def test_related_m2m_local_only(self): result_key = 'get_all_related_many_to_many_local_legacy' for model, expected in TEST_RESULTS[result_key].items(): objects = model._meta.get_all_related_many_to_many_objects(local_only=True) self.assertEqual([o.field.related_query_name() for o in objects], expected) def test_related_m2m_asymmetrical(self): m2m = Person._meta.many_to_many self.assertTrue('following_base' in [f.attname for f in m2m]) related_m2m = Person._meta.get_all_related_many_to_many_objects() self.assertTrue('followers_base' in [o.field.related_query_name() for o in related_m2m]) def test_related_m2m_symmetrical(self): m2m = Person._meta.many_to_many self.assertTrue('friends_base' in [f.attname for f in m2m]) related_m2m = Person._meta.get_all_related_many_to_many_objects() self.assertIn('friends_inherited_rel_+', [o.field.related_query_name() for o in related_m2m]) @test.ignore_warnings(category=RemovedInDjango110Warning) class GetFieldByNameTests(OptionsBaseTests): def test_get_data_field(self): field_info = Person._meta.get_field_by_name('data_abstract') self.assertEqual(field_info[1:], (BasePerson, True, False)) self.assertIsInstance(field_info[0], CharField) def test_get_m2m_field(self): field_info = Person._meta.get_field_by_name('m2m_base') self.assertEqual(field_info[1:], (BasePerson, True, True)) self.assertIsInstance(field_info[0], related.ManyToManyField) def test_get_related_object(self): field_info = Person._meta.get_field_by_name('relating_baseperson') self.assertEqual(field_info[1:], (BasePerson, False, False)) self.assertTrue(field_info[0].auto_created) def test_get_related_m2m(self): field_info = Person._meta.get_field_by_name('relating_people') self.assertEqual(field_info[1:], (None, False, True)) self.assertTrue(field_info[0].auto_created) def test_get_generic_relation(self): field_info = Person._meta.get_field_by_name('generic_relation_base') self.assertEqual(field_info[1:], (None, True, False)) self.assertIsInstance(field_info[0], GenericRelation) def test_get_m2m_field_invalid(self): with warnings.catch_warnings(record=True) as warning: warnings.simplefilter("always") self.assertRaises( FieldDoesNotExist, Person._meta.get_field, **{'field_name': 'm2m_base', 'many_to_many': False} ) self.assertEqual(Person._meta.get_field('m2m_base', many_to_many=True).name, 'm2m_base') # 2 RemovedInDjango110Warning messages should be raised, one for each call of get_field() # with the 'many_to_many' argument. self.assertEqual( [RemovedInDjango110Warning, RemovedInDjango110Warning], [w.message.__class__ for w in warning] ) @test.ignore_warnings(category=RemovedInDjango110Warning) class GetAllFieldNamesTestCase(OptionsBaseTests): def test_get_all_field_names(self): for model, expected_names in TEST_RESULTS['get_all_field_names'].items(): objects = model._meta.get_all_field_names() self.assertEqual(sorted(map(str, objects)), sorted(expected_names)) Loading
django/db/models/options.py +3 −156 Original line number Diff line number Diff line from __future__ import unicode_literals import warnings from bisect import bisect from collections import OrderedDict, defaultdict from itertools import chain Loading @@ -11,15 +10,12 @@ from django.core.exceptions import FieldDoesNotExist from django.db import connections from django.db.models.fields import AutoField from django.db.models.fields.proxy import OrderWrt from django.db.models.fields.related import ManyToManyField from django.utils import six from django.utils.datastructures import ImmutableList, OrderedSet from django.utils.deprecation import RemovedInDjango110Warning from django.utils.encoding import ( force_text, python_2_unicode_compatible, smart_text, ) from django.utils.functional import cached_property from django.utils.lru_cache import lru_cache from django.utils.text import camel_case_to_spaces from django.utils.translation import override, string_concat Loading @@ -41,24 +37,6 @@ DEFAULT_NAMES = ('verbose_name', 'verbose_name_plural', 'db_table', 'ordering', 'required_db_features', 'required_db_vendor') class raise_deprecation(object): def __init__(self, suggested_alternative): self.suggested_alternative = suggested_alternative def __call__(self, fn): def wrapper(*args, **kwargs): warnings.warn( "'%s is an unofficial API that has been deprecated. " "You may be able to replace it with '%s'" % ( fn.__name__, self.suggested_alternative, ), RemovedInDjango110Warning, stacklevel=2 ) return fn(*args, **kwargs) return wrapper def normalize_together(option_together): """ option_together can be either a tuple of tuples, or a single Loading Loading @@ -151,31 +129,6 @@ class Options(object): self.default_related_name = None @lru_cache(maxsize=None) def _map_model(self, link): # This helper function is used to allow backwards compatibility with # the previous API. No future methods should use this function. # It maps a field to (field, model or related_model,) depending on the # field type. model = link.model._meta.concrete_model if model is self.model: model = None return link, model @lru_cache(maxsize=None) def _map_model_details(self, link): # This helper function is used to allow backwards compatibility with # the previous API. No future methods should use this function. # This function maps a field to a tuple of: # (field, model or related_model, direct, is_m2m) depending on the # field type. direct = not link.auto_created or link.concrete model = link.model._meta.concrete_model if model is self.model: model = None m2m = link.is_relation and link.many_to_many return link, model, direct, m2m @property def label(self): return '%s.%s' % (self.app_label, self.object_name) Loading Loading @@ -455,14 +408,6 @@ class Options(object): "local_concrete_fields", (f for f in self.local_fields if f.concrete) ) @raise_deprecation(suggested_alternative="get_fields()") def get_fields_with_model(self): return [self._map_model(f) for f in self.get_fields()] @raise_deprecation(suggested_alternative="get_fields()") def get_concrete_fields_with_model(self): return [self._map_model(f) for f in self.concrete_fields] @cached_property def many_to_many(self): """ Loading Loading @@ -496,10 +441,6 @@ class Options(object): if not obj.hidden or obj.field.many_to_many) ) @raise_deprecation(suggested_alternative="get_fields()") def get_m2m_with_model(self): return [self._map_model(f) for f in self.many_to_many] @cached_property def _forward_fields_map(self): res = {} Loading Loading @@ -530,36 +471,14 @@ class Options(object): pass return res def get_field(self, field_name, many_to_many=None): def get_field(self, field_name): """ Returns a field instance given a field name. The field can be either a forward or reverse field, unless many_to_many is specified; if it is, only forward fields will be returned. The many_to_many argument exists for backwards compatibility reasons; it has been deprecated and will be removed in Django 1.10. Return a field instance given the name of a forward or reverse field. """ m2m_in_kwargs = many_to_many is not None if m2m_in_kwargs: # Always throw a warning if many_to_many is used regardless of # whether it alters the return type or not. warnings.warn( "The 'many_to_many' argument on get_field() is deprecated; " "use a filter on field.many_to_many instead.", RemovedInDjango110Warning ) try: # In order to avoid premature loading of the relation tree # (expensive) we prefer checking if the field is a forward field. field = self._forward_fields_map[field_name] if many_to_many is False and field.many_to_many: raise FieldDoesNotExist( '%s has no field named %r' % (self.object_name, field_name) ) return field return self._forward_fields_map[field_name] except KeyError: # If the app registry is not ready, reverse fields are # unavailable, therefore we throw a FieldDoesNotExist exception. Loading @@ -571,84 +490,12 @@ class Options(object): ) try: if m2m_in_kwargs: # Previous API does not allow searching reverse fields. raise FieldDoesNotExist('%s has no field named %r' % (self.object_name, field_name)) # Retrieve field instance by name from cached or just-computed # field map. return self.fields_map[field_name] except KeyError: raise FieldDoesNotExist('%s has no field named %r' % (self.object_name, field_name)) @raise_deprecation(suggested_alternative="get_field()") def get_field_by_name(self, name): return self._map_model_details(self.get_field(name)) @raise_deprecation(suggested_alternative="get_fields()") def get_all_field_names(self): names = set() fields = self.get_fields() for field in fields: # For backwards compatibility GenericForeignKey should not be # included in the results. if field.is_relation and field.many_to_one and field.related_model is None: continue # Relations to child proxy models should not be included. if (field.model != self.model and field.model._meta.concrete_model == self.concrete_model): continue names.add(field.name) if hasattr(field, 'attname'): names.add(field.attname) return list(names) @raise_deprecation(suggested_alternative="get_fields()") def get_all_related_objects(self, local_only=False, include_hidden=False, include_proxy_eq=False): include_parents = True if local_only is False else PROXY_PARENTS fields = self._get_fields( forward=False, reverse=True, include_parents=include_parents, include_hidden=include_hidden, ) fields = (obj for obj in fields if not isinstance(obj.field, ManyToManyField)) if include_proxy_eq: children = chain.from_iterable(c._relation_tree for c in self.concrete_model._meta.proxied_children if c is not self) relations = (f.remote_field for f in children if include_hidden or not f.remote_field.field.remote_field.is_hidden()) fields = chain(fields, relations) return list(fields) @raise_deprecation(suggested_alternative="get_fields()") def get_all_related_objects_with_model(self, local_only=False, include_hidden=False, include_proxy_eq=False): return [ self._map_model(f) for f in self.get_all_related_objects( local_only=local_only, include_hidden=include_hidden, include_proxy_eq=include_proxy_eq, ) ] @raise_deprecation(suggested_alternative="get_fields()") def get_all_related_many_to_many_objects(self, local_only=False): include_parents = True if local_only is not True else PROXY_PARENTS fields = self._get_fields( forward=False, reverse=True, include_parents=include_parents, include_hidden=True ) return [obj for obj in fields if isinstance(obj.field, ManyToManyField)] @raise_deprecation(suggested_alternative="get_fields()") def get_all_related_m2m_objects_with_model(self): fields = self._get_fields(forward=False, reverse=True, include_hidden=True) return [self._map_model(obj) for obj in fields if isinstance(obj.field, ManyToManyField)] def get_base_chain(self, model): """ Return a list of parent classes leading to `model` (ordered from Loading
docs/ref/models/meta.txt +0 −12 Original line number Diff line number Diff line Loading @@ -70,18 +70,6 @@ Retrieving a single field instance of a model by name ... FieldDoesNotExist: User has no field named 'does_not_exist' .. deprecated:: 1.8 :meth:`Options.get_field()` previously accepted a ``many_to_many`` parameter which could be set to ``False`` to avoid searching ``ManyToManyField``\s. The old behavior has been preserved for backwards compatibility; however, the parameter and this behavior has been deprecated. If you wish to filter out ``ManyToManyField``\s, you can inspect the :attr:`Field.many_to_many <django.db.models.Field.many_to_many>` attribute after calling ``get_field()``. Retrieving all field instances of a model ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Loading
tests/model_meta/test_legacy.pydeleted 100644 → 0 +0 −166 Original line number Diff line number Diff line import warnings from django import test from django.contrib.contenttypes.fields import GenericRelation from django.core.exceptions import FieldDoesNotExist from django.db.models.fields import CharField, related from django.utils.deprecation import RemovedInDjango110Warning from .models import BasePerson, Person from .results import TEST_RESULTS class OptionsBaseTests(test.SimpleTestCase): def _map_related_query_names(self, res): return tuple((o.field.related_query_name(), m) for o, m in res) def _map_names(self, res): return tuple((f.name, m) for f, m in res) class M2MTests(OptionsBaseTests): def test_many_to_many_with_model(self): for model, expected_result in TEST_RESULTS['many_to_many_with_model'].items(): with warnings.catch_warnings(record=True) as warning: warnings.simplefilter("always") models = [model for field, model in model._meta.get_m2m_with_model()] self.assertEqual([RemovedInDjango110Warning], [w.message.__class__ for w in warning]) self.assertEqual(models, expected_result) @test.ignore_warnings(category=RemovedInDjango110Warning) class RelatedObjectsTests(OptionsBaseTests): key_name = lambda self, r: r[0] def test_related_objects(self): result_key = 'get_all_related_objects_with_model_legacy' for model, expected in TEST_RESULTS[result_key].items(): objects = model._meta.get_all_related_objects_with_model() self.assertEqual(self._map_related_query_names(objects), expected) def test_related_objects_local(self): result_key = 'get_all_related_objects_with_model_local_legacy' for model, expected in TEST_RESULTS[result_key].items(): objects = model._meta.get_all_related_objects_with_model(local_only=True) self.assertEqual(self._map_related_query_names(objects), expected) def test_related_objects_include_hidden(self): result_key = 'get_all_related_objects_with_model_hidden_legacy' for model, expected in TEST_RESULTS[result_key].items(): objects = model._meta.get_all_related_objects_with_model(include_hidden=True) self.assertEqual( sorted(self._map_names(objects), key=self.key_name), sorted(expected, key=self.key_name) ) def test_related_objects_include_hidden_local_only(self): result_key = 'get_all_related_objects_with_model_hidden_local_legacy' for model, expected in TEST_RESULTS[result_key].items(): objects = model._meta.get_all_related_objects_with_model( include_hidden=True, local_only=True) self.assertEqual( sorted(self._map_names(objects), key=self.key_name), sorted(expected, key=self.key_name) ) def test_related_objects_proxy(self): result_key = 'get_all_related_objects_with_model_proxy_legacy' for model, expected in TEST_RESULTS[result_key].items(): objects = model._meta.get_all_related_objects_with_model( include_proxy_eq=True) self.assertEqual(self._map_related_query_names(objects), expected) def test_related_objects_proxy_hidden(self): result_key = 'get_all_related_objects_with_model_proxy_hidden_legacy' for model, expected in TEST_RESULTS[result_key].items(): objects = model._meta.get_all_related_objects_with_model( include_proxy_eq=True, include_hidden=True) self.assertEqual( sorted(self._map_names(objects), key=self.key_name), sorted(expected, key=self.key_name) ) @test.ignore_warnings(category=RemovedInDjango110Warning) class RelatedM2MTests(OptionsBaseTests): def test_related_m2m_with_model(self): result_key = 'get_all_related_many_to_many_with_model_legacy' for model, expected in TEST_RESULTS[result_key].items(): objects = model._meta.get_all_related_m2m_objects_with_model() self.assertEqual(self._map_related_query_names(objects), expected) def test_related_m2m_local_only(self): result_key = 'get_all_related_many_to_many_local_legacy' for model, expected in TEST_RESULTS[result_key].items(): objects = model._meta.get_all_related_many_to_many_objects(local_only=True) self.assertEqual([o.field.related_query_name() for o in objects], expected) def test_related_m2m_asymmetrical(self): m2m = Person._meta.many_to_many self.assertTrue('following_base' in [f.attname for f in m2m]) related_m2m = Person._meta.get_all_related_many_to_many_objects() self.assertTrue('followers_base' in [o.field.related_query_name() for o in related_m2m]) def test_related_m2m_symmetrical(self): m2m = Person._meta.many_to_many self.assertTrue('friends_base' in [f.attname for f in m2m]) related_m2m = Person._meta.get_all_related_many_to_many_objects() self.assertIn('friends_inherited_rel_+', [o.field.related_query_name() for o in related_m2m]) @test.ignore_warnings(category=RemovedInDjango110Warning) class GetFieldByNameTests(OptionsBaseTests): def test_get_data_field(self): field_info = Person._meta.get_field_by_name('data_abstract') self.assertEqual(field_info[1:], (BasePerson, True, False)) self.assertIsInstance(field_info[0], CharField) def test_get_m2m_field(self): field_info = Person._meta.get_field_by_name('m2m_base') self.assertEqual(field_info[1:], (BasePerson, True, True)) self.assertIsInstance(field_info[0], related.ManyToManyField) def test_get_related_object(self): field_info = Person._meta.get_field_by_name('relating_baseperson') self.assertEqual(field_info[1:], (BasePerson, False, False)) self.assertTrue(field_info[0].auto_created) def test_get_related_m2m(self): field_info = Person._meta.get_field_by_name('relating_people') self.assertEqual(field_info[1:], (None, False, True)) self.assertTrue(field_info[0].auto_created) def test_get_generic_relation(self): field_info = Person._meta.get_field_by_name('generic_relation_base') self.assertEqual(field_info[1:], (None, True, False)) self.assertIsInstance(field_info[0], GenericRelation) def test_get_m2m_field_invalid(self): with warnings.catch_warnings(record=True) as warning: warnings.simplefilter("always") self.assertRaises( FieldDoesNotExist, Person._meta.get_field, **{'field_name': 'm2m_base', 'many_to_many': False} ) self.assertEqual(Person._meta.get_field('m2m_base', many_to_many=True).name, 'm2m_base') # 2 RemovedInDjango110Warning messages should be raised, one for each call of get_field() # with the 'many_to_many' argument. self.assertEqual( [RemovedInDjango110Warning, RemovedInDjango110Warning], [w.message.__class__ for w in warning] ) @test.ignore_warnings(category=RemovedInDjango110Warning) class GetAllFieldNamesTestCase(OptionsBaseTests): def test_get_all_field_names(self): for model, expected_names in TEST_RESULTS['get_all_field_names'].items(): objects = model._meta.get_all_field_names() self.assertEqual(sorted(map(str, objects)), sorted(expected_names))