diff --git a/django/contrib/admin/checks.py b/django/contrib/admin/checks.py
index d8f7fe57e43..8788fd108d8 100644
--- a/django/contrib/admin/checks.py
+++ b/django/contrib/admin/checks.py
@@ -180,7 +180,7 @@ class BaseModelAdminChecks(object):
return []
else:
if (isinstance(field, models.ManyToManyField) and
- not field.rel.through._meta.auto_created):
+ not field.remote_field.through._meta.auto_created):
return [
checks.Error(
("The value of '%s' cannot include the ManyToManyField '%s', "
diff --git a/django/contrib/admin/filters.py b/django/contrib/admin/filters.py
index 863da768181..80ba2eebc6e 100644
--- a/django/contrib/admin/filters.py
+++ b/django/contrib/admin/filters.py
@@ -9,12 +9,10 @@ import datetime
from django.contrib.admin.options import IncorrectLookupParameters
from django.contrib.admin.utils import (
- get_limit_choices_to_from_path, get_model_from_relation,
- prepare_lookup_value, reverse_field_path,
+ get_model_from_relation, prepare_lookup_value, reverse_field_path,
)
from django.core.exceptions import ImproperlyConfigured, ValidationError
from django.db import models
-from django.db.models.fields.related import ForeignObjectRel, ManyToManyField
from django.utils import timezone
from django.utils.encoding import force_text, smart_text
from django.utils.translation import ugettext_lazy as _
@@ -164,11 +162,7 @@ class FieldListFilter(ListFilter):
class RelatedFieldListFilter(FieldListFilter):
def __init__(self, field, request, params, model, model_admin, field_path):
other_model = get_model_from_relation(field)
- if hasattr(field, 'rel'):
- rel_name = field.rel.get_related_field().name
- else:
- rel_name = other_model._meta.pk.name
- self.lookup_kwarg = '%s__%s__exact' % (field_path, rel_name)
+ self.lookup_kwarg = '%s__%s__exact' % (field_path, field.target_field.name)
self.lookup_kwarg_isnull = '%s__isnull' % field_path
self.lookup_val = request.GET.get(self.lookup_kwarg)
self.lookup_val_isnull = request.GET.get(self.lookup_kwarg_isnull)
@@ -182,9 +176,7 @@ class RelatedFieldListFilter(FieldListFilter):
self.title = self.lookup_title
def has_output(self):
- if (isinstance(self.field, ForeignObjectRel) and
- self.field.field.null or hasattr(self.field, 'rel') and
- self.field.null):
+ if self.field.null:
extra = 1
else:
extra = 0
@@ -212,9 +204,7 @@ class RelatedFieldListFilter(FieldListFilter):
}, [self.lookup_kwarg_isnull]),
'display': val,
}
- if (isinstance(self.field, ForeignObjectRel) and
- (self.field.field.null or isinstance(self.field.field, ManyToManyField)) or
- hasattr(self.field, 'rel') and (self.field.null or isinstance(self.field, ManyToManyField))):
+ if self.field.null:
yield {
'selected': bool(self.lookup_val_isnull),
'query_string': cl.get_query_string({
@@ -223,9 +213,7 @@ class RelatedFieldListFilter(FieldListFilter):
'display': EMPTY_CHANGELIST_VALUE,
}
-FieldListFilter.register(lambda f: (
- bool(f.rel) if hasattr(f, 'rel') else
- isinstance(f, ForeignObjectRel)), RelatedFieldListFilter)
+FieldListFilter.register(lambda f: f.remote_field, RelatedFieldListFilter)
class BooleanFieldListFilter(FieldListFilter):
@@ -371,13 +359,6 @@ class AllValuesFieldListFilter(FieldListFilter):
queryset = model_admin.get_queryset(request)
else:
queryset = parent_model._default_manager.all()
-
- # optional feature: limit choices base on existing relationships
- # queryset = queryset.complex_filter(
- # {'%s__isnull' % reverse_path: False})
- limit_choices_to = get_limit_choices_to_from_path(model, field_path)
- queryset = queryset.filter(limit_choices_to)
-
self.lookup_choices = (queryset
.distinct()
.order_by(field.name)
diff --git a/django/contrib/admin/helpers.py b/django/contrib/admin/helpers.py
index 07ea9a48139..b44d4118285 100644
--- a/django/contrib/admin/helpers.py
+++ b/django/contrib/admin/helpers.py
@@ -203,7 +203,7 @@ class AdminReadonlyField(object):
else:
result_repr = linebreaksbr(result_repr)
else:
- if isinstance(f.rel, ManyToManyRel) and value is not None:
+ if isinstance(f.remote_field, ManyToManyRel) and value is not None:
result_repr = ", ".join(map(six.text_type, value.all()))
else:
result_repr = display_for_field(value, f)
diff --git a/django/contrib/admin/options.py b/django/contrib/admin/options.py
index 855f605d134..b85f0fdeac4 100644
--- a/django/contrib/admin/options.py
+++ b/django/contrib/admin/options.py
@@ -26,8 +26,6 @@ from django.core.urlresolvers import reverse
from django.db import models, router, transaction
from django.db.models.constants import LOOKUP_SEP
from django.db.models.fields import BLANK_CHOICE_DASH
-from django.db.models.fields.related import ForeignObjectRel
-from django.db.models.sql.constants import QUERY_TERMS
from django.forms.formsets import DELETION_FIELD_NAME, all_valid
from django.forms.models import (
BaseInlineFormSet, inlineformset_factory, modelform_defines_fields,
@@ -154,7 +152,7 @@ class BaseModelAdmin(six.with_metaclass(forms.MediaDefiningClass)):
# rendered output. formfield can be None if it came from a
# OneToOneField with parent_link=True or a M2M intermediary.
if formfield and db_field.name not in self.raw_id_fields:
- related_modeladmin = self.admin_site._registry.get(db_field.rel.to)
+ related_modeladmin = self.admin_site._registry.get(db_field.remote_field.model)
wrapper_kwargs = {}
if related_modeladmin:
wrapper_kwargs.update(
@@ -163,7 +161,7 @@ class BaseModelAdmin(six.with_metaclass(forms.MediaDefiningClass)):
can_delete_related=related_modeladmin.has_delete_permission(request),
)
formfield.widget = widgets.RelatedFieldWidgetWrapper(
- formfield.widget, db_field.rel, self.admin_site, **wrapper_kwargs
+ formfield.widget, db_field.remote_field, self.admin_site, **wrapper_kwargs
)
return formfield
@@ -202,11 +200,11 @@ class BaseModelAdmin(six.with_metaclass(forms.MediaDefiningClass)):
ordering. Otherwise don't specify the queryset, let the field decide
(returns None in that case).
"""
- related_admin = self.admin_site._registry.get(db_field.rel.to, None)
+ related_admin = self.admin_site._registry.get(db_field.remote_field.model, None)
if related_admin is not None:
ordering = related_admin.get_ordering(request)
if ordering is not None and ordering != ():
- return db_field.rel.to._default_manager.using(db).order_by(*ordering)
+ return db_field.remote_field.model._default_manager.using(db).order_by(*ordering)
return None
def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
@@ -215,7 +213,7 @@ class BaseModelAdmin(six.with_metaclass(forms.MediaDefiningClass)):
"""
db = kwargs.get('using')
if db_field.name in self.raw_id_fields:
- kwargs['widget'] = widgets.ForeignKeyRawIdWidget(db_field.rel,
+ kwargs['widget'] = widgets.ForeignKeyRawIdWidget(db_field.remote_field,
self.admin_site, using=db)
elif db_field.name in self.radio_fields:
kwargs['widget'] = widgets.AdminRadioSelect(attrs={
@@ -236,12 +234,12 @@ class BaseModelAdmin(six.with_metaclass(forms.MediaDefiningClass)):
"""
# If it uses an intermediary model that isn't auto created, don't show
# a field in admin.
- if not db_field.rel.through._meta.auto_created:
+ if not db_field.remote_field.through._meta.auto_created:
return None
db = kwargs.get('using')
if db_field.name in self.raw_id_fields:
- kwargs['widget'] = widgets.ManyToManyRawIdWidget(db_field.rel,
+ kwargs['widget'] = widgets.ManyToManyRawIdWidget(db_field.remote_field,
self.admin_site, using=db)
kwargs['help_text'] = ''
elif db_field.name in (list(self.filter_vertical) + list(self.filter_horizontal)):
@@ -334,46 +332,32 @@ class BaseModelAdmin(six.with_metaclass(forms.MediaDefiningClass)):
if k == lookup and v == value:
return True
- parts = lookup.split(LOOKUP_SEP)
-
- # Last term in lookup is a query term (__exact, __startswith etc)
- # This term can be ignored.
- if len(parts) > 1 and parts[-1] in QUERY_TERMS:
- parts.pop()
-
- # Special case -- foo__id__exact and foo__id queries are implied
- # if foo has been specifically included in the lookup list; so
- # drop __id if it is the last part. However, first we need to find
- # the pk attribute name.
- rel_name = None
- for part in parts[:-1]:
+ relation_parts = []
+ prev_field = None
+ for part in lookup.split(LOOKUP_SEP):
try:
field = model._meta.get_field(part)
except FieldDoesNotExist:
# Lookups on non-existent fields are ok, since they're ignored
# later.
- return True
- if hasattr(field, 'rel'):
- if field.rel is None:
- # This property or relation doesn't exist, but it's allowed
- # since it's ignored in ChangeList.get_filters().
- return True
- model = field.rel.to
- if hasattr(field.rel, 'get_related_field'):
- rel_name = field.rel.get_related_field().name
- else:
- rel_name = None
- elif isinstance(field, ForeignObjectRel):
- model = field.related_model
- rel_name = model._meta.pk.name
- else:
- rel_name = None
- if rel_name and len(parts) > 1 and parts[-1] == rel_name:
- parts.pop()
+ break
+ # It is allowed to filter on values that would be found from local
+ # model anyways. For example, if you filter on employee__department__id,
+ # then the id value would be found already from employee__department_id.
+ if not prev_field or (prev_field.concrete and
+ field not in prev_field.get_path_info()[-1].target_fields):
+ relation_parts.append(part)
+ if not getattr(field, 'get_path_info', None):
+ # This is not a relational field, so further parts
+ # must be transforms.
+ break
+ prev_field = field
+ model = field.get_path_info()[-1].to_opts.model
- if len(parts) == 1:
+ if len(relation_parts) <= 1:
+ # Either a local field filter, or no fields at all.
return True
- clean_lookup = LOOKUP_SEP.join(parts)
+ clean_lookup = LOOKUP_SEP.join(relation_parts)
valid_lookups = [self.date_hierarchy]
for filter_item in self.list_filter:
if isinstance(filter_item, type) and issubclass(filter_item, SimpleListFilter):
@@ -422,7 +406,7 @@ class BaseModelAdmin(six.with_metaclass(forms.MediaDefiningClass)):
for related_object in related_objects:
related_model = related_object.related_model
if (any(issubclass(model, related_model) for model in registered_models) and
- related_object.field.rel.get_related_field() == field):
+ related_object.field.remote_field.get_related_field() == field):
return True
return False
@@ -1882,8 +1866,8 @@ class InlineModelAdmin(BaseModelAdmin):
# The model was auto-created as intermediary for a
# ManyToMany-relationship, find the target model
for field in opts.fields:
- if field.rel and field.rel.to != self.parent_model:
- opts = field.rel.to._meta
+ if field.remote_field and field.remote_field.model != self.parent_model:
+ opts = field.remote_field.model._meta
break
codename = get_permission_codename('change', opts)
return request.user.has_perm("%s.%s" % (opts.app_label, codename))
diff --git a/django/contrib/admin/templatetags/admin_list.py b/django/contrib/admin/templatetags/admin_list.py
index 55fba4f90ce..affd5958009 100644
--- a/django/contrib/admin/templatetags/admin_list.py
+++ b/django/contrib/admin/templatetags/admin_list.py
@@ -215,7 +215,7 @@ def items_for_result(cl, result, form):
if isinstance(value, (datetime.date, datetime.time)):
row_classes.append('nowrap')
else:
- if isinstance(f.rel, models.ManyToOneRel):
+ if isinstance(f.remote_field, models.ManyToOneRel):
field_val = getattr(result, f.name)
if field_val is None:
result_repr = EMPTY_CHANGELIST_VALUE
diff --git a/django/contrib/admin/utils.py b/django/contrib/admin/utils.py
index 619e9ad796c..69465fa9272 100644
--- a/django/contrib/admin/utils.py
+++ b/django/contrib/admin/utils.py
@@ -446,7 +446,7 @@ def reverse_field_path(model, path):
# Field should point to another model
if field.is_relation and not (field.auto_created and not field.concrete):
related_name = field.related_query_name()
- parent = field.rel.to
+ parent = field.remote_field.model
else:
related_name = field.field.name
parent = field.related_model
@@ -481,23 +481,3 @@ def remove_trailing_data_field(fields):
except NotRelationField:
fields = fields[:-1]
return fields
-
-
-def get_limit_choices_to_from_path(model, path):
- """ Return Q object for limiting choices if applicable.
-
- If final model in path is linked via a ForeignKey or ManyToManyField which
- has a ``limit_choices_to`` attribute, return it as a Q object.
- """
- fields = get_fields_from_path(model, path)
- fields = remove_trailing_data_field(fields)
- get_limit_choices_to = (
- fields and hasattr(fields[-1], 'rel') and
- getattr(fields[-1].rel, 'get_limit_choices_to', None))
- if not get_limit_choices_to:
- return models.Q() # empty Q
- limit_choices_to = get_limit_choices_to()
- if isinstance(limit_choices_to, models.Q):
- return limit_choices_to # already a Q
- else:
- return models.Q(**limit_choices_to) # convert dict to Q
diff --git a/django/contrib/admin/views/main.py b/django/contrib/admin/views/main.py
index 42a2f485ca1..0b38f7b0278 100644
--- a/django/contrib/admin/views/main.py
+++ b/django/contrib/admin/views/main.py
@@ -383,7 +383,7 @@ class ChangeList(object):
except FieldDoesNotExist:
pass
else:
- if isinstance(field.rel, models.ManyToOneRel):
+ if isinstance(field.remote_field, models.ManyToOneRel):
return True
return False
diff --git a/django/contrib/admin/widgets.py b/django/contrib/admin/widgets.py
index 251cc0ef1c6..fd0021c0571 100644
--- a/django/contrib/admin/widgets.py
+++ b/django/contrib/admin/widgets.py
@@ -151,7 +151,7 @@ class ForeignKeyRawIdWidget(forms.TextInput):
super(ForeignKeyRawIdWidget, self).__init__(attrs)
def render(self, name, value, attrs=None):
- rel_to = self.rel.to
+ rel_to = self.rel.model
if attrs is None:
attrs = {}
extra = []
@@ -196,9 +196,9 @@ class ForeignKeyRawIdWidget(forms.TextInput):
def label_for_value(self, value):
key = self.rel.get_related_field().name
try:
- obj = self.rel.to._default_manager.using(self.db).get(**{key: value})
+ obj = self.rel.model._default_manager.using(self.db).get(**{key: value})
return ' %s' % escape(Truncator(obj).words(14, truncate='...'))
- except (ValueError, self.rel.to.DoesNotExist):
+ except (ValueError, self.rel.model.DoesNotExist):
return ''
@@ -210,7 +210,7 @@ class ManyToManyRawIdWidget(ForeignKeyRawIdWidget):
def render(self, name, value, attrs=None):
if attrs is None:
attrs = {}
- if self.rel.to in self.admin_site._registry:
+ if self.rel.model in self.admin_site._registry:
# The related object is registered with the same AdminSite
attrs['class'] = 'vManyToManyRawIdAdminField'
if value:
@@ -248,7 +248,7 @@ class RelatedFieldWidgetWrapper(forms.Widget):
# Backwards compatible check for whether a user can add related
# objects.
if can_add_related is None:
- can_add_related = rel.to in admin_site._registry
+ can_add_related = rel.model in admin_site._registry
self.can_add_related = can_add_related
# XXX: The UX does not support multiple selected values.
multiple = getattr(widget, 'allow_multiple_selected', False)
@@ -280,7 +280,7 @@ class RelatedFieldWidgetWrapper(forms.Widget):
def render(self, name, value, *args, **kwargs):
from django.contrib.admin.views.main import IS_POPUP_VAR, TO_FIELD_VAR
- rel_opts = self.rel.to._meta
+ rel_opts = self.rel.model._meta
info = (rel_opts.app_label, rel_opts.model_name)
self.widget.choices = self.choices
url_params = '&'.join("%s=%s" % param for param in [
diff --git a/django/contrib/admindocs/views.py b/django/contrib/admindocs/views.py
index 91ca143508a..245d0cf39e9 100644
--- a/django/contrib/admindocs/views.py
+++ b/django/contrib/admindocs/views.py
@@ -207,8 +207,8 @@ class ModelDetailView(BaseAdminDocsView):
# ForeignKey is a special case since the field will actually be a
# descriptor that returns the other object
if isinstance(field, models.ForeignKey):
- data_type = field.rel.to.__name__
- app_label = field.rel.to._meta.app_label
+ data_type = field.remote_field.model.__name__
+ app_label = field.remote_field.model._meta.app_label
verbose = utils.parse_rst(
(_("the related `%(app_label)s.%(data_type)s` object") % {
'app_label': app_label, 'data_type': data_type,
@@ -228,8 +228,8 @@ class ModelDetailView(BaseAdminDocsView):
# Gather many-to-many fields.
for field in opts.many_to_many:
- data_type = field.rel.to.__name__
- app_label = field.rel.to._meta.app_label
+ data_type = field.remote_field.model.__name__
+ app_label = field.remote_field.model._meta.app_label
verbose = _("related `%(app_label)s.%(object_name)s` objects") % {
'app_label': app_label,
'object_name': data_type,
diff --git a/django/contrib/auth/admin.py b/django/contrib/auth/admin.py
index eb865a1c933..33106709950 100644
--- a/django/contrib/auth/admin.py
+++ b/django/contrib/auth/admin.py
@@ -31,7 +31,7 @@ class GroupAdmin(admin.ModelAdmin):
def formfield_for_manytomany(self, db_field, request=None, **kwargs):
if db_field.name == 'permissions':
- qs = kwargs.get('queryset', db_field.rel.to.objects)
+ qs = kwargs.get('queryset', db_field.remote_field.model.objects)
# Avoid a major performance hit resolving permission names which
# triggers a content_type load:
kwargs['queryset'] = qs.select_related('content_type')
diff --git a/django/contrib/auth/management/commands/createsuperuser.py b/django/contrib/auth/management/commands/createsuperuser.py
index 65ad94b6949..c1251b32d62 100644
--- a/django/contrib/auth/management/commands/createsuperuser.py
+++ b/django/contrib/auth/management/commands/createsuperuser.py
@@ -90,11 +90,11 @@ class Command(BaseCommand):
input_msg = capfirst(verbose_field_name)
if default_username:
input_msg += " (leave blank to use '%s')" % default_username
- username_rel = self.username_field.rel
+ username_rel = self.username_field.remote_field
input_msg = force_str('%s%s: ' % (
input_msg,
' (%s.%s)' % (
- username_rel.to._meta.object_name,
+ username_rel.model._meta.object_name,
username_rel.field_name
) if username_rel else '')
)
@@ -114,8 +114,13 @@ class Command(BaseCommand):
field = self.UserModel._meta.get_field(field_name)
user_data[field_name] = options.get(field_name)
while user_data[field_name] is None:
- message = force_str('%s%s: ' % (capfirst(field.verbose_name),
- ' (%s.%s)' % (field.rel.to._meta.object_name, field.rel.field_name) if field.rel else ''))
+ message = force_str('%s%s: ' % (
+ capfirst(field.verbose_name),
+ ' (%s.%s)' % (
+ field.remote_field.model._meta.object_name,
+ field.remote_field.field_name,
+ ) if field.remote_field else '',
+ ))
user_data[field_name] = self.get_input_data(field, message)
# Get a password
diff --git a/django/contrib/contenttypes/fields.py b/django/contrib/contenttypes/fields.py
index f72cd49b263..f5c1804a35a 100644
--- a/django/contrib/contenttypes/fields.py
+++ b/django/contrib/contenttypes/fields.py
@@ -39,6 +39,7 @@ class GenericForeignKey(object):
one_to_many = False
one_to_one = False
related_model = None
+ remote_field = None
allow_unsaved_instance_assignment = False
@@ -135,7 +136,7 @@ class GenericForeignKey(object):
id='contenttypes.E003',
)
]
- elif field.rel.to != ContentType:
+ elif field.remote_field.model != ContentType:
return [
checks.Error(
"'%s.%s' is not a ForeignKey to 'contenttypes.ContentType'." % (
@@ -323,7 +324,7 @@ class GenericRelation(ForeignObject):
return errors
def _check_generic_foreign_key_existence(self):
- target = self.rel.to
+ target = self.remote_field.model
if isinstance(target, ModelBase):
fields = target._meta.virtual_fields
if any(isinstance(field, GenericForeignKey) and
@@ -348,16 +349,16 @@ class GenericRelation(ForeignObject):
def resolve_related_fields(self):
self.to_fields = [self.model._meta.pk.name]
- return [(self.rel.to._meta.get_field(self.object_id_field_name), self.model._meta.pk)]
+ return [(self.remote_field.model._meta.get_field(self.object_id_field_name), self.model._meta.pk)]
def get_path_info(self):
- opts = self.rel.to._meta
+ opts = self.remote_field.model._meta
target = opts.pk
- return [PathInfo(self.model._meta, opts, (target,), self.rel, True, False)]
+ return [PathInfo(self.model._meta, opts, (target,), self.remote_field, True, False)]
def get_reverse_path_info(self):
opts = self.model._meta
- from_opts = self.rel.to._meta
+ from_opts = self.remote_field.model._meta
return [PathInfo(from_opts, opts, (opts.pk,), self, not self.unique, False)]
def get_choices_default(self):
@@ -371,7 +372,7 @@ class GenericRelation(ForeignObject):
kwargs['virtual_only'] = True
super(GenericRelation, self).contribute_to_class(cls, name, **kwargs)
self.model = cls
- setattr(cls, self.name, ReverseGenericRelatedObjectsDescriptor(self.rel))
+ setattr(cls, self.name, ReverseGenericRelatedObjectsDescriptor(self.remote_field))
def set_attributes_from_rel(self):
pass
@@ -387,7 +388,7 @@ class GenericRelation(ForeignObject):
for_concrete_model=self.for_concrete_model)
def get_extra_restriction(self, where_class, alias, remote_alias):
- field = self.rel.to._meta.get_field(self.content_type_field_name)
+ field = self.remote_field.model._meta.get_field(self.content_type_field_name)
contenttype_pk = self.get_content_type().pk
cond = where_class()
lookup = field.get_lookup('exact')(field.get_col(remote_alias), contenttype_pk)
@@ -398,7 +399,7 @@ class GenericRelation(ForeignObject):
"""
Return all objects related to ``objs`` via this ``GenericRelation``.
"""
- return self.rel.to._base_manager.db_manager(using).filter(**{
+ return self.remote_field.model._base_manager.db_manager(using).filter(**{
"%s__pk" % self.content_type_field_name: ContentType.objects.db_manager(using).get_for_model(
self.model, for_concrete_model=self.for_concrete_model).pk,
"%s__in" % self.object_id_field_name: [obj.pk for obj in objs]
@@ -421,7 +422,7 @@ class ReverseGenericRelatedObjectsDescriptor(ForeignRelatedObjectsDescriptor):
@cached_property
def related_manager_cls(self):
return create_generic_related_manager(
- self.rel.to._default_manager.__class__,
+ self.rel.model._default_manager.__class__,
self.rel,
)
@@ -439,7 +440,7 @@ def create_generic_related_manager(superclass, rel):
self.instance = instance
- self.model = rel.to
+ self.model = rel.model
content_type = ContentType.objects.db_manager(instance._state.db).get_for_model(
instance, for_concrete_model=rel.field.for_concrete_model)
diff --git a/django/contrib/contenttypes/forms.py b/django/contrib/contenttypes/forms.py
index e590c3db05f..b862735adec 100644
--- a/django/contrib/contenttypes/forms.py
+++ b/django/contrib/contenttypes/forms.py
@@ -68,7 +68,7 @@ def generic_inlineformset_factory(model, form=ModelForm,
opts = model._meta
# if there is no field called `ct_field` let the exception propagate
ct_field = opts.get_field(ct_field)
- if not isinstance(ct_field, models.ForeignKey) or ct_field.rel.to != ContentType:
+ if not isinstance(ct_field, models.ForeignKey) or ct_field.remote_field.model != ContentType:
raise Exception("fk_name '%s' is not a ForeignKey to ContentType" % ct_field)
fk_field = opts.get_field(fk_field) # let the exception propagate
if exclude is not None:
diff --git a/django/contrib/contenttypes/views.py b/django/contrib/contenttypes/views.py
index 02446d023a2..e99459e4f2a 100644
--- a/django/contrib/contenttypes/views.py
+++ b/django/contrib/contenttypes/views.py
@@ -48,7 +48,7 @@ def shortcut(request, content_type_id, object_id):
# First, look for an many-to-many relationship to Site.
for field in opts.many_to_many:
- if field.rel.to is Site:
+ if field.remote_field.model is Site:
try:
# Caveat: In the case of multiple related Sites, this just
# selects the *first* one, which is arbitrary.
@@ -61,7 +61,7 @@ def shortcut(request, content_type_id, object_id):
# Next, look for a many-to-one relationship to Site.
if object_domain is None:
for field in obj._meta.fields:
- if field.rel and field.rel.to is Site:
+ if field.remote_field and field.remote_field.model is Site:
try:
object_domain = getattr(obj, field.name).domain
except Site.DoesNotExist:
diff --git a/django/contrib/gis/db/models/lookups.py b/django/contrib/gis/db/models/lookups.py
index 30b5c0435de..46104a2fa3a 100644
--- a/django/contrib/gis/db/models/lookups.py
+++ b/django/contrib/gis/db/models/lookups.py
@@ -45,7 +45,7 @@ class GISLookup(Lookup):
# model field associated with the next field in the list
# until there's no more left.
while len(field_list):
- opts = geo_fld.rel.to._meta
+ opts = geo_fld.remote_field.model._meta
geo_fld = opts.get_field(field_list.pop())
except (FieldDoesNotExist, AttributeError):
return False
diff --git a/django/contrib/gis/utils/layermapping.py b/django/contrib/gis/utils/layermapping.py
index c9badc75220..1f7de823b75 100644
--- a/django/contrib/gis/utils/layermapping.py
+++ b/django/contrib/gis/utils/layermapping.py
@@ -229,7 +229,7 @@ class LayerMapping(object):
elif isinstance(model_field, models.ForeignKey):
if isinstance(ogr_name, dict):
# Is every given related model mapping field in the Layer?
- rel_model = model_field.rel.to
+ rel_model = model_field.remote_field.model
for rel_name, ogr_field in ogr_name.items():
idx = check_ogr_fld(ogr_field)
try:
diff --git a/django/contrib/postgres/fields/array.py b/django/contrib/postgres/fields/array.py
index 970355fd622..9da7ec4cb75 100644
--- a/django/contrib/postgres/fields/array.py
+++ b/django/contrib/postgres/fields/array.py
@@ -33,7 +33,7 @@ class ArrayField(Field):
def check(self, **kwargs):
errors = super(ArrayField, self).check(**kwargs)
- if self.base_field.rel:
+ if self.base_field.remote_field:
errors.append(
checks.Error(
'Base field for array cannot be a related field.',
diff --git a/django/core/serializers/__init__.py b/django/core/serializers/__init__.py
index 5d049a4f918..19216bdfa70 100644
--- a/django/core/serializers/__init__.py
+++ b/django/core/serializers/__init__.py
@@ -184,16 +184,16 @@ def sort_dependencies(app_list):
# Now add a dependency for any FK relation with a model that
# defines a natural key
for field in model._meta.fields:
- if hasattr(field.rel, 'to'):
- rel_model = field.rel.to
+ if field.remote_field:
+ rel_model = field.remote_field.model
if hasattr(rel_model, 'natural_key') and rel_model != model:
deps.append(rel_model)
# Also add a dependency for any simple M2M relation with a model
# that defines a natural key. M2M relations with explicit through
# models don't count as dependencies.
for field in model._meta.many_to_many:
- if field.rel.through._meta.auto_created:
- rel_model = field.rel.to
+ if field.remote_field.through._meta.auto_created:
+ rel_model = field.remote_field.model
if hasattr(rel_model, 'natural_key') and rel_model != model:
deps.append(rel_model)
model_dependencies.append((model, deps))
diff --git a/django/core/serializers/base.py b/django/core/serializers/base.py
index 7f00e7147d0..2c18d7b477a 100644
--- a/django/core/serializers/base.py
+++ b/django/core/serializers/base.py
@@ -49,7 +49,7 @@ class Serializer(object):
concrete_model = obj._meta.concrete_model
for field in concrete_model._meta.local_fields:
if field.serialize:
- if field.rel is None:
+ if field.remote_field is None:
if self.selected_fields is None or field.attname in self.selected_fields:
self.handle_field(obj, field)
else:
diff --git a/django/core/serializers/python.py b/django/core/serializers/python.py
index 0b18f918584..7caff520669 100644
--- a/django/core/serializers/python.py
+++ b/django/core/serializers/python.py
@@ -55,7 +55,7 @@ class Serializer(base.Serializer):
self._current[field.name] = field.value_to_string(obj)
def handle_fk_field(self, obj, field):
- if self.use_natural_foreign_keys and hasattr(field.rel.to, 'natural_key'):
+ if self.use_natural_foreign_keys and hasattr(field.remote_field.model, 'natural_key'):
related = getattr(obj, field.name)
if related:
value = related.natural_key()
@@ -68,8 +68,8 @@ class Serializer(base.Serializer):
self._current[field.name] = value
def handle_m2m_field(self, obj, field):
- if field.rel.through._meta.auto_created:
- if self.use_natural_foreign_keys and hasattr(field.rel.to, 'natural_key'):
+ if field.remote_field.through._meta.auto_created:
+ if self.use_natural_foreign_keys and hasattr(field.remote_field.model, 'natural_key'):
m2m_value = lambda value: value.natural_key()
else:
m2m_value = lambda value: force_text(value._get_pk_val(), strings_only=True)
@@ -120,33 +120,33 @@ def Deserializer(object_list, **options):
field = Model._meta.get_field(field_name)
# Handle M2M relations
- if field.rel and isinstance(field.rel, models.ManyToManyRel):
- if hasattr(field.rel.to._default_manager, 'get_by_natural_key'):
+ if field.remote_field and isinstance(field.remote_field, models.ManyToManyRel):
+ if hasattr(field.remote_field.model._default_manager, 'get_by_natural_key'):
def m2m_convert(value):
if hasattr(value, '__iter__') and not isinstance(value, six.text_type):
- return field.rel.to._default_manager.db_manager(db).get_by_natural_key(*value).pk
+ return field.remote_field.model._default_manager.db_manager(db).get_by_natural_key(*value).pk
else:
- return force_text(field.rel.to._meta.pk.to_python(value), strings_only=True)
+ return force_text(field.remote_field.model._meta.pk.to_python(value), strings_only=True)
else:
- m2m_convert = lambda v: force_text(field.rel.to._meta.pk.to_python(v), strings_only=True)
+ m2m_convert = lambda v: force_text(field.remote_field.model._meta.pk.to_python(v), strings_only=True)
m2m_data[field.name] = [m2m_convert(pk) for pk in field_value]
# Handle FK fields
- elif field.rel and isinstance(field.rel, models.ManyToOneRel):
+ elif field.remote_field and isinstance(field.remote_field, models.ManyToOneRel):
if field_value is not None:
- if hasattr(field.rel.to._default_manager, 'get_by_natural_key'):
+ if hasattr(field.remote_field.model._default_manager, 'get_by_natural_key'):
if hasattr(field_value, '__iter__') and not isinstance(field_value, six.text_type):
- obj = field.rel.to._default_manager.db_manager(db).get_by_natural_key(*field_value)
- value = getattr(obj, field.rel.field_name)
+ obj = field.remote_field.model._default_manager.db_manager(db).get_by_natural_key(*field_value)
+ value = getattr(obj, field.remote_field.field_name)
# If this is a natural foreign key to an object that
# has a FK/O2O as the foreign key, use the FK value
- if field.rel.to._meta.pk.rel:
+ if field.remote_field.model._meta.pk.remote_field:
value = value.pk
else:
- value = field.rel.to._meta.get_field(field.rel.field_name).to_python(field_value)
+ value = field.remote_field.model._meta.get_field(field.remote_field.field_name).to_python(field_value)
data[field.attname] = value
else:
- data[field.attname] = field.rel.to._meta.get_field(field.rel.field_name).to_python(field_value)
+ data[field.attname] = field.remote_field.model._meta.get_field(field.remote_field.field_name).to_python(field_value)
else:
data[field.attname] = None
diff --git a/django/core/serializers/xml_serializer.py b/django/core/serializers/xml_serializer.py
index 73bed113b90..e22be93f01d 100644
--- a/django/core/serializers/xml_serializer.py
+++ b/django/core/serializers/xml_serializer.py
@@ -91,7 +91,7 @@ class Serializer(base.Serializer):
self._start_relational_field(field)
related_att = getattr(obj, field.get_attname())
if related_att is not None:
- if self.use_natural_foreign_keys and hasattr(field.rel.to, 'natural_key'):
+ if self.use_natural_foreign_keys and hasattr(field.remote_field.model, 'natural_key'):
related = getattr(obj, field.name)
# If related object has a natural key, use it
related = related.natural_key()
@@ -112,9 +112,9 @@ class Serializer(base.Serializer):
serialized as references to the object's PK (i.e. the related *data*
is not dumped, just the relation).
"""
- if field.rel.through._meta.auto_created:
+ if field.remote_field.through._meta.auto_created:
self._start_relational_field(field)
- if self.use_natural_foreign_keys and hasattr(field.rel.to, 'natural_key'):
+ if self.use_natural_foreign_keys and hasattr(field.remote_field.model, 'natural_key'):
# If the objects in the m2m have a natural key, use it
def handle_m2m(value):
natural = value.natural_key()
@@ -142,8 +142,8 @@ class Serializer(base.Serializer):
self.indent(2)
self.xml.startElement("field", {
"name": field.name,
- "rel": field.rel.__class__.__name__,
- "to": smart_text(field.rel.to._meta),
+ "rel": field.remote_field.__class__.__name__,
+ "to": smart_text(field.remote_field.model._meta),
})
@@ -204,9 +204,9 @@ class Deserializer(base.Deserializer):
field = Model._meta.get_field(field_name)
# As is usually the case, relation fields get the special treatment.
- if field.rel and isinstance(field.rel, models.ManyToManyRel):
+ if field.remote_field and isinstance(field.remote_field, models.ManyToManyRel):
m2m_data[field.name] = self._handle_m2m_field_node(field_node, field)
- elif field.rel and isinstance(field.rel, models.ManyToOneRel):
+ elif field.remote_field and isinstance(field.remote_field, models.ManyToOneRel):
data[field.attname] = self._handle_fk_field_node(field_node, field)
else:
if field_node.getElementsByTagName('None'):
@@ -228,43 +228,43 @@ class Deserializer(base.Deserializer):
if node.getElementsByTagName('None'):
return None
else:
- if hasattr(field.rel.to._default_manager, 'get_by_natural_key'):
+ if hasattr(field.remote_field.model._default_manager, 'get_by_natural_key'):
keys = node.getElementsByTagName('natural')
if keys:
# If there are 'natural' subelements, it must be a natural key
field_value = [getInnerText(k).strip() for k in keys]
- obj = field.rel.to._default_manager.db_manager(self.db).get_by_natural_key(*field_value)
- obj_pk = getattr(obj, field.rel.field_name)
+ obj = field.remote_field.model._default_manager.db_manager(self.db).get_by_natural_key(*field_value)
+ obj_pk = getattr(obj, field.remote_field.field_name)
# If this is a natural foreign key to an object that
# has a FK/O2O as the foreign key, use the FK value
- if field.rel.to._meta.pk.rel:
+ if field.remote_field.model._meta.pk.remote_field:
obj_pk = obj_pk.pk
else:
# Otherwise, treat like a normal PK
field_value = getInnerText(node).strip()
- obj_pk = field.rel.to._meta.get_field(field.rel.field_name).to_python(field_value)
+ obj_pk = field.remote_field.model._meta.get_field(field.remote_field.field_name).to_python(field_value)
return obj_pk
else:
field_value = getInnerText(node).strip()
- return field.rel.to._meta.get_field(field.rel.field_name).to_python(field_value)
+ return field.remote_field.model._meta.get_field(field.remote_field.field_name).to_python(field_value)
def _handle_m2m_field_node(self, node, field):
"""
Handle a node for a ManyToManyField.
"""
- if hasattr(field.rel.to._default_manager, 'get_by_natural_key'):
+ if hasattr(field.remote_field.model._default_manager, 'get_by_natural_key'):
def m2m_convert(n):
keys = n.getElementsByTagName('natural')
if keys:
# If there are 'natural' subelements, it must be a natural key
field_value = [getInnerText(k).strip() for k in keys]
- obj_pk = field.rel.to._default_manager.db_manager(self.db).get_by_natural_key(*field_value).pk
+ obj_pk = field.remote_field.model._default_manager.db_manager(self.db).get_by_natural_key(*field_value).pk
else:
# Otherwise, treat like a normal PK value.
- obj_pk = field.rel.to._meta.pk.to_python(n.getAttribute('pk'))
+ obj_pk = field.remote_field.model._meta.pk.to_python(n.getAttribute('pk'))
return obj_pk
else:
- m2m_convert = lambda n: field.rel.to._meta.pk.to_python(n.getAttribute('pk'))
+ m2m_convert = lambda n: field.remote_field.model._meta.pk.to_python(n.getAttribute('pk'))
return [m2m_convert(c) for c in node.getElementsByTagName("object")]
def _get_model_from_node(self, node, attr):
diff --git a/django/db/backends/base/introspection.py b/django/db/backends/base/introspection.py
index 5f828b94ccf..762cca441ff 100644
--- a/django/db/backends/base/introspection.py
+++ b/django/db/backends/base/introspection.py
@@ -125,7 +125,7 @@ class BaseDatabaseIntrospection(object):
for f in model._meta.local_many_to_many:
# If this is an m2m using an intermediate table,
# we don't need to reset the sequence.
- if f.rel.through is None:
+ if f.remote_field.through is None:
sequence_list.append({'table': f.m2m_db_table(), 'column': None})
return sequence_list
diff --git a/django/db/backends/base/schema.py b/django/db/backends/base/schema.py
index 167c95dbd04..5c3bf8baee1 100644
--- a/django/db/backends/base/schema.py
+++ b/django/db/backends/base/schema.py
@@ -241,9 +241,9 @@ class BaseDatabaseSchemaEditor(object):
definition += " %s" % col_type_suffix
params.extend(extra_params)
# FK
- if field.rel and field.db_constraint:
- to_table = field.rel.to._meta.db_table
- to_column = field.rel.to._meta.get_field(field.rel.field_name).column
+ if field.remote_field and field.db_constraint:
+ to_table = field.remote_field.model._meta.db_table
+ to_column = field.remote_field.model._meta.get_field(field.remote_field.field_name).column
if self.connection.features.supports_foreign_keys:
self.deferred_sql.append(self._create_fk_sql(model, field, "_fk_%(to_table)s_%(to_column)s"))
elif self.sql_create_inline_fk:
@@ -284,8 +284,8 @@ class BaseDatabaseSchemaEditor(object):
# Make M2M tables
for field in model._meta.local_many_to_many:
- if field.rel.through._meta.auto_created:
- self.create_model(field.rel.through)
+ if field.remote_field.through._meta.auto_created:
+ self.create_model(field.remote_field.through)
def delete_model(self, model):
"""
@@ -293,8 +293,8 @@ class BaseDatabaseSchemaEditor(object):
"""
# Handle auto-created intermediary models
for field in model._meta.local_many_to_many:
- if field.rel.through._meta.auto_created:
- self.delete_model(field.rel.through)
+ if field.remote_field.through._meta.auto_created:
+ self.delete_model(field.remote_field.through)
# Delete the table
self.execute(self.sql_delete_table % {
@@ -377,8 +377,8 @@ class BaseDatabaseSchemaEditor(object):
table instead (for M2M fields)
"""
# Special-case implicit M2M tables
- if field.many_to_many and field.rel.through._meta.auto_created:
- return self.create_model(field.rel.through)
+ if field.many_to_many and field.remote_field.through._meta.auto_created:
+ return self.create_model(field.remote_field.through)
# Get the column's definition
definition, params = self.column_sql(model, field, include_default=True)
# It might not actually have a column behind it
@@ -409,7 +409,7 @@ class BaseDatabaseSchemaEditor(object):
if field.db_index and not field.unique:
self.deferred_sql.append(self._create_index_sql(model, [field]))
# Add any FK constraints later
- if field.rel and self.connection.features.supports_foreign_keys and field.db_constraint:
+ if field.remote_field and self.connection.features.supports_foreign_keys and field.db_constraint:
self.deferred_sql.append(self._create_fk_sql(model, field, "_fk_%(to_table)s_%(to_column)s"))
# Reset connection if required
if self.connection.features.connection_persists_old_columns:
@@ -421,13 +421,13 @@ class BaseDatabaseSchemaEditor(object):
but for M2Ms may involve deleting a table.
"""
# Special-case implicit M2M tables
- if field.many_to_many and field.rel.through._meta.auto_created:
- return self.delete_model(field.rel.through)
+ if field.many_to_many and field.remote_field.through._meta.auto_created:
+ return self.delete_model(field.remote_field.through)
# It might not actually have a column behind it
if field.db_parameters(connection=self.connection)['type'] is None:
return
# Drop any FK constraints, MySQL requires explicit deletion
- if field.rel:
+ if field.remote_field:
fk_names = self._constraint_names(model, [field.column], foreign_key=True)
for fk_name in fk_names:
self.execute(self._delete_constraint_sql(self.sql_delete_fk, model, fk_name))
@@ -454,21 +454,21 @@ class BaseDatabaseSchemaEditor(object):
old_type = old_db_params['type']
new_db_params = new_field.db_parameters(connection=self.connection)
new_type = new_db_params['type']
- if (old_type is None and old_field.rel is None) or (new_type is None and new_field.rel is None):
+ if (old_type is None and old_field.remote_field is None) or (new_type is None and new_field.remote_field is None):
raise ValueError(
"Cannot alter field %s into %s - they do not properly define "
"db_type (are you using PostGIS 1.5 or badly-written custom "
"fields?)" % (old_field, new_field),
)
elif old_type is None and new_type is None and (
- old_field.rel.through and new_field.rel.through and
- old_field.rel.through._meta.auto_created and
- new_field.rel.through._meta.auto_created):
+ old_field.remote_field.through and new_field.remote_field.through and
+ old_field.remote_field.through._meta.auto_created and
+ new_field.remote_field.through._meta.auto_created):
return self._alter_many_to_many(model, old_field, new_field, strict)
elif old_type is None and new_type is None and (
- old_field.rel.through and new_field.rel.through and
- not old_field.rel.through._meta.auto_created and
- not new_field.rel.through._meta.auto_created):
+ old_field.remote_field.through and new_field.remote_field.through and
+ not old_field.remote_field.through._meta.auto_created and
+ not new_field.remote_field.through._meta.auto_created):
# Both sides have through models; this is a no-op.
return
elif old_type is None or new_type is None:
@@ -487,7 +487,7 @@ class BaseDatabaseSchemaEditor(object):
# Drop any FK constraints, we'll remake them later
fks_dropped = set()
- if old_field.rel and old_field.db_constraint:
+ if old_field.remote_field and old_field.db_constraint:
fk_names = self._constraint_names(model, [old_field.column], foreign_key=True)
if strict and len(fk_names) != 1:
raise ValueError("Found wrong number (%s) of foreign key constraints for %s.%s" % (
@@ -707,8 +707,8 @@ class BaseDatabaseSchemaEditor(object):
}
)
# Does it have a foreign key?
- if (new_field.rel and
- (fks_dropped or not old_field.rel or not old_field.db_constraint) and
+ if (new_field.remote_field and
+ (fks_dropped or not old_field.remote_field or not old_field.db_constraint) and
new_field.db_constraint):
self.execute(self._create_fk_sql(model, new_field, "_fk_%(to_table)s_%(to_column)s"))
# Rebuild FKs that pointed to us if we previously had to drop them
@@ -766,22 +766,22 @@ class BaseDatabaseSchemaEditor(object):
Alters M2Ms to repoint their to= endpoints.
"""
# Rename the through table
- if old_field.rel.through._meta.db_table != new_field.rel.through._meta.db_table:
- self.alter_db_table(old_field.rel.through, old_field.rel.through._meta.db_table,
- new_field.rel.through._meta.db_table)
+ if old_field.remote_field.through._meta.db_table != new_field.remote_field.through._meta.db_table:
+ self.alter_db_table(old_field.remote_field.through, old_field.remote_field.through._meta.db_table,
+ new_field.remote_field.through._meta.db_table)
# Repoint the FK to the other side
self.alter_field(
- new_field.rel.through,
+ new_field.remote_field.through,
# We need the field that points to the target model, so we can tell alter_field to change it -
# this is m2m_reverse_field_name() (as opposed to m2m_field_name, which points to our model)
- old_field.rel.through._meta.get_field(old_field.m2m_reverse_field_name()),
- new_field.rel.through._meta.get_field(new_field.m2m_reverse_field_name()),
+ old_field.remote_field.through._meta.get_field(old_field.m2m_reverse_field_name()),
+ new_field.remote_field.through._meta.get_field(new_field.m2m_reverse_field_name()),
)
self.alter_field(
- new_field.rel.through,
+ new_field.remote_field.through,
# for self-referential models we need to alter field from the other end too
- old_field.rel.through._meta.get_field(old_field.m2m_field_name()),
- new_field.rel.through._meta.get_field(new_field.m2m_field_name()),
+ old_field.remote_field.through._meta.get_field(old_field.m2m_field_name()),
+ new_field.remote_field.through._meta.get_field(new_field.m2m_field_name()),
)
def _create_index_name(self, model, column_names, suffix=""):
@@ -860,8 +860,8 @@ class BaseDatabaseSchemaEditor(object):
def _create_fk_sql(self, model, field, suffix):
from_table = model._meta.db_table
from_column = field.column
- to_table = field.related_field.model._meta.db_table
- to_column = field.related_field.column
+ to_table = field.target_field.model._meta.db_table
+ to_column = field.target_field.column
suffix = suffix % {
"to_table": to_table,
"to_column": to_column,
diff --git a/django/db/backends/mysql/validation.py b/django/db/backends/mysql/validation.py
index 9dcd14403ff..94352db6170 100644
--- a/django/db/backends/mysql/validation.py
+++ b/django/db/backends/mysql/validation.py
@@ -14,7 +14,7 @@ class DatabaseValidation(BaseDatabaseValidation):
errors = super(DatabaseValidation, self).check_field(field, **kwargs)
# Ignore any related fields.
- if getattr(field, 'rel', None) is None:
+ if getattr(field, 'remote_field', None) is None:
field_type = field.db_type(connection)
# Ignore any non-concrete fields
diff --git a/django/db/backends/oracle/operations.py b/django/db/backends/oracle/operations.py
index 02fd6fcc7d5..32dd6272169 100644
--- a/django/db/backends/oracle/operations.py
+++ b/django/db/backends/oracle/operations.py
@@ -346,7 +346,7 @@ WHEN (new.%(col_name)s IS NULL)
# continue to loop
break
for f in model._meta.many_to_many:
- if not f.rel.through:
+ if not f.remote_field.through:
table_name = self.quote_name(f.m2m_db_table())
sequence_name = self._get_sequence_name(f.m2m_db_table())
column_name = self.quote_name('id')
diff --git a/django/db/backends/postgresql_psycopg2/operations.py b/django/db/backends/postgresql_psycopg2/operations.py
index 7483d97aee7..df96d16ad23 100644
--- a/django/db/backends/postgresql_psycopg2/operations.py
+++ b/django/db/backends/postgresql_psycopg2/operations.py
@@ -172,7 +172,7 @@ class DatabaseOperations(BaseDatabaseOperations):
)
break # Only one AutoField is allowed per model, so don't bother continuing.
for f in model._meta.many_to_many:
- if not f.rel.through:
+ if not f.remote_field.through:
output.append(
"%s setval(pg_get_serial_sequence('%s','%s'), "
"coalesce(max(%s), 1), max(%s) %s null) %s %s;" % (
diff --git a/django/db/backends/sqlite3/schema.py b/django/db/backends/sqlite3/schema.py
index 26c4771f8fd..0664d31e7a0 100644
--- a/django/db/backends/sqlite3/schema.py
+++ b/django/db/backends/sqlite3/schema.py
@@ -118,8 +118,8 @@ class DatabaseSchemaEditor(BaseDatabaseSchemaEditor):
del body[field.name]
del mapping[field.column]
# Remove any implicit M2M tables
- if field.many_to_many and field.rel.through._meta.auto_created:
- return self.delete_model(field.rel.through)
+ if field.many_to_many and field.remote_field.through._meta.auto_created:
+ return self.delete_model(field.remote_field.through)
# Work inside a new app registry
apps = Apps()
@@ -215,8 +215,8 @@ class DatabaseSchemaEditor(BaseDatabaseSchemaEditor):
table instead (for M2M fields)
"""
# Special-case implicit M2M tables
- if field.many_to_many and field.rel.through._meta.auto_created:
- return self.create_model(field.rel.through)
+ if field.many_to_many and field.remote_field.through._meta.auto_created:
+ return self.create_model(field.remote_field.through)
self._remake_table(model, create_fields=[field])
def remove_field(self, model, field):
@@ -227,8 +227,8 @@ class DatabaseSchemaEditor(BaseDatabaseSchemaEditor):
# M2M fields are a special case
if field.many_to_many:
# For implicit M2M tables, delete the auto-created table
- if field.rel.through._meta.auto_created:
- self.delete_model(field.rel.through)
+ if field.remote_field.through._meta.auto_created:
+ self.delete_model(field.remote_field.through)
# For explicit "through" M2M fields, do nothing
# For everything else, remake.
else:
@@ -263,25 +263,25 @@ class DatabaseSchemaEditor(BaseDatabaseSchemaEditor):
"""
Alters M2Ms to repoint their to= endpoints.
"""
- if old_field.rel.through._meta.db_table == new_field.rel.through._meta.db_table:
+ if old_field.remote_field.through._meta.db_table == new_field.remote_field.through._meta.db_table:
# The field name didn't change, but some options did; we have to propagate this altering.
self._remake_table(
- old_field.rel.through,
+ old_field.remote_field.through,
alter_fields=[(
# We need the field that points to the target model, so we can tell alter_field to change it -
# this is m2m_reverse_field_name() (as opposed to m2m_field_name, which points to our model)
- old_field.rel.through._meta.get_field(old_field.m2m_reverse_field_name()),
- new_field.rel.through._meta.get_field(new_field.m2m_reverse_field_name()),
+ old_field.remote_field.through._meta.get_field(old_field.m2m_reverse_field_name()),
+ new_field.remote_field.through._meta.get_field(new_field.m2m_reverse_field_name()),
)],
override_uniques=(new_field.m2m_field_name(), new_field.m2m_reverse_field_name()),
)
return
# Make a new through table
- self.create_model(new_field.rel.through)
+ self.create_model(new_field.remote_field.through)
# Copy the data across
self.execute("INSERT INTO %s (%s) SELECT %s FROM %s" % (
- self.quote_name(new_field.rel.through._meta.db_table),
+ self.quote_name(new_field.remote_field.through._meta.db_table),
', '.join([
"id",
new_field.m2m_column_name(),
@@ -292,7 +292,7 @@ class DatabaseSchemaEditor(BaseDatabaseSchemaEditor):
old_field.m2m_column_name(),
old_field.m2m_reverse_name(),
]),
- self.quote_name(old_field.rel.through._meta.db_table),
+ self.quote_name(old_field.remote_field.through._meta.db_table),
))
# Delete the old through table
- self.delete_model(old_field.rel.through)
+ self.delete_model(old_field.remote_field.through)
diff --git a/django/db/migrations/autodetector.py b/django/db/migrations/autodetector.py
index 33a9e9e1663..786de794df5 100644
--- a/django/db/migrations/autodetector.py
+++ b/django/db/migrations/autodetector.py
@@ -78,7 +78,7 @@ class MigrationAutodetector(object):
fields_def = []
for name, field in fields:
deconstruction = self.deep_deconstruct(field)
- if field.rel and field.rel.to:
+ if field.remote_field and field.remote_field.model:
del deconstruction[2]['to']
fields_def.append(deconstruction)
return fields_def
@@ -163,11 +163,11 @@ class MigrationAutodetector(object):
old_model_state = self.from_state.models[app_label, old_model_name]
for field_name, field in old_model_state.fields:
old_field = self.old_apps.get_model(app_label, old_model_name)._meta.get_field(field_name)
- if (hasattr(old_field, "rel") and getattr(old_field.rel, "through", None)
- and not old_field.rel.through._meta.auto_created):
+ if (hasattr(old_field, "remote_field") and getattr(old_field.remote_field, "through", None)
+ and not old_field.remote_field.through._meta.auto_created):
through_key = (
- old_field.rel.through._meta.app_label,
- old_field.rel.through._meta.model_name,
+ old_field.remote_field.through._meta.app_label,
+ old_field.remote_field.through._meta.model_name,
)
self.through_users[through_key] = (app_label, old_model_name, field_name)
@@ -460,20 +460,20 @@ class MigrationAutodetector(object):
related_fields = {}
primary_key_rel = None
for field in model_opts.local_fields:
- if field.rel:
- if field.rel.to:
+ if field.remote_field:
+ if field.remote_field.model:
if field.primary_key:
- primary_key_rel = field.rel.to
- elif not field.rel.parent_link:
+ primary_key_rel = field.remote_field.model
+ elif not field.remote_field.parent_link:
related_fields[field.name] = field
# through will be none on M2Ms on swapped-out models;
# we can treat lack of through as auto_created=True, though.
- if getattr(field.rel, "through", None) and not field.rel.through._meta.auto_created:
+ if getattr(field.remote_field, "through", None) and not field.remote_field.through._meta.auto_created:
related_fields[field.name] = field
for field in model_opts.local_many_to_many:
- if field.rel.to:
+ if field.remote_field.model:
related_fields[field.name] = field
- if getattr(field.rel, "through", None) and not field.rel.through._meta.auto_created:
+ if getattr(field.remote_field, "through", None) and not field.remote_field.through._meta.auto_created:
related_fields[field.name] = field
# Are there unique/index_together to defer?
unique_together = model_state.options.pop('unique_together', None)
@@ -522,13 +522,13 @@ class MigrationAutodetector(object):
dep_app_label = "__setting__"
dep_object_name = swappable_setting
else:
- dep_app_label = field.rel.to._meta.app_label
- dep_object_name = field.rel.to._meta.object_name
+ dep_app_label = field.remote_field.model._meta.app_label
+ dep_object_name = field.remote_field.model._meta.object_name
dependencies = [(dep_app_label, dep_object_name, None, True)]
- if getattr(field.rel, "through", None) and not field.rel.through._meta.auto_created:
+ if getattr(field.remote_field, "through", None) and not field.remote_field.through._meta.auto_created:
dependencies.append((
- field.rel.through._meta.app_label,
- field.rel.through._meta.object_name,
+ field.remote_field.through._meta.app_label,
+ field.remote_field.through._meta.object_name,
None,
True
))
@@ -639,17 +639,17 @@ class MigrationAutodetector(object):
# Gather related fields
related_fields = {}
for field in model._meta.local_fields:
- if field.rel:
- if field.rel.to:
+ if field.remote_field:
+ if field.remote_field.model:
related_fields[field.name] = field
# through will be none on M2Ms on swapped-out models;
# we can treat lack of through as auto_created=True, though.
- if getattr(field.rel, "through", None) and not field.rel.through._meta.auto_created:
+ if getattr(field.remote_field, "through", None) and not field.remote_field.through._meta.auto_created:
related_fields[field.name] = field
for field in model._meta.local_many_to_many:
- if field.rel.to:
+ if field.remote_field.model:
related_fields[field.name] = field
- if getattr(field.rel, "through", None) and not field.rel.through._meta.auto_created:
+ if getattr(field.remote_field, "through", None) and not field.remote_field.through._meta.auto_created:
related_fields[field.name] = field
# Generate option removal first
unique_together = model_state.options.pop('unique_together', None)
@@ -736,7 +736,7 @@ class MigrationAutodetector(object):
for rem_app_label, rem_model_name, rem_field_name in sorted(self.old_field_keys - self.new_field_keys):
if rem_app_label == app_label and rem_model_name == model_name:
old_field_dec = self.deep_deconstruct(old_model_state.get_field_by_name(rem_field_name))
- if field.rel and field.rel.to and 'to' in old_field_dec[2]:
+ if field.remote_field and field.remote_field.model and 'to' in old_field_dec[2]:
old_rel_to = old_field_dec[2]['to']
if old_rel_to in self.renamed_models_rel:
old_field_dec[2]['to'] = self.renamed_models_rel[old_rel_to]
@@ -766,20 +766,20 @@ class MigrationAutodetector(object):
field = self.new_apps.get_model(app_label, model_name)._meta.get_field(field_name)
# Fields that are foreignkeys/m2ms depend on stuff
dependencies = []
- if field.rel and field.rel.to:
+ if field.remote_field and field.remote_field.model:
# Account for FKs to swappable models
swappable_setting = getattr(field, 'swappable_setting', None)
if swappable_setting is not None:
dep_app_label = "__setting__"
dep_object_name = swappable_setting
else:
- dep_app_label = field.rel.to._meta.app_label
- dep_object_name = field.rel.to._meta.object_name
+ dep_app_label = field.remote_field.model._meta.app_label
+ dep_object_name = field.remote_field.model._meta.object_name
dependencies = [(dep_app_label, dep_object_name, None, True)]
- if getattr(field.rel, "through", None) and not field.rel.through._meta.auto_created:
+ if getattr(field.remote_field, "through", None) and not field.remote_field.through._meta.auto_created:
dependencies.append((
- field.rel.through._meta.app_label,
- field.rel.through._meta.object_name,
+ field.remote_field.through._meta.app_label,
+ field.remote_field.through._meta.object_name,
None,
True,
))
@@ -838,13 +838,13 @@ class MigrationAutodetector(object):
new_field = self.new_apps.get_model(app_label, model_name)._meta.get_field(field_name)
# Implement any model renames on relations; these are handled by RenameModel
# so we need to exclude them from the comparison
- if hasattr(new_field, "rel") and getattr(new_field.rel, "to", None):
+ if hasattr(new_field, "remote_field") and getattr(new_field.remote_field, "model", None):
rename_key = (
- new_field.rel.to._meta.app_label,
- new_field.rel.to._meta.model_name,
+ new_field.remote_field.model._meta.app_label,
+ new_field.remote_field.model._meta.model_name,
)
if rename_key in self.renamed_models:
- new_field.rel.to = old_field.rel.to
+ new_field.remote_field.model = old_field.remote_field.model
old_field_dec = self.deep_deconstruct(old_field)
new_field_dec = self.deep_deconstruct(new_field)
if old_field_dec != new_field_dec:
diff --git a/django/db/migrations/operations/fields.py b/django/db/migrations/operations/fields.py
index e00cfa66e22..ea4092ab26a 100644
--- a/django/db/migrations/operations/fields.py
+++ b/django/db/migrations/operations/fields.py
@@ -191,11 +191,11 @@ class AlterField(Operation):
# If the field is a relatedfield with an unresolved rel.to, just
# set it equal to the other field side. Bandaid fix for AlterField
# migrations that are part of a RenameModel change.
- if from_field.rel and from_field.rel.to:
- if isinstance(from_field.rel.to, six.string_types):
- from_field.rel.to = to_field.rel.to
- elif to_field.rel and isinstance(to_field.rel.to, six.string_types):
- to_field.rel.to = from_field.rel.to
+ if from_field.remote_field and from_field.remote_field.model:
+ if isinstance(from_field.remote_field.model, six.string_types):
+ from_field.remote_field.model = to_field.remote_field.model
+ elif to_field.remote_field and isinstance(to_field.remote_field.model, six.string_types):
+ to_field.remote_field.model = from_field.remote_field.model
if not self.preserve_default:
to_field.default = self.field.default
schema_editor.alter_field(from_model, from_field, to_field)
diff --git a/django/db/migrations/operations/models.py b/django/db/migrations/operations/models.py
index efd35f0501a..ece15274530 100644
--- a/django/db/migrations/operations/models.py
+++ b/django/db/migrations/operations/models.py
@@ -74,9 +74,9 @@ class CreateModel(Operation):
strings_to_check.append(base.split(".")[-1])
# Check we have no FKs/M2Ms with it
for fname, field in self.fields:
- if field.rel:
- if isinstance(field.rel.to, six.string_types):
- strings_to_check.append(field.rel.to.split(".")[-1])
+ if field.remote_field:
+ if isinstance(field.remote_field.model, six.string_types):
+ strings_to_check.append(field.remote_field.model.split(".")[-1])
# Now go over all the strings and compare them
for string in strings_to_check:
if string.lower() == name.lower():
@@ -181,7 +181,7 @@ class RenameModel(Operation):
for name, field in state.models[related_key].fields:
if name == related_object.field.name:
field = field.clone()
- field.rel.to = "%s.%s" % (app_label, self.new_name)
+ field.remote_field.model = "%s.%s" % (app_label, self.new_name)
new_fields.append((name, field))
state.models[related_key].fields = new_fields
state.reload_model(*related_key)
@@ -220,11 +220,11 @@ class RenameModel(Operation):
fields = zip(old_model._meta.local_many_to_many, new_model._meta.local_many_to_many)
for (old_field, new_field) in fields:
# Skip self-referential fields as these are renamed above.
- if new_field.model == new_field.related_model or not new_field.rel.through._meta.auto_created:
+ if new_field.model == new_field.related_model or not new_field.remote_field.through._meta.auto_created:
continue
# Rename the M2M table that's based on this model's name.
- old_m2m_model = old_field.rel.through
- new_m2m_model = new_field.rel.through
+ old_m2m_model = old_field.remote_field.through
+ new_m2m_model = new_field.remote_field.through
schema_editor.alter_db_table(
new_m2m_model,
old_m2m_model._meta.db_table,
@@ -296,11 +296,11 @@ class AlterModelTable(Operation):
)
# Rename M2M fields whose name is based on this model's db_table
for (old_field, new_field) in zip(old_model._meta.local_many_to_many, new_model._meta.local_many_to_many):
- if new_field.rel.through._meta.auto_created:
+ if new_field.remote_field.through._meta.auto_created:
schema_editor.alter_db_table(
- new_field.rel.through,
- old_field.rel.through._meta.db_table,
- new_field.rel.through._meta.db_table,
+ new_field.remote_field.through,
+ old_field.remote_field.through._meta.db_table,
+ new_field.remote_field.through._meta.db_table,
)
def database_backwards(self, app_label, schema_editor, from_state, to_state):
diff --git a/django/db/migrations/optimizer.py b/django/db/migrations/optimizer.py
index 8c5ee718194..5bb97b5fe49 100644
--- a/django/db/migrations/optimizer.py
+++ b/django/db/migrations/optimizer.py
@@ -230,15 +230,15 @@ class MigrationOptimizer(object):
def reduce_create_model_add_field(self, operation, other, in_between):
if operation.name_lower == other.model_name_lower:
# Don't allow optimizations of FKs through models they reference
- if hasattr(other.field, "rel") and other.field.rel:
+ if hasattr(other.field, "remote_field") and other.field.remote_field:
for between in in_between:
# Check that it doesn't point to the model
- app_label, object_name = self.model_to_key(other.field.rel.to)
+ app_label, object_name = self.model_to_key(other.field.remote_field.model)
if between.references_model(object_name, app_label):
return None
# Check that it's not through the model
- if getattr(other.field.rel, "through", None):
- app_label, object_name = self.model_to_key(other.field.rel.through)
+ if getattr(other.field.remote_field, "through", None):
+ app_label, object_name = self.model_to_key(other.field.remote_field.through)
if between.references_model(object_name, app_label):
return None
# OK, that's fine
diff --git a/django/db/migrations/state.py b/django/db/migrations/state.py
index 3d78cf609e3..d0d065af8ae 100644
--- a/django/db/migrations/state.py
+++ b/django/db/migrations/state.py
@@ -94,9 +94,9 @@ class ProjectState(object):
model_state = self.models[(app_label, model_name)]
for name, field in model_state.fields:
if field.is_relation:
- if field.rel.to == RECURSIVE_RELATIONSHIP_CONSTANT:
+ if field.remote_field.model == RECURSIVE_RELATIONSHIP_CONSTANT:
continue
- rel_app_label, rel_model_name = _get_app_label_and_model_name(field.rel.to, app_label)
+ rel_app_label, rel_model_name = _get_app_label_and_model_name(field.remote_field.model, app_label)
related_models.add((rel_app_label, rel_model_name.lower()))
# Unregister all related models
@@ -328,7 +328,7 @@ class ModelState(object):
# Deconstruct the fields
fields = []
for field in model._meta.local_fields:
- if getattr(field, "rel", None) and exclude_rels:
+ if getattr(field, "remote_field", None) and exclude_rels:
continue
if isinstance(field, OrderWrt):
continue
diff --git a/django/db/models/base.py b/django/db/models/base.py
index 06a90882f09..fdd1ce28d9d 100644
--- a/django/db/models/base.py
+++ b/django/db/models/base.py
@@ -199,7 +199,7 @@ class ModelBase(type):
# Locate OneToOneField instances.
for field in base._meta.local_fields:
if isinstance(field, OneToOneField):
- parent_links[field.rel.to] = field
+ parent_links[field.remote_field.model] = field
# Do the appropriate setup for any model parents.
for base in parents:
@@ -307,19 +307,19 @@ class ModelBase(type):
# certain it has been created
def make_foreign_order_accessors(field, model, cls):
setattr(
- field.rel.to,
+ field.remote_field.model,
'get_%s_order' % cls.__name__.lower(),
curry(method_get_order, cls)
)
setattr(
- field.rel.to,
+ field.remote_field.model,
'set_%s_order' % cls.__name__.lower(),
curry(method_set_order, cls)
)
add_lazy_relation(
cls,
opts.order_with_respect_to,
- opts.order_with_respect_to.rel.to,
+ opts.order_with_respect_to.remote_field.model,
make_foreign_order_accessors
)
@@ -382,7 +382,7 @@ class Model(six.with_metaclass(ModelBase)):
setattr(self, field.attname, val)
kwargs.pop(field.name, None)
# Maintain compatibility with existing calls.
- if isinstance(field.rel, ManyToOneRel):
+ if isinstance(field.remote_field, ManyToOneRel):
kwargs.pop(field.attname, None)
# Now we're left with the unprocessed fields that *must* come from
@@ -399,7 +399,7 @@ class Model(six.with_metaclass(ModelBase)):
# This field will be populated on request.
continue
if kwargs:
- if isinstance(field.rel, ForeignObjectRel):
+ if isinstance(field.remote_field, ForeignObjectRel):
try:
# Assume object instance was passed in.
rel_obj = kwargs.pop(field.name)
@@ -879,7 +879,7 @@ class Model(six.with_metaclass(ModelBase)):
def prepare_database_save(self, field):
if self.pk is None:
raise ValueError("Unsaved model instance %r cannot be used in an ORM query." % self)
- return getattr(self, field.rel.field_name)
+ return getattr(self, field.remote_field.field_name)
def clean(self):
"""
@@ -1238,20 +1238,20 @@ class Model(six.with_metaclass(ModelBase)):
fields = cls._meta.local_many_to_many
# Skip when the target model wasn't found.
- fields = (f for f in fields if isinstance(f.rel.to, ModelBase))
+ fields = (f for f in fields if isinstance(f.remote_field.model, ModelBase))
# Skip when the relationship model wasn't found.
- fields = (f for f in fields if isinstance(f.rel.through, ModelBase))
+ fields = (f for f in fields if isinstance(f.remote_field.through, ModelBase))
for f in fields:
- signature = (f.rel.to, cls, f.rel.through)
+ signature = (f.remote_field.model, cls, f.remote_field.through)
if signature in seen_intermediary_signatures:
errors.append(
checks.Error(
"The model has two many-to-many relations through "
"the intermediate model '%s.%s'." % (
- f.rel.through._meta.app_label,
- f.rel.through._meta.object_name
+ f.remote_field.through._meta.app_label,
+ f.remote_field.through._meta.object_name
),
hint=None,
obj=cls,
@@ -1448,7 +1448,7 @@ class Model(six.with_metaclass(ModelBase)):
)
)
else:
- if isinstance(field.rel, models.ManyToManyRel):
+ if isinstance(field.remote_field, models.ManyToManyRel):
errors.append(
checks.Error(
"'%s' refers to a ManyToManyField '%s', but "
@@ -1595,7 +1595,7 @@ class Model(six.with_metaclass(ModelBase)):
for f in cls._meta.local_many_to_many:
# Check if auto-generated name for the M2M field is too long
# for the database.
- for m2m in f.rel.through._meta.local_fields:
+ for m2m in f.remote_field.through._meta.local_fields:
_, rel_name = m2m.get_attname_column()
if (m2m.db_column is None and rel_name is not None
and len(rel_name) > allowed_len):
@@ -1624,7 +1624,7 @@ class Model(six.with_metaclass(ModelBase)):
def method_set_order(ordered_obj, self, id_list, using=None):
if using is None:
using = DEFAULT_DB_ALIAS
- rel_val = getattr(self, ordered_obj._meta.order_with_respect_to.rel.field_name)
+ rel_val = getattr(self, ordered_obj._meta.order_with_respect_to.remote_field.field_name)
order_name = ordered_obj._meta.order_with_respect_to.name
# FIXME: It would be nice if there was an "update many" version of update
# for situations like this.
@@ -1634,7 +1634,7 @@ def method_set_order(ordered_obj, self, id_list, using=None):
def method_get_order(ordered_obj, self):
- rel_val = getattr(self, ordered_obj._meta.order_with_respect_to.rel.field_name)
+ rel_val = getattr(self, ordered_obj._meta.order_with_respect_to.remote_field.field_name)
order_name = ordered_obj._meta.order_with_respect_to.name
pk_name = ordered_obj._meta.pk.name
return [r[pk_name] for r in
diff --git a/django/db/models/deletion.py b/django/db/models/deletion.py
index 0adfb7f1593..8bfba43fac6 100644
--- a/django/db/models/deletion.py
+++ b/django/db/models/deletion.py
@@ -14,7 +14,7 @@ class ProtectedError(IntegrityError):
def CASCADE(collector, field, sub_objs, using):
- collector.collect(sub_objs, source=field.rel.to,
+ collector.collect(sub_objs, source=field.remote_field.model,
source_attr=field.name, nullable=field.null)
if field.null and not connections[using].features.can_defer_constraint_checks:
collector.add_field_update(field, None, sub_objs)
@@ -23,7 +23,7 @@ def CASCADE(collector, field, sub_objs, using):
def PROTECT(collector, field, sub_objs, using):
raise ProtectedError("Cannot delete some instances of model '%s' because "
"they are referenced through a protected foreign key: '%s.%s'" % (
- field.rel.to.__name__, sub_objs[0].__class__.__name__, field.name
+ field.remote_field.model.__name__, sub_objs[0].__class__.__name__, field.name
),
sub_objs
)
@@ -136,7 +136,7 @@ class Collector(object):
skipping parent -> child -> parent chain preventing fast delete of
the child.
"""
- if from_field and from_field.rel.on_delete is not CASCADE:
+ if from_field and from_field.remote_field.on_delete is not CASCADE:
return False
if not (hasattr(objs, 'model') and hasattr(objs, '_raw_delete')):
return False
@@ -153,7 +153,7 @@ class Collector(object):
# Foreign keys pointing to this model, both from m2m and other
# models.
for related in get_candidate_relations_to_delete(opts):
- if related.field.rel.on_delete is not DO_NOTHING:
+ if related.field.remote_field.on_delete is not DO_NOTHING:
return False
for field in model._meta.virtual_fields:
if hasattr(field, 'bulk_related_objects'):
@@ -214,14 +214,13 @@ class Collector(object):
# object instance.
parent_objs = [getattr(obj, ptr.name) for obj in new_objs]
self.collect(parent_objs, source=model,
- source_attr=ptr.rel.related_name,
+ source_attr=ptr.remote_field.related_name,
collect_related=False,
reverse_dependency=True)
-
if collect_related:
for related in get_candidate_relations_to_delete(model._meta):
field = related.field
- if field.rel.on_delete == DO_NOTHING:
+ if field.remote_field.on_delete == DO_NOTHING:
continue
batches = self.get_del_batches(new_objs, field)
for batch in batches:
@@ -229,14 +228,14 @@ class Collector(object):
if self.can_fast_delete(sub_objs, from_field=field):
self.fast_deletes.append(sub_objs)
elif sub_objs:
- field.rel.on_delete(self, field, sub_objs, self.using)
+ field.remote_field.on_delete(self, field, sub_objs, self.using)
for field in model._meta.virtual_fields:
if hasattr(field, 'bulk_related_objects'):
# Its something like generic foreign key.
sub_objs = field.bulk_related_objects(new_objs, self.using)
self.collect(sub_objs,
source=model,
- source_attr=field.rel.related_name,
+ source_attr=field.remote_field.related_name,
nullable=True)
def related_objects(self, related, objs):
diff --git a/django/db/models/fields/__init__.py b/django/db/models/fields/__init__.py
index 6ee1185c90a..b305330f3b6 100644
--- a/django/db/models/fields/__init__.py
+++ b/django/db/models/fields/__init__.py
@@ -23,6 +23,7 @@ from django.utils.duration import duration_string
from django.utils.functional import cached_property, curry, total_ordering, Promise
from django.utils.text import capfirst
from django.utils import timezone
+from django.utils.deprecation import RemovedInDjango21Warning
from django.utils.translation import ugettext_lazy as _
from django.utils.encoding import (smart_text, force_text, force_bytes,
python_2_unicode_compatible)
@@ -146,8 +147,8 @@ class Field(RegisterLookupMixin):
self.primary_key = primary_key
self.max_length, self._unique = max_length, unique
self.blank, self.null = blank, null
- self.rel = rel
- self.is_relation = self.rel is not None
+ self.remote_field = rel
+ self.is_relation = self.remote_field is not None
self.default = default
self.editable = editable
self.serialize = serialize
@@ -240,6 +241,13 @@ class Field(RegisterLookupMixin):
else:
return []
+ @property
+ def rel(self):
+ warnings.warn(
+ "Usage of field.rel has been deprecated. Use field.remote_field instead.",
+ RemovedInDjango21Warning, 2)
+ return self.remote_field
+
def _check_choices(self):
if self.choices:
if (isinstance(self.choices, six.string_types) or
@@ -468,10 +476,10 @@ class Field(RegisterLookupMixin):
# We don't have to deepcopy very much here, since most things are not
# intended to be altered after initial creation.
obj = copy.copy(self)
- if self.rel:
- obj.rel = copy.copy(self.rel)
- if hasattr(self.rel, 'field') and self.rel.field is self:
- obj.rel.field = obj
+ if self.remote_field:
+ obj.remote_field = copy.copy(self.remote_field)
+ if hasattr(self.remote_field, 'field') and self.remote_field.field is self:
+ obj.remote_field.field = obj
memodict[id(self)] = obj
return obj
@@ -811,10 +819,10 @@ class Field(RegisterLookupMixin):
not blank_defined else [])
if self.choices:
return first_choice + choices
- rel_model = self.rel.to
+ rel_model = self.remote_field.model
limit_choices_to = limit_choices_to or self.get_limit_choices_to()
- if hasattr(self.rel, 'get_related_field'):
- lst = [(getattr(x, self.rel.get_related_field().attname),
+ if hasattr(self.remote_field, 'get_related_field'):
+ lst = [(getattr(x, self.remote_field.get_related_field().attname),
smart_text(x))
for x in rel_model._default_manager.complex_filter(
limit_choices_to)]
diff --git a/django/db/models/fields/related.py b/django/db/models/fields/related.py
index e4a176b9215..9e6bbaaf29c 100644
--- a/django/db/models/fields/related.py
+++ b/django/db/models/fields/related.py
@@ -22,7 +22,9 @@ from django.db.models.fields.related_lookups import (
from django.db.models.query import QuerySet
from django.db.models.query_utils import PathInfo
from django.utils import six
-from django.utils.deprecation import RemovedInDjango20Warning
+from django.utils.deprecation import (
+ RemovedInDjango20Warning, RemovedInDjango21Warning,
+)
from django.utils.encoding import force_text, smart_text
from django.utils.functional import cached_property, curry
from django.utils.translation import ugettext_lazy as _
@@ -116,7 +118,7 @@ class RelatedField(Field):
def related_model(self):
# Can't cache this property until all the models are loaded.
apps.check_models_ready()
- return self.rel.to
+ return self.remote_field.model
def check(self, **kwargs):
errors = super(RelatedField, self).check(**kwargs)
@@ -129,7 +131,7 @@ class RelatedField(Field):
def _check_related_name_is_valid(self):
import re
import keyword
- related_name = self.rel.related_name
+ related_name = self.remote_field.related_name
is_valid_id = (related_name and re.match('^[a-zA-Z_][a-zA-Z0-9_]*$', related_name)
and not keyword.iskeyword(related_name))
@@ -137,7 +139,7 @@ class RelatedField(Field):
return [
checks.Error(
"The name '%s' is invalid related_name for field %s.%s" %
- (self.rel.related_name, self.model._meta.object_name,
+ (self.remote_field.related_name, self.model._meta.object_name,
self.name),
hint="Related name must be a valid Python identifier or end with a '+'",
obj=self,
@@ -147,10 +149,10 @@ class RelatedField(Field):
return []
def _check_relation_model_exists(self):
- rel_is_missing = self.rel.to not in apps.get_models()
- rel_is_string = isinstance(self.rel.to, six.string_types)
- model_name = self.rel.to if rel_is_string else self.rel.to._meta.object_name
- if rel_is_missing and (rel_is_string or not self.rel.to._meta.swapped):
+ rel_is_missing = self.remote_field.model not in apps.get_models()
+ rel_is_string = isinstance(self.remote_field.model, six.string_types)
+ model_name = self.remote_field.model if rel_is_string else self.remote_field.model._meta.object_name
+ if rel_is_missing and (rel_is_string or not self.remote_field.model._meta.swapped):
return [
checks.Error(
("Field defines a relation with model '%s', which "
@@ -163,18 +165,18 @@ class RelatedField(Field):
return []
def _check_referencing_to_swapped_model(self):
- if (self.rel.to not in apps.get_models() and
- not isinstance(self.rel.to, six.string_types) and
- self.rel.to._meta.swapped):
+ if (self.remote_field.model not in apps.get_models() and
+ not isinstance(self.remote_field.model, six.string_types) and
+ self.remote_field.model._meta.swapped):
model = "%s.%s" % (
- self.rel.to._meta.app_label,
- self.rel.to._meta.object_name
+ self.remote_field.model._meta.app_label,
+ self.remote_field.model._meta.object_name
)
return [
checks.Error(
("Field defines a relation with the model '%s', "
"which has been swapped out.") % model,
- hint="Update the relation to point at 'settings.%s'." % self.rel.to._meta.swappable,
+ hint="Update the relation to point at 'settings.%s'." % self.remote_field.model._meta.swappable,
obj=self,
id='fields.E301',
)
@@ -190,20 +192,15 @@ class RelatedField(Field):
errors = []
opts = self.model._meta
- # `f.rel.to` may be a string instead of a model. Skip if model name is
+ # `f.remote_field.model` may be a string instead of a model. Skip if model name is
# not resolved.
- if not isinstance(self.rel.to, ModelBase):
+ if not isinstance(self.remote_field.model, ModelBase):
return []
# If the field doesn't install backward relation on the target model (so
# `is_hidden` returns True), then there are no clashes to check and we
# can skip these fields.
- if self.rel.is_hidden():
- return []
-
- try:
- self.rel
- except AttributeError:
+ if self.remote_field.is_hidden():
return []
# Consider that we are checking field `Model.foreign` and the models
@@ -217,9 +214,9 @@ class RelatedField(Field):
# foreign = models.ForeignKey(Target)
# m2m = models.ManyToManyField(Target)
- rel_opts = self.rel.to._meta
+ rel_opts = self.remote_field.model._meta
# rel_opts.object_name == "Target"
- rel_name = self.rel.get_accessor_name() # i. e. "model_set"
+ rel_name = self.remote_field.get_accessor_name() # i. e. "model_set"
rel_query_name = self.related_query_name() # i. e. "model"
field_name = "%s.%s" % (opts.object_name,
self.name) # i. e. "Model.field"
@@ -300,16 +297,16 @@ class RelatedField(Field):
sup.contribute_to_class(cls, name, virtual_only=virtual_only)
if not cls._meta.abstract:
- if self.rel.related_name:
- related_name = force_text(self.rel.related_name) % {
+ if self.remote_field.related_name:
+ related_name = force_text(self.remote_field.related_name) % {
'class': cls.__name__.lower(),
'app_label': cls._meta.app_label.lower()
}
- self.rel.related_name = related_name
- other = self.rel.to
+ self.remote_field.related_name = related_name
+ other = self.remote_field.model
if isinstance(other, six.string_types) or other._meta.pk is None:
def resolve_related_class(field, model, cls):
- field.rel.to = model
+ field.remote_field.model = model
field.do_related_class(model, cls)
add_lazy_relation(cls, self, other, resolve_related_class)
else:
@@ -323,12 +320,12 @@ class RelatedField(Field):
"""
if self.swappable:
# Work out string form of "to"
- if isinstance(self.rel.to, six.string_types):
- to_string = self.rel.to
+ if isinstance(self.remote_field.model, six.string_types):
+ to_string = self.remote_field.model
else:
to_string = "%s.%s" % (
- self.rel.to._meta.app_label,
- self.rel.to._meta.object_name,
+ self.remote_field.model._meta.app_label,
+ self.remote_field.model._meta.object_name,
)
# See if anything swapped/swappable matches
for model in apps.get_models(include_swapped=True):
@@ -340,22 +337,22 @@ class RelatedField(Field):
return None
def set_attributes_from_rel(self):
- self.name = self.name or (self.rel.to._meta.model_name + '_' + self.rel.to._meta.pk.name)
+ self.name = self.name or (self.remote_field.model._meta.model_name + '_' + self.remote_field.model._meta.pk.name)
if self.verbose_name is None:
- self.verbose_name = self.rel.to._meta.verbose_name
- self.rel.set_field_name()
+ self.verbose_name = self.remote_field.model._meta.verbose_name
+ self.remote_field.set_field_name()
@property
def related(self):
warnings.warn(
- "Usage of field.related has been deprecated. Use field.rel instead.",
+ "Usage of field.related has been deprecated. Use field.remote_field instead.",
RemovedInDjango20Warning, 2)
- return self.rel
+ return self.remote_field
def do_related_class(self, other, cls):
self.set_attributes_from_rel()
if not cls._meta.abstract:
- self.contribute_to_related_class(other, self.rel)
+ self.contribute_to_related_class(other, self.remote_field)
def get_limit_choices_to(self):
"""
@@ -364,9 +361,9 @@ class RelatedField(Field):
If it is a callable, it will be invoked and the result will be
returned.
"""
- if callable(self.rel.limit_choices_to):
- return self.rel.limit_choices_to()
- return self.rel.limit_choices_to
+ if callable(self.remote_field.limit_choices_to):
+ return self.remote_field.limit_choices_to()
+ return self.remote_field.limit_choices_to
def formfield(self, **kwargs):
"""
@@ -377,11 +374,11 @@ class RelatedField(Field):
being constructed.
"""
defaults = {}
- if hasattr(self.rel, 'get_related_field'):
+ if hasattr(self.remote_field, 'get_related_field'):
# If this is a callable, do not invoke it here. Just pass
# it in the defaults for when the form class will later be
# instantiated.
- limit_choices_to = self.rel.limit_choices_to
+ limit_choices_to = self.remote_field.limit_choices_to
defaults.update({
'limit_choices_to': limit_choices_to,
})
@@ -393,7 +390,19 @@ class RelatedField(Field):
Define the name that can be used to identify this related object in a
table-spanning query.
"""
- return self.rel.related_query_name or self.rel.related_name or self.opts.model_name
+ return self.remote_field.related_query_name or self.remote_field.related_name or self.opts.model_name
+
+ @property
+ def target_field(self):
+ """
+ When filtering against this relation, returns the field on the remote
+ model against which the filtering should happen.
+ """
+ target_fields = self.get_path_info()[-1].target_fields
+ if len(target_fields) > 1:
+ raise exceptions.FieldError(
+ "The relation has multiple target fields, but only single target field was asked for")
+ return target_fields[0]
class SingleRelatedObjectDescriptor(object):
@@ -553,11 +562,11 @@ class ReverseSingleRelatedObjectDescriptor(object):
@cached_property
def RelatedObjectDoesNotExist(self):
# The exception can't be created at initialization time since the
- # related model might not be resolved yet; `rel.to` might still be
+ # related model might not be resolved yet; `rel.model` might still be
# a string model reference.
return type(
str('RelatedObjectDoesNotExist'),
- (self.field.rel.to.DoesNotExist, AttributeError),
+ (self.field.remote_field.model.DoesNotExist, AttributeError),
{}
)
@@ -565,11 +574,11 @@ class ReverseSingleRelatedObjectDescriptor(object):
return hasattr(instance, self.cache_name)
def get_queryset(self, **hints):
- manager = self.field.rel.to._default_manager
+ manager = self.field.remote_field.model._default_manager
# If the related manager indicates that it should be used for
# related fields, respect that.
if not getattr(manager, 'use_for_related_fields', False):
- manager = self.field.rel.to._base_manager
+ manager = self.field.remote_field.model._base_manager
return manager.db_manager(hints=hints).all()
def get_prefetch_queryset(self, instances, queryset=None):
@@ -588,7 +597,7 @@ class ReverseSingleRelatedObjectDescriptor(object):
# (related_name ends with a '+'). Refs #21410.
# The check for len(...) == 1 is a special case that allows the query
# to be join-less and smaller. Refs #21760.
- if self.field.rel.is_hidden() or len(self.field.foreign_related_fields) == 1:
+ if self.field.remote_field.is_hidden() or len(self.field.foreign_related_fields) == 1:
query = {'%s__in' % related_field.name: set(instance_attr(inst)[0] for inst in instances)}
else:
query = {'%s__in' % self.field.related_query_name(): instances}
@@ -596,8 +605,8 @@ class ReverseSingleRelatedObjectDescriptor(object):
# Since we're going to assign directly in the cache,
# we must manage the reverse relation cache manually.
- if not self.field.rel.multiple:
- rel_obj_cache_name = self.field.rel.get_cache_name()
+ if not self.field.remote_field.multiple:
+ rel_obj_cache_name = self.field.remote_field.get_cache_name()
for rel_obj in queryset:
instance = instances_dict[rel_obj_attr(rel_obj)]
setattr(rel_obj, rel_obj_cache_name, instance)
@@ -625,8 +634,8 @@ class ReverseSingleRelatedObjectDescriptor(object):
qs = qs.filter(extra_filter, **params)
# Assuming the database enforces foreign keys, this won't fail.
rel_obj = qs.get()
- if not self.field.rel.multiple:
- setattr(rel_obj, self.field.rel.get_cache_name(), instance)
+ if not self.field.remote_field.multiple:
+ setattr(rel_obj, self.field.remote_field.get_cache_name(), instance)
setattr(instance, self.cache_name, rel_obj)
if rel_obj is None and not self.field.null:
raise self.RelatedObjectDoesNotExist(
@@ -643,13 +652,13 @@ class ReverseSingleRelatedObjectDescriptor(object):
'Cannot assign None: "%s.%s" does not allow null values.' %
(instance._meta.object_name, self.field.name)
)
- elif value is not None and not isinstance(value, self.field.rel.to):
+ elif value is not None and not isinstance(value, self.field.remote_field.model):
raise ValueError(
'Cannot assign "%r": "%s.%s" must be a "%s" instance.' % (
value,
instance._meta.object_name,
self.field.name,
- self.field.rel.to._meta.object_name,
+ self.field.remote_field.model._meta.object_name,
)
)
elif value is not None:
@@ -678,7 +687,7 @@ class ReverseSingleRelatedObjectDescriptor(object):
# cache. This cache also might not exist if the related object
# hasn't been accessed yet.
if related is not None:
- setattr(related, self.field.rel.get_cache_name(), None)
+ setattr(related, self.field.remote_field.get_cache_name(), None)
for lh_field, rh_field in self.field.related_fields:
setattr(instance, lh_field.attname, None)
@@ -690,7 +699,7 @@ class ReverseSingleRelatedObjectDescriptor(object):
if not self.field.allow_unsaved_instance_assignment and pk is None:
raise ValueError(
'Cannot assign "%r": "%s" instance isn\'t saved in the database.' %
- (value, self.field.rel.to._meta.object_name)
+ (value, self.field.remote_field.model._meta.object_name)
)
setattr(instance, lh_field.attname, getattr(value, rh_field.attname))
@@ -698,8 +707,8 @@ class ReverseSingleRelatedObjectDescriptor(object):
# object caches now, too. This avoids another db hit if you get the
# object you just set.
setattr(instance, self.cache_name, value)
- if value is not None and not self.field.rel.multiple:
- setattr(value, self.field.rel.get_cache_name(), instance)
+ if value is not None and not self.field.remote_field.multiple:
+ setattr(value, self.field.remote_field.get_cache_name(), instance)
def create_foreign_related_manager(superclass, rel):
@@ -809,7 +818,7 @@ def create_foreign_related_manager(superclass, rel):
if self.field.get_local_related_value(obj) == val:
old_ids.add(obj.pk)
else:
- raise self.field.rel.to.DoesNotExist("%r is not related to %r." % (obj, self.instance))
+ raise self.field.remote_field.model.DoesNotExist("%r is not related to %r." % (obj, self.instance))
self._clear(self.filter(pk__in=old_ids), bulk)
remove.alters_data = True
@@ -911,7 +920,7 @@ def create_many_related_manager(superclass, rel, reverse):
self.instance = instance
if not reverse:
- self.model = rel.to
+ self.model = rel.model
self.query_field_name = rel.field.related_query_name()
self.prefetch_cache_name = rel.field.name
self.source_field_name = rel.field.m2m_field_name()
@@ -1265,7 +1274,7 @@ class ManyRelatedObjectsDescriptor(ForeignRelatedObjectsDescriptor):
@cached_property
def related_manager_cls(self):
- model = self.rel.related_model if self.reverse else self.rel.to
+ model = self.rel.related_model if self.reverse else self.rel.model
return create_many_related_manager(
model._default_manager.__class__,
self.rel,
@@ -1287,10 +1296,14 @@ class ForeignObjectRel(object):
editable = False
is_relation = True
+ # Reverse relations are always nullable (Django can't enforce that a
+ # foreign key on the related model points to this model).
+ null = True
+
def __init__(self, field, to, related_name=None, related_query_name=None,
limit_choices_to=None, parent_link=False, on_delete=None):
self.field = field
- self.to = to
+ self.model = to
self.related_name = related_name
self.related_query_name = related_query_name
self.limit_choices_to = {} if limit_choices_to is None else limit_choices_to
@@ -1304,9 +1317,13 @@ class ForeignObjectRel(object):
# __init__ as the field doesn't have its model yet. Calling these methods
# before field.contribute_to_class() has been called will result in
# AttributeError
- @cached_property
- def model(self):
- return self.to
+ @property
+ def to(self):
+ warnings.warn(
+ "Usage of ForeignObjectRel.to attribute has been deprecated. "
+ "Use the model attribute instead.",
+ RemovedInDjango21Warning, 2)
+ return self.model
@cached_property
def hidden(self):
@@ -1316,6 +1333,21 @@ class ForeignObjectRel(object):
def name(self):
return self.field.related_query_name()
+ @property
+ def remote_field(self):
+ return self.field
+
+ @property
+ def target_field(self):
+ """
+ When filtering against this relation, returns the field on the remote
+ model against which the filtering should happen.
+ """
+ target_fields = self.get_path_info()[-1].target_fields
+ if len(target_fields) > 1:
+ raise exceptions.FieldError("Can't use target_field for multicolumn relations.")
+ return target_fields[0]
+
@cached_property
def related_model(self):
if not self.field.model:
@@ -1342,13 +1374,6 @@ class ForeignObjectRel(object):
def get_prep_lookup(self, lookup_name, value):
return self.field.get_prep_lookup(lookup_name, value)
- @cached_property
- def target_field(self):
- target_fields = self.get_path_info().target_fields
- if len(target_fields) > 1:
- raise RuntimeError("Multicolumn relations do not have a single target_field.")
- return target_fields[0]
-
def get_lookup(self, lookup_name):
return self.field.get_lookup(lookup_name)
@@ -1419,7 +1444,7 @@ class ForeignObjectRel(object):
model = model or self.related_model
if self.multiple:
# If this is a symmetrical m2m relation on self, there is no reverse accessor.
- if self.symmetrical and model == self.to:
+ if self.symmetrical and model == self.model:
return None
if self.related_name:
return self.related_name
@@ -1469,14 +1494,14 @@ class ManyToOneRel(ForeignObjectRel):
"""
Return the Field in the 'to' object to which this relationship is tied.
"""
- field = self.to._meta.get_field(self.field_name)
+ field = self.model._meta.get_field(self.field_name)
if not field.concrete:
raise FieldDoesNotExist("No related field named '%s'" %
self.field_name)
return field
def set_field_name(self):
- self.field_name = self.field_name or self.to._meta.pk.name
+ self.field_name = self.field_name or self.model._meta.pk.name
class OneToOneRel(ManyToOneRel):
@@ -1540,8 +1565,8 @@ class ManyToManyRel(ForeignObjectRel):
field = opts.get_field(self.through_fields[0])
else:
for field in opts.fields:
- rel = getattr(field, 'rel', None)
- if rel and rel.to == self.to:
+ rel = getattr(field, 'remote_field', None)
+ if rel and rel.model == self.model:
break
return field.foreign_related_fields[0]
@@ -1587,7 +1612,7 @@ class ForeignObject(RelatedField):
return errors
def _check_unique_target(self):
- rel_is_string = isinstance(self.rel.to, six.string_types)
+ rel_is_string = isinstance(self.remote_field.model, six.string_types)
if rel_is_string or not self.requires_unique_target:
return []
@@ -1596,17 +1621,12 @@ class ForeignObject(RelatedField):
except FieldDoesNotExist:
return []
- try:
- self.rel
- except AttributeError:
- return []
-
has_unique_field = any(rel_field.unique
for rel_field in self.foreign_related_fields)
if not has_unique_field and len(self.foreign_related_fields) > 1:
field_combination = ', '.join("'%s'" % rel_field.name
for rel_field in self.foreign_related_fields)
- model_name = self.rel.to.__name__
+ model_name = self.remote_field.model.__name__
return [
checks.Error(
"None of the fields %s on model '%s' have a unique=True constraint."
@@ -1618,7 +1638,7 @@ class ForeignObject(RelatedField):
]
elif not has_unique_field:
field_name = self.foreign_related_fields[0].name
- model_name = self.rel.to.__name__
+ model_name = self.remote_field.model.__name__
return [
checks.Error(
("'%s.%s' must set unique=True "
@@ -1635,19 +1655,19 @@ class ForeignObject(RelatedField):
name, path, args, kwargs = super(ForeignObject, self).deconstruct()
kwargs['from_fields'] = self.from_fields
kwargs['to_fields'] = self.to_fields
- if self.rel.related_name is not None:
- kwargs['related_name'] = self.rel.related_name
- if self.rel.related_query_name is not None:
- kwargs['related_query_name'] = self.rel.related_query_name
- if self.rel.on_delete != CASCADE:
- kwargs['on_delete'] = self.rel.on_delete
- if self.rel.parent_link:
- kwargs['parent_link'] = self.rel.parent_link
+ if self.remote_field.related_name is not None:
+ kwargs['related_name'] = self.remote_field.related_name
+ if self.remote_field.related_query_name is not None:
+ kwargs['related_query_name'] = self.remote_field.related_query_name
+ if self.remote_field.on_delete != CASCADE:
+ kwargs['on_delete'] = self.remote_field.on_delete
+ if self.remote_field.parent_link:
+ kwargs['parent_link'] = self.remote_field.parent_link
# Work out string form of "to"
- if isinstance(self.rel.to, six.string_types):
- kwargs['to'] = self.rel.to
+ if isinstance(self.remote_field.model, six.string_types):
+ kwargs['to'] = self.remote_field.model
else:
- kwargs['to'] = "%s.%s" % (self.rel.to._meta.app_label, self.rel.to._meta.object_name)
+ kwargs['to'] = "%s.%s" % (self.remote_field.model._meta.app_label, self.remote_field.model._meta.object_name)
# If swappable is True, then see if we're actually pointing to the target
# of a swap.
swappable_setting = self.swappable_setting
@@ -1671,16 +1691,16 @@ class ForeignObject(RelatedField):
def resolve_related_fields(self):
if len(self.from_fields) < 1 or len(self.from_fields) != len(self.to_fields):
raise ValueError('Foreign Object from and to fields must be the same non-zero length')
- if isinstance(self.rel.to, six.string_types):
- raise ValueError('Related model %r cannot be resolved' % self.rel.to)
+ if isinstance(self.remote_field.model, six.string_types):
+ raise ValueError('Related model %r cannot be resolved' % self.remote_field.model)
related_fields = []
for index in range(len(self.from_fields)):
from_field_name = self.from_fields[index]
to_field_name = self.to_fields[index]
from_field = (self if from_field_name == 'self'
else self.opts.get_field(from_field_name))
- to_field = (self.rel.to._meta.pk if to_field_name is None
- else self.rel.to._meta.get_field(to_field_name))
+ to_field = (self.remote_field.model._meta.pk if to_field_name is None
+ else self.remote_field.model._meta.get_field(to_field_name))
related_fields.append((from_field, to_field))
return related_fields
@@ -1770,7 +1790,7 @@ class ForeignObject(RelatedField):
"""
Get path from this field to the related model.
"""
- opts = self.rel.to._meta
+ opts = self.remote_field.model._meta
from_opts = self.model._meta
return [PathInfo(from_opts, opts, self.foreign_related_fields, self, False, True)]
@@ -1779,8 +1799,8 @@ class ForeignObject(RelatedField):
Get path from the related model to this field's model.
"""
opts = self.model._meta
- from_opts = self.rel.to._meta
- pathinfos = [PathInfo(from_opts, opts, (opts.pk,), self.rel, not self.unique, False)]
+ from_opts = self.remote_field.model._meta
+ pathinfos = [PathInfo(from_opts, opts, (opts.pk,), self.remote_field, not self.unique, False)]
return pathinfos
def get_lookup(self, lookup_name):
@@ -1817,13 +1837,13 @@ class ForeignObject(RelatedField):
def contribute_to_related_class(self, cls, related):
# Internal FK's - i.e., those with a related name ending with '+' -
# and swapped models don't get a related descriptor.
- if not self.rel.is_hidden() and not related.related_model._meta.swapped:
+ if not self.remote_field.is_hidden() and not related.related_model._meta.swapped:
setattr(cls, related.get_accessor_name(), self.related_accessor_class(related))
# While 'limit_choices_to' might be a callable, simply pass
# it along for later - this is too early because it's still
# model load time.
- if self.rel.limit_choices_to:
- cls._meta.related_fkey_lookups.append(self.rel.limit_choices_to)
+ if self.remote_field.limit_choices_to:
+ cls._meta.related_fkey_lookups.append(self.remote_field.limit_choices_to)
class ForeignKey(ForeignObject):
@@ -1891,7 +1911,7 @@ class ForeignKey(ForeignObject):
return errors
def _check_on_delete(self):
- on_delete = getattr(self.rel, 'on_delete', None)
+ on_delete = getattr(self.remote_field, 'on_delete', None)
if on_delete == SET_NULL and not self.null:
return [
checks.Error(
@@ -1935,9 +1955,9 @@ class ForeignKey(ForeignObject):
if self.db_constraint is not True:
kwargs['db_constraint'] = self.db_constraint
# Rel needs more work.
- to_meta = getattr(self.rel.to, "_meta", None)
- if self.rel.field_name and (not to_meta or (to_meta.pk and self.rel.field_name != to_meta.pk.name)):
- kwargs['to_field'] = self.rel.field_name
+ to_meta = getattr(self.remote_field.model, "_meta", None)
+ if self.remote_field.field_name and (not to_meta or (to_meta.pk and self.remote_field.field_name != to_meta.pk.name)):
+ kwargs['to_field'] = self.remote_field.field_name
return name, path, args, kwargs
@property
@@ -1949,20 +1969,20 @@ class ForeignKey(ForeignObject):
Get path from the related model to this field's model.
"""
opts = self.model._meta
- from_opts = self.rel.to._meta
- pathinfos = [PathInfo(from_opts, opts, (opts.pk,), self.rel, not self.unique, False)]
+ from_opts = self.remote_field.model._meta
+ pathinfos = [PathInfo(from_opts, opts, (opts.pk,), self.remote_field, not self.unique, False)]
return pathinfos
def validate(self, value, model_instance):
- if self.rel.parent_link:
+ if self.remote_field.parent_link:
return
super(ForeignKey, self).validate(value, model_instance)
if value is None:
return
using = router.db_for_read(model_instance.__class__, instance=model_instance)
- qs = self.rel.to._default_manager.using(using).filter(
- **{self.rel.field_name: value}
+ qs = self.remote_field.model._default_manager.using(using).filter(
+ **{self.remote_field.field_name: value}
)
qs = qs.complex_filter(self.get_limit_choices_to())
if not qs.exists():
@@ -1970,8 +1990,8 @@ class ForeignKey(ForeignObject):
self.error_messages['invalid'],
code='invalid',
params={
- 'model': self.rel.to._meta.verbose_name, 'pk': value,
- 'field': self.rel.field_name, 'value': value,
+ 'model': self.remote_field.model._meta.verbose_name, 'pk': value,
+ 'field': self.remote_field.field_name, 'value': value,
}, # 'pk' is included for backwards compatibility
)
@@ -1986,7 +2006,7 @@ class ForeignKey(ForeignObject):
def get_default(self):
"Here we check if the default value is an object and return the to_field if so."
field_default = super(ForeignKey, self).get_default()
- if isinstance(field_default, self.rel.to):
+ if isinstance(field_default, self.remote_field.model):
return getattr(field_default, self.target_field.attname)
return field_default
@@ -2015,19 +2035,19 @@ class ForeignKey(ForeignObject):
def contribute_to_related_class(self, cls, related):
super(ForeignKey, self).contribute_to_related_class(cls, related)
- if self.rel.field_name is None:
- self.rel.field_name = cls._meta.pk.name
+ if self.remote_field.field_name is None:
+ self.remote_field.field_name = cls._meta.pk.name
def formfield(self, **kwargs):
db = kwargs.pop('using', None)
- if isinstance(self.rel.to, six.string_types):
+ if isinstance(self.remote_field.model, six.string_types):
raise ValueError("Cannot create form field for %r yet, because "
"its related model %r has not been loaded yet" %
- (self.name, self.rel.to))
+ (self.name, self.remote_field.model))
defaults = {
'form_class': forms.ModelChoiceField,
- 'queryset': self.rel.to._default_manager.using(db),
- 'to_field_name': self.rel.field_name,
+ 'queryset': self.remote_field.model._default_manager.using(db),
+ 'to_field_name': self.remote_field.field_name,
}
defaults.update(kwargs)
return super(ForeignKey, self).formfield(**defaults)
@@ -2095,12 +2115,12 @@ class OneToOneField(ForeignKey):
return name, path, args, kwargs
def formfield(self, **kwargs):
- if self.rel.parent_link:
+ if self.remote_field.parent_link:
return None
return super(OneToOneField, self).formfield(**kwargs)
def save_form_data(self, instance, data):
- if isinstance(data, self.rel.to):
+ if isinstance(data, self.remote_field.model):
setattr(instance, self.name, data)
else:
setattr(instance, self.attname, data)
@@ -2113,23 +2133,23 @@ class OneToOneField(ForeignKey):
def create_many_to_many_intermediary_model(field, klass):
from django.db import models
managed = True
- if isinstance(field.rel.to, six.string_types) and field.rel.to != RECURSIVE_RELATIONSHIP_CONSTANT:
- to_model = field.rel.to
+ if isinstance(field.remote_field.model, six.string_types) and field.remote_field.model != RECURSIVE_RELATIONSHIP_CONSTANT:
+ to_model = field.remote_field.model
to = to_model.split('.')[-1]
def set_managed(field, model, cls):
- field.rel.through._meta.managed = model._meta.managed or cls._meta.managed
+ field.remote_field.through._meta.managed = model._meta.managed or cls._meta.managed
add_lazy_relation(klass, field, to_model, set_managed)
- elif isinstance(field.rel.to, six.string_types):
+ elif isinstance(field.remote_field.model, six.string_types):
to = klass._meta.object_name
to_model = klass
managed = klass._meta.managed
else:
- to = field.rel.to._meta.object_name
- to_model = field.rel.to
+ to = field.remote_field.model._meta.object_name
+ to_model = field.remote_field.model
managed = klass._meta.managed or to_model._meta.managed
name = '%s_%s' % (klass._meta.object_name, field.name)
- if field.rel.to == RECURSIVE_RELATIONSHIP_CONSTANT or to == klass._meta.object_name:
+ if field.remote_field.model == RECURSIVE_RELATIONSHIP_CONSTANT or to == klass._meta.object_name:
from_ = 'from_%s' % to.lower()
to = 'to_%s' % to.lower()
else:
@@ -2154,13 +2174,13 @@ def create_many_to_many_intermediary_model(field, klass):
klass,
related_name='%s+' % name,
db_tablespace=field.db_tablespace,
- db_constraint=field.rel.db_constraint,
+ db_constraint=field.remote_field.db_constraint,
),
to: models.ForeignKey(
to_model,
related_name='%s+' % name,
db_tablespace=field.db_tablespace,
- db_constraint=field.rel.db_constraint,
+ db_constraint=field.remote_field.db_constraint,
)
})
@@ -2219,11 +2239,14 @@ class ManyToManyField(RelatedField):
through_fields=through_fields,
db_constraint=db_constraint,
)
+ self.has_null_arg = 'null' in kwargs
super(ManyToManyField, self).__init__(**kwargs)
self.db_table = db_table
self.swappable = swappable
+ # Many-to-many fields are always nullable.
+ self.null = True
def check(self, **kwargs):
errors = super(ManyToManyField, self).check(**kwargs)
@@ -2247,7 +2270,7 @@ class ManyToManyField(RelatedField):
def _check_ignored_options(self, **kwargs):
warnings = []
- if self.null:
+ if self.has_null_arg:
warnings.append(
checks.Warning(
'null has no effect on ManyToManyField.',
@@ -2270,15 +2293,15 @@ class ManyToManyField(RelatedField):
return warnings
def _check_relationship_model(self, from_model=None, **kwargs):
- if hasattr(self.rel.through, '_meta'):
+ if hasattr(self.remote_field.through, '_meta'):
qualified_model_name = "%s.%s" % (
- self.rel.through._meta.app_label, self.rel.through.__name__)
+ self.remote_field.through._meta.app_label, self.remote_field.through.__name__)
else:
- qualified_model_name = self.rel.through
+ qualified_model_name = self.remote_field.through
errors = []
- if self.rel.through not in apps.get_models(include_auto_created=True):
+ if self.remote_field.through not in apps.get_models(include_auto_created=True):
# The relationship model is not installed.
errors.append(
checks.Error(
@@ -2300,18 +2323,18 @@ class ManyToManyField(RelatedField):
)
# Set some useful local variables
- to_model = self.rel.to
+ to_model = self.remote_field.model
from_model_name = from_model._meta.object_name
if isinstance(to_model, six.string_types):
to_model_name = to_model
else:
to_model_name = to_model._meta.object_name
- relationship_model_name = self.rel.through._meta.object_name
+ relationship_model_name = self.remote_field.through._meta.object_name
self_referential = from_model == to_model
# Check symmetrical attribute.
- if (self_referential and self.rel.symmetrical and
- not self.rel.through._meta.auto_created):
+ if (self_referential and self.remote_field.symmetrical and
+ not self.remote_field.through._meta.auto_created):
errors.append(
checks.Error(
'Many-to-many fields with intermediate tables must not be symmetrical.',
@@ -2323,10 +2346,10 @@ class ManyToManyField(RelatedField):
# Count foreign keys in intermediate model
if self_referential:
- seen_self = sum(from_model == getattr(field.rel, 'to', None)
- for field in self.rel.through._meta.fields)
+ seen_self = sum(from_model == getattr(field.remote_field, 'model', None)
+ for field in self.remote_field.through._meta.fields)
- if seen_self > 2 and not self.rel.through_fields:
+ if seen_self > 2 and not self.remote_field.through_fields:
errors.append(
checks.Error(
("The model is used as an intermediate model by "
@@ -2336,19 +2359,19 @@ class ManyToManyField(RelatedField):
"through_fields keyword argument.") % (self, from_model_name),
hint=("Use through_fields to specify which two "
"foreign keys Django should use."),
- obj=self.rel.through,
+ obj=self.remote_field.through,
id='fields.E333',
)
)
else:
# Count foreign keys in relationship model
- seen_from = sum(from_model == getattr(field.rel, 'to', None)
- for field in self.rel.through._meta.fields)
- seen_to = sum(to_model == getattr(field.rel, 'to', None)
- for field in self.rel.through._meta.fields)
+ seen_from = sum(from_model == getattr(field.remote_field, 'model', None)
+ for field in self.remote_field.through._meta.fields)
+ seen_to = sum(to_model == getattr(field.remote_field, 'model', None)
+ for field in self.remote_field.through._meta.fields)
- if seen_from > 1 and not self.rel.through_fields:
+ if seen_from > 1 and not self.remote_field.through_fields:
errors.append(
checks.Error(
("The model is used as an intermediate model by "
@@ -2364,7 +2387,7 @@ class ManyToManyField(RelatedField):
)
)
- if seen_to > 1 and not self.rel.through_fields:
+ if seen_to > 1 and not self.remote_field.through_fields:
errors.append(
checks.Error(
("The model is used as an intermediate model by "
@@ -2388,17 +2411,17 @@ class ManyToManyField(RelatedField):
self, from_model_name, to_model_name
),
hint=None,
- obj=self.rel.through,
+ obj=self.remote_field.through,
id='fields.E336',
)
)
# Validate `through_fields`.
- if self.rel.through_fields is not None:
+ if self.remote_field.through_fields is not None:
# Validate that we're given an iterable of at least two items
# and that none of them is "falsy".
- if not (len(self.rel.through_fields) >= 2 and
- self.rel.through_fields[0] and self.rel.through_fields[1]):
+ if not (len(self.remote_field.through_fields) >= 2 and
+ self.remote_field.through_fields[0] and self.remote_field.through_fields[1]):
errors.append(
checks.Error(
("Field specifies 'through_fields' but does not "
@@ -2422,15 +2445,15 @@ class ManyToManyField(RelatedField):
"where the field is attached to."
)
- source, through, target = from_model, self.rel.through, self.rel.to
- source_field_name, target_field_name = self.rel.through_fields[:2]
+ source, through, target = from_model, self.remote_field.through, self.remote_field.model
+ source_field_name, target_field_name = self.remote_field.through_fields[:2]
for field_name, related_model in ((source_field_name, source),
(target_field_name, target)):
possible_field_names = []
for f in through._meta.fields:
- if hasattr(f, 'rel') and getattr(f.rel, 'to', None) == related_model:
+ if hasattr(f, 'remote_field') and getattr(f.remote_field, 'model', None) == related_model:
possible_field_names.append(f.name)
if possible_field_names:
hint = ("Did you mean one of the following foreign "
@@ -2452,8 +2475,8 @@ class ManyToManyField(RelatedField):
)
)
else:
- if not (hasattr(field, 'rel') and
- getattr(field.rel, 'to', None) == related_model):
+ if not (hasattr(field, 'remote_field') and
+ getattr(field.remote_field, 'model', None) == related_model):
errors.append(
checks.Error(
"'%s.%s' is not a foreign key to '%s'." % (
@@ -2470,24 +2493,31 @@ class ManyToManyField(RelatedField):
def deconstruct(self):
name, path, args, kwargs = super(ManyToManyField, self).deconstruct()
# Handle the simpler arguments.
+ del kwargs["null"]
if self.db_table is not None:
kwargs['db_table'] = self.db_table
- if self.rel.db_constraint is not True:
- kwargs['db_constraint'] = self.rel.db_constraint
- if self.rel.related_name is not None:
- kwargs['related_name'] = self.rel.related_name
- if self.rel.related_query_name is not None:
- kwargs['related_query_name'] = self.rel.related_query_name
+ if self.remote_field.db_constraint is not True:
+ kwargs['db_constraint'] = self.remote_field.db_constraint
+ if self.remote_field.related_name is not None:
+ kwargs['related_name'] = self.remote_field.related_name
+ if self.remote_field.related_query_name is not None:
+ kwargs['related_query_name'] = self.remote_field.related_query_name
# Rel needs more work.
- if isinstance(self.rel.to, six.string_types):
- kwargs['to'] = self.rel.to
+ if isinstance(self.remote_field.model, six.string_types):
+ kwargs['to'] = self.remote_field.model
else:
- kwargs['to'] = "%s.%s" % (self.rel.to._meta.app_label, self.rel.to._meta.object_name)
- if getattr(self.rel, 'through', None) is not None:
- if isinstance(self.rel.through, six.string_types):
- kwargs['through'] = self.rel.through
- elif not self.rel.through._meta.auto_created:
- kwargs['through'] = "%s.%s" % (self.rel.through._meta.app_label, self.rel.through._meta.object_name)
+ kwargs['to'] = "%s.%s" % (
+ self.remote_field.model._meta.app_label,
+ self.remote_field.model._meta.object_name,
+ )
+ if getattr(self.remote_field, 'through', None) is not None:
+ if isinstance(self.remote_field.through, six.string_types):
+ kwargs['through'] = self.remote_field.through
+ elif not self.remote_field.through._meta.auto_created:
+ kwargs['through'] = "%s.%s" % (
+ self.remote_field.through._meta.app_label,
+ self.remote_field.through._meta.object_name,
+ )
# If swappable is True, then see if we're actually pointing to the target
# of a swap.
swappable_setting = self.swappable_setting
@@ -2513,7 +2543,7 @@ class ManyToManyField(RelatedField):
Called by both direct and indirect m2m traversal.
"""
pathinfos = []
- int_model = self.rel.through
+ int_model = self.remote_field.through
linkfield1 = int_model._meta.get_field(self.m2m_field_name())
linkfield2 = int_model._meta.get_field(self.m2m_reverse_field_name())
if direct:
@@ -2540,8 +2570,8 @@ class ManyToManyField(RelatedField):
Function that can be curried to provide the m2m table name for this
relation.
"""
- if self.rel.through is not None:
- return self.rel.through._meta.db_table
+ if self.remote_field.through is not None:
+ return self.remote_field.through._meta.db_table
elif self.db_table:
return self.db_table
else:
@@ -2556,12 +2586,12 @@ class ManyToManyField(RelatedField):
cache_attr = '_m2m_%s_cache' % attr
if hasattr(self, cache_attr):
return getattr(self, cache_attr)
- if self.rel.through_fields is not None:
- link_field_name = self.rel.through_fields[0]
+ if self.remote_field.through_fields is not None:
+ link_field_name = self.remote_field.through_fields[0]
else:
link_field_name = None
- for f in self.rel.through._meta.fields:
- if (f.is_relation and f.rel.to == related.related_model and
+ for f in self.remote_field.through._meta.fields:
+ if (f.is_relation and f.remote_field.model == related.related_model and
(link_field_name is None or link_field_name == f.name)):
setattr(self, cache_attr, getattr(f, attr))
return getattr(self, cache_attr)
@@ -2575,12 +2605,12 @@ class ManyToManyField(RelatedField):
if hasattr(self, cache_attr):
return getattr(self, cache_attr)
found = False
- if self.rel.through_fields is not None:
- link_field_name = self.rel.through_fields[1]
+ if self.remote_field.through_fields is not None:
+ link_field_name = self.remote_field.through_fields[1]
else:
link_field_name = None
- for f in self.rel.through._meta.fields:
- if f.is_relation and f.rel.to == related.model:
+ for f in self.remote_field.through._meta.fields:
+ if f.is_relation and f.remote_field.model == related.model:
if link_field_name is None and related.related_model == related.model:
# If this is an m2m-intermediate to self,
# the first foreign key you find will be
@@ -2617,8 +2647,9 @@ class ManyToManyField(RelatedField):
# specify *what* on my non-reversible relation?!"), so we set it up
# automatically. The funky name reduces the chance of an accidental
# clash.
- if self.rel.symmetrical and (self.rel.to == "self" or self.rel.to == cls._meta.object_name):
- self.rel.related_name = "%s_rel_+" % name
+ if self.remote_field.symmetrical and (
+ self.remote_field.model == "self" or self.remote_field.model == cls._meta.object_name):
+ self.remote_field.related_name = "%s_rel_+" % name
super(ManyToManyField, self).contribute_to_class(cls, name, **kwargs)
@@ -2626,27 +2657,27 @@ class ManyToManyField(RelatedField):
# 1) There is a manually specified intermediate, or
# 2) The class owning the m2m field is abstract.
# 3) The class owning the m2m field has been swapped out.
- if not self.rel.through and not cls._meta.abstract and not cls._meta.swapped:
- self.rel.through = create_many_to_many_intermediary_model(self, cls)
+ if not self.remote_field.through and not cls._meta.abstract and not cls._meta.swapped:
+ self.remote_field.through = create_many_to_many_intermediary_model(self, cls)
# Add the descriptor for the m2m relation.
- setattr(cls, self.name, ManyRelatedObjectsDescriptor(self.rel, reverse=False))
+ setattr(cls, self.name, ManyRelatedObjectsDescriptor(self.remote_field, reverse=False))
# Set up the accessor for the m2m table name for the relation.
self.m2m_db_table = curry(self._get_m2m_db_table, cls._meta)
# Populate some necessary rel arguments so that cross-app relations
# work correctly.
- if not cls._meta.abstract and isinstance(self.rel.through, six.string_types):
+ if not cls._meta.abstract and isinstance(self.remote_field.through, six.string_types):
def resolve_through_model(field, model, cls):
- field.rel.through = model
- add_lazy_relation(cls, self, self.rel.through, resolve_through_model)
+ field.remote_field.through = model
+ add_lazy_relation(cls, self, self.remote_field.through, resolve_through_model)
def contribute_to_related_class(self, cls, related):
# Internal M2Ms (i.e., those with a related name ending with '+')
# and swapped models don't get a related descriptor.
- if not self.rel.is_hidden() and not related.related_model._meta.swapped:
- setattr(cls, related.get_accessor_name(), ManyRelatedObjectsDescriptor(self.rel, reverse=True))
+ if not self.remote_field.is_hidden() and not related.related_model._meta.swapped:
+ setattr(cls, related.get_accessor_name(), ManyRelatedObjectsDescriptor(self.remote_field, reverse=True))
# Set up the accessors for the column names on the m2m table.
self.m2m_column_name = curry(self._get_m2m_attr, related, 'column')
@@ -2655,9 +2686,9 @@ class ManyToManyField(RelatedField):
self.m2m_field_name = curry(self._get_m2m_attr, related, 'name')
self.m2m_reverse_field_name = curry(self._get_m2m_reverse_attr, related, 'name')
- get_m2m_rel = curry(self._get_m2m_attr, related, 'rel')
+ get_m2m_rel = curry(self._get_m2m_attr, related, 'remote_field')
self.m2m_target_field_name = lambda: get_m2m_rel().field_name
- get_m2m_reverse_rel = curry(self._get_m2m_reverse_attr, related, 'rel')
+ get_m2m_reverse_rel = curry(self._get_m2m_reverse_attr, related, 'remote_field')
self.m2m_reverse_target_field_name = lambda: get_m2m_reverse_rel().field_name
def set_attributes_from_rel(self):
@@ -2676,7 +2707,7 @@ class ManyToManyField(RelatedField):
db = kwargs.pop('using', None)
defaults = {
'form_class': forms.ModelMultipleChoiceField,
- 'queryset': self.rel.to._default_manager.using(db),
+ 'queryset': self.remote_field.model._default_manager.using(db),
}
defaults.update(kwargs)
# If initial is passed in, it's a list of related objects, but the
diff --git a/django/db/models/fields/related_lookups.py b/django/db/models/fields/related_lookups.py
index b689c9928e0..1588128ed11 100644
--- a/django/db/models/fields/related_lookups.py
+++ b/django/db/models/fields/related_lookups.py
@@ -26,8 +26,8 @@ def get_normalized_value(value, lhs):
# Account for one-to-one relations when sent a different model
sources = lhs.output_field.get_path_info()[-1].target_fields
for source in sources:
- while not isinstance(value, source.model) and source.rel:
- source = source.rel.to._meta.get_field(source.rel.field_name)
+ while not isinstance(value, source.model) and source.remote_field:
+ source = source.remote_field.model._meta.get_field(source.remote_field.field_name)
value_list.append(getattr(value, source.attname))
return tuple(value_list)
if not isinstance(value, tuple):
diff --git a/django/db/models/options.py b/django/db/models/options.py
index 4a2ab1036f0..91b6790b84e 100644
--- a/django/db/models/options.py
+++ b/django/db/models/options.py
@@ -308,9 +308,9 @@ class Options(object):
# ideally, we'd just ask for field.related_model. However, related_model
# is a cached property, and all the models haven't been loaded yet, so
# we need to make sure we don't cache a string reference.
- if field.is_relation and hasattr(field.rel, 'to') and field.rel.to:
+ if field.is_relation and hasattr(field.remote_field, 'model') and field.remote_field.model:
try:
- field.rel.to._meta._expire_cache(forward=False)
+ field.remote_field.model._meta._expire_cache(forward=False)
except AttributeError:
pass
self._expire_cache()
@@ -393,7 +393,7 @@ class Options(object):
is_not_an_m2m_field = lambda f: not (f.is_relation and f.many_to_many)
is_not_a_generic_relation = lambda f: not (f.is_relation and f.one_to_many)
is_not_a_generic_foreign_key = lambda f: not (
- f.is_relation and f.many_to_one and not (hasattr(f.rel, 'to') and f.rel.to)
+ f.is_relation and f.many_to_one and not (hasattr(f.remote_field, 'model') and f.remote_field.model)
)
return make_immutable_fields_list(
"fields",
@@ -592,8 +592,8 @@ class Options(object):
children = chain.from_iterable(c._relation_tree
for c in self.concrete_model._meta.proxied_children
if c is not self)
- relations = (f.rel for f in children
- if include_hidden or not f.rel.field.rel.is_hidden())
+ 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)
@@ -690,8 +690,8 @@ class Options(object):
if f.is_relation and f.related_model is not None
)
for f in fields_with_relations:
- if not isinstance(f.rel.to, six.string_types):
- related_objects_graph[f.rel.to._meta].append(f)
+ if not isinstance(f.remote_field.model, six.string_types):
+ related_objects_graph[f.remote_field.model._meta].append(f)
for model in all_models:
# Set the relation_tree using the internal __dict__. In this way
@@ -804,8 +804,8 @@ class Options(object):
for field in all_fields:
# If hidden fields should be included or the relation is not
# intentionally hidden, add to the fields dict.
- if include_hidden or not field.rel.hidden:
- fields.append(field.rel)
+ if include_hidden or not field.remote_field.hidden:
+ fields.append(field.remote_field)
if forward:
fields.extend(
diff --git a/django/db/models/query.py b/django/db/models/query.py
index b50650b057b..5f455b12beb 100644
--- a/django/db/models/query.py
+++ b/django/db/models/query.py
@@ -1645,12 +1645,12 @@ class RelatedPopulator(object):
reverse = klass_info['reverse']
self.reverse_cache_name = None
if reverse:
- self.cache_name = field.rel.get_cache_name()
+ self.cache_name = field.remote_field.get_cache_name()
self.reverse_cache_name = field.get_cache_name()
else:
self.cache_name = field.get_cache_name()
if field.unique:
- self.reverse_cache_name = field.rel.get_cache_name()
+ self.reverse_cache_name = field.remote_field.get_cache_name()
def get_deferred_cls(self, klass_info, init_list):
model_cls = klass_info['model']
diff --git a/django/db/models/query_utils.py b/django/db/models/query_utils.py
index 35e3fe58bce..d8bf9b8e677 100644
--- a/django/db/models/query_utils.py
+++ b/django/db/models/query_utils.py
@@ -181,9 +181,9 @@ def select_related_descend(field, restricted, requested, load_fields, reverse=Fa
* load_fields - the set of fields to be loaded on this model
* reverse - boolean, True if we are checking a reverse select related
"""
- if not field.rel:
+ if not field.remote_field:
return False
- if field.rel.parent_link and not reverse:
+ if field.remote_field.parent_link and not reverse:
return False
if restricted:
if reverse and field.related_query_name() not in requested:
diff --git a/django/db/models/sql/compiler.py b/django/db/models/sql/compiler.py
index d54c1bc5e42..002bc058243 100644
--- a/django/db/models/sql/compiler.py
+++ b/django/db/models/sql/compiler.py
@@ -676,7 +676,7 @@ class SQLCompiler(object):
only_load.get(field_model)):
continue
klass_info = {
- 'model': f.rel.to,
+ 'model': f.remote_field.model,
'field': f,
'reverse': False,
'from_parent': False,
@@ -686,13 +686,13 @@ class SQLCompiler(object):
_, _, _, joins, _ = self.query.setup_joins(
[f.name], opts, root_alias)
alias = joins[-1]
- columns = self.get_default_columns(start_alias=alias, opts=f.rel.to._meta)
+ columns = self.get_default_columns(start_alias=alias, opts=f.remote_field.model._meta)
for col in columns:
select_fields.append(len(select))
select.append((col, None))
klass_info['select_fields'] = select_fields
next_klass_infos = self.get_related_selections(
- select, f.rel.to._meta, alias, cur_depth + 1, next, restricted)
+ select, f.remote_field.model._meta, alias, cur_depth + 1, next, restricted)
get_related_klass_infos(klass_info, next_klass_infos)
if restricted:
@@ -1003,7 +1003,7 @@ class SQLUpdateCompiler(SQLCompiler):
if val.contains_aggregate:
raise FieldError("Aggregate functions are not allowed in this query")
elif hasattr(val, 'prepare_database_save'):
- if field.rel:
+ if field.remote_field:
val = val.prepare_database_save(field)
else:
raise TypeError("Database is trying to update a relational field "
diff --git a/django/db/models/sql/query.py b/django/db/models/sql/query.py
index c041ef0b071..69178680ee8 100644
--- a/django/db/models/sql/query.py
+++ b/django/db/models/sql/query.py
@@ -608,7 +608,7 @@ class Query(object):
if is_reverse_o2o(source):
cur_model = source.related_model
else:
- cur_model = source.rel.to
+ cur_model = source.remote_field.model
opts = cur_model._meta
# Even if we're "just passing through" this model, we must add
# both the current model's pk and the related reference field
@@ -1303,7 +1303,7 @@ class Query(object):
opts = int_model._meta
else:
final_field = opts.parents[int_model]
- targets = (final_field.rel.get_related_field(),)
+ targets = (final_field.remote_field.get_related_field(),)
opts = int_model._meta
path.append(PathInfo(final_field.model._meta, opts, targets, final_field, False, True))
cur_names_with_path[1].append(
diff --git a/django/forms/models.py b/django/forms/models.py
index 1a0076eaee2..59f9d501d51 100644
--- a/django/forms/models.py
+++ b/django/forms/models.py
@@ -589,8 +589,8 @@ class BaseModelFormSet(BaseFormSet):
If the field is a related field, fetch the concrete field's (that
is, the ultimate pointed-to field's) to_python.
"""
- while field.rel is not None:
- field = field.rel.get_related_field()
+ while field.remote_field is not None:
+ field = field.remote_field.get_related_field()
return field.to_python
def _construct_form(self, i, **kwargs):
@@ -797,7 +797,7 @@ class BaseModelFormSet(BaseFormSet):
def pk_is_not_editable(pk):
return ((not pk.editable) or (pk.auto_created or isinstance(pk, AutoField))
- or (pk.rel and pk.rel.parent_link and pk_is_not_editable(pk.rel.to._meta.pk)))
+ or (pk.remote_field and pk.remote_field.parent_link and pk_is_not_editable(pk.remote_field.model._meta.pk)))
if pk_is_not_editable(pk) or pk.name not in form.fields:
if form.is_bound:
# If we're adding the related instance, ignore its primary key
@@ -813,7 +813,7 @@ class BaseModelFormSet(BaseFormSet):
except IndexError:
pk_value = None
if isinstance(pk, OneToOneField) or isinstance(pk, ForeignKey):
- qs = pk.rel.to._default_manager.get_queryset()
+ qs = pk.remote_field.model._default_manager.get_queryset()
else:
qs = self.model._default_manager.get_queryset()
qs = qs.using(form.instance._state.db)
@@ -863,7 +863,7 @@ class BaseInlineFormSet(BaseModelFormSet):
def __init__(self, data=None, files=None, instance=None,
save_as_new=False, prefix=None, queryset=None, **kwargs):
if instance is None:
- self.instance = self.fk.rel.to()
+ self.instance = self.fk.remote_field.model()
else:
self.instance = instance
self.save_as_new = save_as_new
@@ -893,15 +893,15 @@ class BaseInlineFormSet(BaseModelFormSet):
# Set the fk value here so that the form can do its validation.
fk_value = self.instance.pk
- if self.fk.rel.field_name != self.fk.rel.to._meta.pk.name:
- fk_value = getattr(self.instance, self.fk.rel.field_name)
+ if self.fk.remote_field.field_name != self.fk.remote_field.model._meta.pk.name:
+ fk_value = getattr(self.instance, self.fk.remote_field.field_name)
fk_value = getattr(fk_value, 'pk', fk_value)
setattr(form.instance, self.fk.get_attname(), fk_value)
return form
@classmethod
def get_default_prefix(cls):
- return cls.fk.rel.get_accessor_name(model=cls.model).replace('+', '')
+ return cls.fk.remote_field.get_accessor_name(model=cls.model).replace('+', '')
def save_new(self, form, commit=True):
# Ensure the latest copy of the related instance is present on each
@@ -911,7 +911,7 @@ class BaseInlineFormSet(BaseModelFormSet):
# Use commit=False so we can assign the parent key afterwards, then
# save the object.
obj = form.save(commit=False)
- pk_value = getattr(self.instance, self.fk.rel.field_name)
+ pk_value = getattr(self.instance, self.fk.remote_field.field_name)
setattr(obj, self.fk.get_attname(), getattr(pk_value, 'pk', pk_value))
if commit:
obj.save()
@@ -932,8 +932,8 @@ class BaseInlineFormSet(BaseModelFormSet):
kwargs = {
'label': getattr(form.fields.get(name), 'label', capfirst(self.fk.verbose_name))
}
- if self.fk.rel.field_name != self.fk.rel.to._meta.pk.name:
- kwargs['to_field'] = self.fk.rel.field_name
+ if self.fk.remote_field.field_name != self.fk.remote_field.model._meta.pk.name:
+ kwargs['to_field'] = self.fk.remote_field.field_name
# If we're adding a new object, ignore a parent's auto-generated pk
# as it will be regenerated on the save request.
@@ -970,8 +970,8 @@ def _get_foreign_key(parent_model, model, fk_name=None, can_fail=False):
if len(fks_to_parent) == 1:
fk = fks_to_parent[0]
if not isinstance(fk, ForeignKey) or \
- (fk.rel.to != parent_model and
- fk.rel.to not in parent_model._meta.get_parent_list()):
+ (fk.remote_field.model != parent_model and
+ fk.remote_field.model not in parent_model._meta.get_parent_list()):
raise ValueError(
"fk_name '%s' is not a ForeignKey to '%s.%s'."
% (fk_name, parent_model._meta.app_label, parent_model._meta.object_name))
@@ -984,8 +984,8 @@ def _get_foreign_key(parent_model, model, fk_name=None, can_fail=False):
fks_to_parent = [
f for f in opts.fields
if isinstance(f, ForeignKey)
- and (f.rel.to == parent_model
- or f.rel.to in parent_model._meta.get_parent_list())
+ and (f.remote_field.model == parent_model
+ or f.remote_field.model in parent_model._meta.get_parent_list())
]
if len(fks_to_parent) == 1:
fk = fks_to_parent[0]
diff --git a/docs/internals/deprecation.txt b/docs/internals/deprecation.txt
index db351ede16c..abce521d757 100644
--- a/docs/internals/deprecation.txt
+++ b/docs/internals/deprecation.txt
@@ -26,6 +26,10 @@ details on these changes.
compatibility layer which allows absolute URLs to be considered equal to
relative ones when the path is identical will also be removed.
+* ``Field.rel`` will be removed.
+
+* ``Field.remote_field.to`` attribute will be removed.
+
.. _deprecation-removed-in-2.0:
2.0
diff --git a/docs/releases/1.9.txt b/docs/releases/1.9.txt
index 10a4db55182..9f15e9151ab 100644
--- a/docs/releases/1.9.txt
+++ b/docs/releases/1.9.txt
@@ -348,6 +348,15 @@ versions:
Its parsing caused bugs with the current syntax, so support for the old syntax
will be removed in Django 2.0 following an accelerated deprecation.
+``Field.rel`` changes
+~~~~~~~~~~~~~~~~~~~~~
+
+``Field.rel`` and its methods and attributes have changed to match the related
+fields API. The ``Field.rel`` attribute is renamed to ``remote_field`` and many
+of its methods and attributes are either changed or renamed.
+
+The aim of these changes is to provide a documented API for relation fields.
+
Miscellaneous
~~~~~~~~~~~~~
diff --git a/tests/admin_widgets/tests.py b/tests/admin_widgets/tests.py
index d32411ef674..5083295c467 100644
--- a/tests/admin_widgets/tests.py
+++ b/tests/admin_widgets/tests.py
@@ -437,7 +437,7 @@ class ForeignKeyRawIdWidgetTest(DjangoTestCase):
band.album_set.create(
name='Hybrid Theory', cover_art=r'albums\hybrid_theory.jpg'
)
- rel = models.Album._meta.get_field('band').rel
+ rel = models.Album._meta.get_field('band').remote_field
w = widgets.ForeignKeyRawIdWidget(rel, widget_admin_site)
self.assertHTMLEqual(
@@ -456,7 +456,7 @@ class ForeignKeyRawIdWidgetTest(DjangoTestCase):
core = models.Inventory.objects.create(
barcode=87, name='Core', parent=apple
)
- rel = models.Inventory._meta.get_field('parent').rel
+ rel = models.Inventory._meta.get_field('parent').remote_field
w = widgets.ForeignKeyRawIdWidget(rel, widget_admin_site)
self.assertHTMLEqual(
w.render('test', core.parent_id, attrs={}), (
@@ -471,7 +471,7 @@ class ForeignKeyRawIdWidgetTest(DjangoTestCase):
# have no magnifying glass link. See #16542
big_honeycomb = models.Honeycomb.objects.create(location='Old tree')
big_honeycomb.bee_set.create()
- rel = models.Bee._meta.get_field('honeycomb').rel
+ rel = models.Bee._meta.get_field('honeycomb').remote_field
w = widgets.ForeignKeyRawIdWidget(rel, widget_admin_site)
self.assertHTMLEqual(
@@ -484,7 +484,7 @@ class ForeignKeyRawIdWidgetTest(DjangoTestCase):
# no magnifying glass link. See #16542
subject1 = models.Individual.objects.create(name='Subject #1')
models.Individual.objects.create(name='Child', parent=subject1)
- rel = models.Individual._meta.get_field('parent').rel
+ rel = models.Individual._meta.get_field('parent').remote_field
w = widgets.ForeignKeyRawIdWidget(rel, widget_admin_site)
self.assertHTMLEqual(
@@ -494,7 +494,7 @@ class ForeignKeyRawIdWidgetTest(DjangoTestCase):
def test_proper_manager_for_label_lookup(self):
# see #9258
- rel = models.Inventory._meta.get_field('parent').rel
+ rel = models.Inventory._meta.get_field('parent').remote_field
w = widgets.ForeignKeyRawIdWidget(rel, widget_admin_site)
hidden = models.Inventory.objects.create(
@@ -521,7 +521,7 @@ class ManyToManyRawIdWidgetTest(DjangoTestCase):
m1 = models.Member.objects.create(name='Chester')
m2 = models.Member.objects.create(name='Mike')
band.members.add(m1, m2)
- rel = models.Band._meta.get_field('members').rel
+ rel = models.Band._meta.get_field('members').remote_field
w = widgets.ManyToManyRawIdWidget(rel, widget_admin_site)
self.assertHTMLEqual(
@@ -546,7 +546,7 @@ class ManyToManyRawIdWidgetTest(DjangoTestCase):
c1 = models.Company.objects.create(name='Doodle')
c2 = models.Company.objects.create(name='Pear')
consultor1.companies.add(c1, c2)
- rel = models.Advisor._meta.get_field('companies').rel
+ rel = models.Advisor._meta.get_field('companies').remote_field
w = widgets.ManyToManyRawIdWidget(rel, widget_admin_site)
self.assertHTMLEqual(
@@ -562,14 +562,14 @@ class ManyToManyRawIdWidgetTest(DjangoTestCase):
class RelatedFieldWidgetWrapperTests(DjangoTestCase):
def test_no_can_add_related(self):
- rel = models.Individual._meta.get_field('parent').rel
+ rel = models.Individual._meta.get_field('parent').remote_field
w = widgets.AdminRadioSelect()
# Used to fail with a name error.
w = widgets.RelatedFieldWidgetWrapper(w, rel, widget_admin_site)
self.assertFalse(w.can_add_related)
def test_select_multiple_widget_cant_change_delete_related(self):
- rel = models.Individual._meta.get_field('parent').rel
+ rel = models.Individual._meta.get_field('parent').remote_field
widget = forms.SelectMultiple()
wrapper = widgets.RelatedFieldWidgetWrapper(
widget, rel, widget_admin_site,
@@ -582,7 +582,7 @@ class RelatedFieldWidgetWrapperTests(DjangoTestCase):
self.assertFalse(wrapper.can_delete_related)
def test_on_delete_cascade_rel_cant_delete_related(self):
- rel = models.Individual._meta.get_field('soulmate').rel
+ rel = models.Individual._meta.get_field('soulmate').remote_field
widget = forms.Select()
wrapper = widgets.RelatedFieldWidgetWrapper(
widget, rel, widget_admin_site,
diff --git a/tests/backends/tests.py b/tests/backends/tests.py
index fcd165146e5..737efae411d 100644
--- a/tests/backends/tests.py
+++ b/tests/backends/tests.py
@@ -1058,7 +1058,7 @@ class DBConstraintTestCase(TestCase):
self.assertEqual(models.Object.objects.count(), 2)
self.assertEqual(obj.related_objects.count(), 1)
- intermediary_model = models.Object._meta.get_field("related_objects").rel.through
+ intermediary_model = models.Object._meta.get_field("related_objects").remote_field.through
intermediary_model.objects.create(from_object_id=obj.id, to_object_id=12345)
self.assertEqual(obj.related_objects.count(), 1)
self.assertEqual(intermediary_model.objects.count(), 2)
diff --git a/tests/basic/tests.py b/tests/basic/tests.py
index 934c5587f41..d2a96799cf2 100644
--- a/tests/basic/tests.py
+++ b/tests/basic/tests.py
@@ -766,5 +766,5 @@ class TestRelatedObjectDeprecation(TestCase):
self.assertEqual(len(warns), 1)
self.assertEqual(
str(warns.pop().message),
- 'Usage of field.related has been deprecated. Use field.rel instead.'
+ 'Usage of field.related has been deprecated. Use field.remote_field instead.'
)
diff --git a/tests/delete/tests.py b/tests/delete/tests.py
index 9465340d0be..5330b4a13b3 100644
--- a/tests/delete/tests.py
+++ b/tests/delete/tests.py
@@ -153,7 +153,7 @@ class DeletionTests(TestCase):
r = R.objects.create()
m.m2m.add(r)
r.delete()
- through = M._meta.get_field('m2m').rel.through
+ through = M._meta.get_field('m2m').remote_field.through
self.assertFalse(through.objects.exists())
r = R.objects.create()
diff --git a/tests/field_deconstruction/tests.py b/tests/field_deconstruction/tests.py
index bbbbefa7e9f..30c5e892131 100644
--- a/tests/field_deconstruction/tests.py
+++ b/tests/field_deconstruction/tests.py
@@ -178,8 +178,8 @@ class FieldDeconstructionTests(TestCase):
# Test basic pointing
from django.contrib.auth.models import Permission
field = models.ForeignKey("auth.Permission")
- field.rel.to = Permission
- field.rel.field_name = "id"
+ field.remote_field.model = Permission
+ field.remote_field.field_name = "id"
name, path, args, kwargs = field.deconstruct()
self.assertEqual(path, "django.db.models.ForeignKey")
self.assertEqual(args, [])
diff --git a/tests/foreign_object/models.py b/tests/foreign_object/models.py
index 1e3214fec7e..177a2007587 100644
--- a/tests/foreign_object/models.py
+++ b/tests/foreign_object/models.py
@@ -109,7 +109,7 @@ class ArticleTranslationDescriptor(ReverseSingleRelatedObjectDescriptor):
if instance is None:
raise AttributeError("%s must be accessed via instance" % self.field.name)
setattr(instance, self.cache_name, value)
- if value is not None and not self.field.rel.multiple:
+ if value is not None and not self.field.remote_field.multiple:
setattr(value, self.field.related.get_cache_name(), instance)
diff --git a/tests/many_to_one/tests.py b/tests/many_to_one/tests.py
index af7503fba87..d828abe8c26 100644
--- a/tests/many_to_one/tests.py
+++ b/tests/many_to_one/tests.py
@@ -565,12 +565,12 @@ class ManyToOneTests(TestCase):
p = Parent()
with self.assertRaisesMessage(ValueError,
'Cannot assign "%r": "%s" instance isn\'t saved in the database.'
- % (p, Child.parent.field.rel.to._meta.object_name)):
+ % (p, Child.parent.field.remote_field.model._meta.object_name)):
Child(parent=p)
with self.assertRaisesMessage(ValueError,
'Cannot assign "%r": "%s" instance isn\'t saved in the database.'
- % (p, Child.parent.field.rel.to._meta.object_name)):
+ % (p, Child.parent.field.remote_field.model._meta.object_name)):
ToFieldChild(parent=p)
# Creation using attname keyword argument and an id will cause the
@@ -610,7 +610,7 @@ class ManyToOneTests(TestCase):
# Regression for #12190 -- Should be able to instantiate a FK outside
# of a model, and interrogate its related field.
cat = models.ForeignKey(Category)
- self.assertEqual('id', cat.rel.get_related_field().name)
+ self.assertEqual('id', cat.remote_field.get_related_field().name)
def test_relation_unsaved(self):
# Test that the _set manager does not join on Null value fields (#17541)
diff --git a/tests/migrations/test_autodetector.py b/tests/migrations/test_autodetector.py
index 2d45dac7f82..59654f95393 100644
--- a/tests/migrations/test_autodetector.py
+++ b/tests/migrations/test_autodetector.py
@@ -1068,7 +1068,7 @@ class AutodetectorTests(TestCase):
autodetector = MigrationAutodetector(before, after)
changes = autodetector._detect_changes()
# The field name the FK on the book model points to
- self.assertEqual(changes['otherapp'][0].operations[0].fields[2][1].rel.field_name, 'id')
+ self.assertEqual(changes['otherapp'][0].operations[0].fields[2][1].remote_field.field_name, 'id')
# Now, we test the custom pk field name
before = self.make_project_state([])
@@ -1076,7 +1076,7 @@ class AutodetectorTests(TestCase):
autodetector = MigrationAutodetector(before, after)
changes = autodetector._detect_changes()
# The field name the FK on the book model points to
- self.assertEqual(changes['otherapp'][0].operations[0].fields[2][1].rel.field_name, 'pk_field')
+ self.assertEqual(changes['otherapp'][0].operations[0].fields[2][1].remote_field.field_name, 'pk_field')
def test_unmanaged_create(self):
"""Tests that the autodetector correctly deals with managed models."""
@@ -1126,7 +1126,7 @@ class AutodetectorTests(TestCase):
autodetector = MigrationAutodetector(before, after)
changes = autodetector._detect_changes()
# The field name the FK on the book model points to
- self.assertEqual(changes['otherapp'][0].operations[0].fields[2][1].rel.field_name, 'id')
+ self.assertEqual(changes['otherapp'][0].operations[0].fields[2][1].remote_field.field_name, 'id')
# Now, we test the custom pk field name
before = self.make_project_state([])
@@ -1134,7 +1134,7 @@ class AutodetectorTests(TestCase):
autodetector = MigrationAutodetector(before, after)
changes = autodetector._detect_changes()
# The field name the FK on the book model points to
- self.assertEqual(changes['otherapp'][0].operations[0].fields[2][1].rel.field_name, 'pk_field')
+ self.assertEqual(changes['otherapp'][0].operations[0].fields[2][1].remote_field.field_name, 'pk_field')
@override_settings(AUTH_USER_MODEL="thirdapp.CustomUser")
def test_swappable(self):
@@ -1159,7 +1159,7 @@ class AutodetectorTests(TestCase):
self.assertOperationTypes(changes, 'testapp', 0, ["AlterField"])
self.assertOperationAttributes(changes, 'testapp', 0, 0, model_name="author", name='user')
fk_field = changes['testapp'][0].operations[0].field
- to_model = '%s.%s' % (fk_field.rel.to._meta.app_label, fk_field.rel.to._meta.object_name)
+ to_model = '%s.%s' % (fk_field.remote_field.model._meta.app_label, fk_field.remote_field.model._meta.object_name)
self.assertEqual(to_model, 'thirdapp.CustomUser')
def test_add_field_with_default(self):
diff --git a/tests/migrations/test_operations.py b/tests/migrations/test_operations.py
index a3511540589..b5faa8f0f67 100644
--- a/tests/migrations/test_operations.py
+++ b/tests/migrations/test_operations.py
@@ -479,7 +479,7 @@ class OperationTests(OperationTestBase):
self.assertNotIn(("test_rnmo", "pony"), new_state.models)
self.assertIn(("test_rnmo", "horse"), new_state.models)
# RenameModel also repoints all incoming FKs and M2Ms
- self.assertEqual("test_rnmo.Horse", new_state.models["test_rnmo", "rider"].fields[1][1].rel.to)
+ self.assertEqual("test_rnmo.Horse", new_state.models["test_rnmo", "rider"].fields[1][1].remote_field.model)
self.assertTableNotExists("test_rnmo_pony")
self.assertTableExists("test_rnmo_horse")
if connection.features.supports_foreign_keys:
@@ -490,7 +490,7 @@ class OperationTests(OperationTestBase):
# Test original state and database
self.assertIn(("test_rnmo", "pony"), original_state.models)
self.assertNotIn(("test_rnmo", "horse"), original_state.models)
- self.assertEqual("Pony", original_state.models["test_rnmo", "rider"].fields[1][1].rel.to)
+ self.assertEqual("Pony", original_state.models["test_rnmo", "rider"].fields[1][1].remote_field.model)
self.assertTableExists("test_rnmo_pony")
self.assertTableNotExists("test_rnmo_horse")
if connection.features.supports_foreign_keys:
@@ -515,7 +515,7 @@ class OperationTests(OperationTestBase):
self.assertNotIn(("test_rmwsrf", "rider"), new_state.models)
self.assertIn(("test_rmwsrf", "horserider"), new_state.models)
# Remember, RenameModel also repoints all incoming FKs and M2Ms
- self.assertEqual("test_rmwsrf.HorseRider", new_state.models["test_rmwsrf", "horserider"].fields[2][1].rel.to)
+ self.assertEqual("test_rmwsrf.HorseRider", new_state.models["test_rmwsrf", "horserider"].fields[2][1].remote_field.model)
# Test the database alteration
self.assertTableExists("test_rmwsrf_rider")
self.assertTableNotExists("test_rmwsrf_horserider")
@@ -553,8 +553,8 @@ class OperationTests(OperationTestBase):
self.assertIn(("test_rmwsc", "littlehorse"), new_state.models)
# RenameModel shouldn't repoint the superclass's relations, only local ones
self.assertEqual(
- project_state.models["test_rmwsc", "rider"].fields[1][1].rel.to,
- new_state.models["test_rmwsc", "rider"].fields[1][1].rel.to
+ project_state.models["test_rmwsc", "rider"].fields[1][1].remote_field.model,
+ new_state.models["test_rmwsc", "rider"].fields[1][1].remote_field.model
)
# Before running the migration we have a table for Shetland Pony, not Little Horse
self.assertTableExists("test_rmwsc_shetlandpony")
@@ -612,7 +612,7 @@ class OperationTests(OperationTestBase):
pony.riders.add(rider)
self.assertEqual(Pony.objects.count(), 2)
self.assertEqual(Rider.objects.count(), 2)
- self.assertEqual(Pony._meta.get_field('riders').rel.through.objects.count(), 2)
+ self.assertEqual(Pony._meta.get_field('riders').remote_field.through.objects.count(), 2)
def test_add_field(self):
"""
diff --git a/tests/model_fields/test_field_flags.py b/tests/model_fields/test_field_flags.py
index e86ee3a5281..47aa0771f1b 100644
--- a/tests/model_fields/test_field_flags.py
+++ b/tests/model_fields/test_field_flags.py
@@ -155,7 +155,7 @@ class FieldFlagsTests(test.TestCase):
# Ensure all m2m reverses are m2m
for field in m2m_type_fields:
- reverse_field = field.rel
+ reverse_field = field.remote_field
self.assertTrue(reverse_field.is_relation)
self.assertTrue(reverse_field.many_to_many)
self.assertTrue(reverse_field.related_model)
@@ -171,7 +171,7 @@ class FieldFlagsTests(test.TestCase):
# Ensure all o2m reverses are m2o
for field in o2m_type_fields:
if field.concrete:
- reverse_field = field.rel
+ reverse_field = field.remote_field
self.assertTrue(reverse_field.is_relation and reverse_field.many_to_one)
def test_cardinality_m2o(self):
@@ -213,6 +213,6 @@ class FieldFlagsTests(test.TestCase):
for field in AllFieldsModel._meta.get_fields():
is_concrete_forward_field = field.concrete and field.related_model
if is_concrete_forward_field:
- reverse_field = field.rel
+ reverse_field = field.remote_field
self.assertEqual(field.model, reverse_field.related_model)
self.assertEqual(field.related_model, reverse_field.model)
diff --git a/tests/model_fields/tests.py b/tests/model_fields/tests.py
index 80d35d18e10..31f2cee3374 100644
--- a/tests/model_fields/tests.py
+++ b/tests/model_fields/tests.py
@@ -199,7 +199,7 @@ class ForeignKeyTests(test.TestCase):
self.assertEqual(warnings, expected_warnings)
def test_related_name_converted_to_text(self):
- rel_name = Bar._meta.get_field('a').rel.related_name
+ rel_name = Bar._meta.get_field('a').remote_field.related_name
self.assertIsInstance(rel_name, six.text_type)
def test_abstract_model_pending_lookups(self):
diff --git a/tests/model_meta/tests.py b/tests/model_meta/tests.py
index c77a286308b..340024be47f 100644
--- a/tests/model_meta/tests.py
+++ b/tests/model_meta/tests.py
@@ -226,7 +226,7 @@ class RelationTreeTests(TestCase):
# Testing non hidden related objects
self.assertEqual(
sorted([field.related_query_name() for field in Relation._meta._relation_tree
- if not field.rel.field.rel.is_hidden()]),
+ if not field.remote_field.field.remote_field.is_hidden()]),
sorted([
'fk_abstract_rel', 'fk_base_rel', 'fk_concrete_rel', 'fo_abstract_rel',
'fo_base_rel', 'fo_concrete_rel', 'm2m_abstract_rel',
diff --git a/tests/model_options/models/tablespaces.py b/tests/model_options/models/tablespaces.py
index 7328a3df6ee..42fe1356c8e 100644
--- a/tests/model_options/models/tablespaces.py
+++ b/tests/model_options/models/tablespaces.py
@@ -42,8 +42,8 @@ class Article(models.Model):
# Also set the tables for automatically created models
-Authors = Article._meta.get_field('authors').rel.through
+Authors = Article._meta.get_field('authors').remote_field.through
Authors._meta.db_table = 'model_options_articleref_authors'
-Reviewers = Article._meta.get_field('reviewers').rel.through
+Reviewers = Article._meta.get_field('reviewers').remote_field.through
Reviewers._meta.db_table = 'model_options_articleref_reviewers'
diff --git a/tests/one_to_one/tests.py b/tests/one_to_one/tests.py
index 2cda25b0aa6..4047e2d1fdc 100644
--- a/tests/one_to_one/tests.py
+++ b/tests/one_to_one/tests.py
@@ -136,7 +136,7 @@ class OneToOneTests(TestCase):
place = Place(name='User', address='London')
with self.assertRaisesMessage(ValueError,
'Cannot assign "%r": "%s" instance isn\'t saved in the database.'
- % (place, Restaurant.place.field.rel.to._meta.object_name)):
+ % (place, Restaurant.place.field.remote_field.model._meta.object_name)):
Restaurant.objects.create(place=place, serves_hot_dogs=True, serves_pizza=False)
bar = UndergroundBar()
p = Place(name='User', address='London')
@@ -410,7 +410,7 @@ class OneToOneTests(TestCase):
be added to the related model.
"""
self.assertFalse(
- hasattr(Target, HiddenPointer._meta.get_field('target').rel.get_accessor_name())
+ hasattr(Target, HiddenPointer._meta.get_field('target').remote_field.get_accessor_name())
)
def test_related_object(self):
diff --git a/tests/queryset_pickle/tests.py b/tests/queryset_pickle/tests.py
index e8420008541..d31b0cce1a5 100644
--- a/tests/queryset_pickle/tests.py
+++ b/tests/queryset_pickle/tests.py
@@ -82,7 +82,7 @@ class PickleabilityTestCase(TestCase):
m1 = M2MModel.objects.create()
g1 = Group.objects.create(name='foof')
m1.groups.add(g1)
- m2m_through = M2MModel._meta.get_field('groups').rel.through
+ m2m_through = M2MModel._meta.get_field('groups').remote_field.through
original = m2m_through.objects.get()
dumped = pickle.dumps(original)
reloaded = pickle.loads(dumped)
diff --git a/tests/schema/fields.py b/tests/schema/fields.py
index 1436b971b7e..0392a29aa26 100644
--- a/tests/schema/fields.py
+++ b/tests/schema/fields.py
@@ -35,12 +35,13 @@ class CustomManyToManyField(RelatedField):
super(CustomManyToManyField, self).__init__(**kwargs)
def contribute_to_class(self, cls, name, **kwargs):
- if self.rel.symmetrical and (self.rel.to == "self" or self.rel.to == cls._meta.object_name):
- self.rel.related_name = "%s_rel_+" % name
+ if self.remote_field.symmetrical and (
+ self.remote_field.model == "self" or self.remote_field.model == cls._meta.object_name):
+ self.remote_field.related_name = "%s_rel_+" % name
super(CustomManyToManyField, self).contribute_to_class(cls, name, **kwargs)
- if not self.rel.through and not cls._meta.abstract and not cls._meta.swapped:
- self.rel.through = create_many_to_many_intermediary_model(self, cls)
- setattr(cls, self.name, ManyRelatedObjectsDescriptor(self.rel))
+ if not self.remote_field.through and not cls._meta.abstract and not cls._meta.swapped:
+ self.remote_field.through = create_many_to_many_intermediary_model(self, cls)
+ setattr(cls, self.name, ManyRelatedObjectsDescriptor(self.remote_field))
self.m2m_db_table = curry(self._get_m2m_db_table, cls._meta)
def get_internal_type(self):
diff --git a/tests/schema/tests.py b/tests/schema/tests.py
index df45ed2d595..f6b80b9c835 100644
--- a/tests/schema/tests.py
+++ b/tests/schema/tests.py
@@ -68,7 +68,7 @@ class SchemaTests(TransactionTestCase):
# Remove any M2M tables first
for field in model._meta.local_many_to_many:
with atomic():
- tbl = field.rel.through._meta.db_table
+ tbl = field.remote_field.through._meta.db_table
if tbl in table_names:
cursor.execute(connection.schema_editor().sql_delete_table % {
"table": connection.ops.quote_name(tbl),
@@ -244,7 +244,7 @@ class SchemaTests(TransactionTestCase):
with connection.schema_editor() as editor:
editor.add_field(LocalAuthorWithM2M, new_field)
# Make sure no FK constraint is present
- constraints = self.get_constraints(new_field.rel.through._meta.db_table)
+ constraints = self.get_constraints(new_field.remote_field.through._meta.db_table)
for name, details in constraints.items():
if details['columns'] == ["tag_id"] and details['foreign_key']:
self.fail("FK constraint for tag_id found")
@@ -740,7 +740,7 @@ class SchemaTests(TransactionTestCase):
editor.create_model(TagM2MTest)
editor.create_model(LocalBookWithM2M)
# Ensure there is now an m2m table there
- columns = self.column_classes(LocalBookWithM2M._meta.get_field("tags").rel.through)
+ columns = self.column_classes(LocalBookWithM2M._meta.get_field("tags").remote_field.through)
self.assertEqual(columns['tagm2mtest_id'][0], "IntegerField")
def test_m2m_create(self):
@@ -813,12 +813,12 @@ class SchemaTests(TransactionTestCase):
new_field = M2MFieldClass("schema.TagM2MTest", related_name="authors")
new_field.contribute_to_class(LocalAuthorWithM2M, "tags")
# Ensure there's no m2m table there
- self.assertRaises(DatabaseError, self.column_classes, new_field.rel.through)
+ self.assertRaises(DatabaseError, self.column_classes, new_field.remote_field.through)
# Add the field
with connection.schema_editor() as editor:
editor.add_field(LocalAuthorWithM2M, new_field)
# Ensure there is now an m2m table there
- columns = self.column_classes(new_field.rel.through)
+ columns = self.column_classes(new_field.remote_field.through)
self.assertEqual(columns['tagm2mtest_id'][0], "IntegerField")
# "Alter" the field. This should not rename the DB table to itself.
@@ -829,7 +829,7 @@ class SchemaTests(TransactionTestCase):
with connection.schema_editor() as editor:
editor.remove_field(LocalAuthorWithM2M, new_field)
# Ensure there's no m2m table there
- self.assertRaises(DatabaseError, self.column_classes, new_field.rel.through)
+ self.assertRaises(DatabaseError, self.column_classes, new_field.remote_field.through)
def test_m2m(self):
self._test_m2m(ManyToManyField)
@@ -910,7 +910,7 @@ class SchemaTests(TransactionTestCase):
editor.create_model(TagM2MTest)
editor.create_model(UniqueTest)
# Ensure the M2M exists and points to TagM2MTest
- constraints = self.get_constraints(LocalBookWithM2M._meta.get_field("tags").rel.through._meta.db_table)
+ constraints = self.get_constraints(LocalBookWithM2M._meta.get_field("tags").remote_field.through._meta.db_table)
if connection.features.supports_foreign_keys:
for name, details in constraints.items():
if details['columns'] == ["tagm2mtest_id"] and details['foreign_key']:
@@ -925,9 +925,9 @@ class SchemaTests(TransactionTestCase):
with connection.schema_editor() as editor:
editor.alter_field(LocalBookWithM2M, old_field, new_field)
# Ensure old M2M is gone
- self.assertRaises(DatabaseError, self.column_classes, LocalBookWithM2M._meta.get_field("tags").rel.through)
+ self.assertRaises(DatabaseError, self.column_classes, LocalBookWithM2M._meta.get_field("tags").remote_field.through)
# Ensure the new M2M exists and points to UniqueTest
- constraints = self.get_constraints(new_field.rel.through._meta.db_table)
+ constraints = self.get_constraints(new_field.remote_field.through._meta.db_table)
if connection.features.supports_foreign_keys:
for name, details in constraints.items():
if details['columns'] == ["uniquetest_id"] and details['foreign_key']: