Renamed Field.rel attribute to remote_field

Field.rel is now deprecated. Rel objects have now also remote_field
attribute. This means that self == self.remote_field.remote_field.

In addition, made the Rel objects a bit more like Field objects. Still,
marked ManyToManyFields as null=True.
This commit is contained in:
Anssi Kääriäinen 2015-02-26 16:19:17 +02:00 committed by Tim Graham
parent f9c70bb3a1
commit 8f30556329
62 changed files with 601 additions and 598 deletions

View File

@ -180,7 +180,7 @@ class BaseModelAdminChecks(object):
return [] return []
else: else:
if (isinstance(field, models.ManyToManyField) and if (isinstance(field, models.ManyToManyField) and
not field.rel.through._meta.auto_created): not field.remote_field.through._meta.auto_created):
return [ return [
checks.Error( checks.Error(
("The value of '%s' cannot include the ManyToManyField '%s', " ("The value of '%s' cannot include the ManyToManyField '%s', "

View File

@ -9,12 +9,10 @@ import datetime
from django.contrib.admin.options import IncorrectLookupParameters from django.contrib.admin.options import IncorrectLookupParameters
from django.contrib.admin.utils import ( from django.contrib.admin.utils import (
get_limit_choices_to_from_path, get_model_from_relation, get_model_from_relation, prepare_lookup_value, reverse_field_path,
prepare_lookup_value, reverse_field_path,
) )
from django.core.exceptions import ImproperlyConfigured, ValidationError from django.core.exceptions import ImproperlyConfigured, ValidationError
from django.db import models from django.db import models
from django.db.models.fields.related import ForeignObjectRel, ManyToManyField
from django.utils import timezone from django.utils import timezone
from django.utils.encoding import force_text, smart_text from django.utils.encoding import force_text, smart_text
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
@ -164,11 +162,7 @@ class FieldListFilter(ListFilter):
class RelatedFieldListFilter(FieldListFilter): class RelatedFieldListFilter(FieldListFilter):
def __init__(self, field, request, params, model, model_admin, field_path): def __init__(self, field, request, params, model, model_admin, field_path):
other_model = get_model_from_relation(field) other_model = get_model_from_relation(field)
if hasattr(field, 'rel'): self.lookup_kwarg = '%s__%s__exact' % (field_path, field.target_field.name)
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_isnull = '%s__isnull' % field_path self.lookup_kwarg_isnull = '%s__isnull' % field_path
self.lookup_val = request.GET.get(self.lookup_kwarg) self.lookup_val = request.GET.get(self.lookup_kwarg)
self.lookup_val_isnull = request.GET.get(self.lookup_kwarg_isnull) self.lookup_val_isnull = request.GET.get(self.lookup_kwarg_isnull)
@ -182,9 +176,7 @@ class RelatedFieldListFilter(FieldListFilter):
self.title = self.lookup_title self.title = self.lookup_title
def has_output(self): def has_output(self):
if (isinstance(self.field, ForeignObjectRel) and if self.field.null:
self.field.field.null or hasattr(self.field, 'rel') and
self.field.null):
extra = 1 extra = 1
else: else:
extra = 0 extra = 0
@ -212,9 +204,7 @@ class RelatedFieldListFilter(FieldListFilter):
}, [self.lookup_kwarg_isnull]), }, [self.lookup_kwarg_isnull]),
'display': val, 'display': val,
} }
if (isinstance(self.field, ForeignObjectRel) and if self.field.null:
(self.field.field.null or isinstance(self.field.field, ManyToManyField)) or
hasattr(self.field, 'rel') and (self.field.null or isinstance(self.field, ManyToManyField))):
yield { yield {
'selected': bool(self.lookup_val_isnull), 'selected': bool(self.lookup_val_isnull),
'query_string': cl.get_query_string({ 'query_string': cl.get_query_string({
@ -223,9 +213,7 @@ class RelatedFieldListFilter(FieldListFilter):
'display': EMPTY_CHANGELIST_VALUE, 'display': EMPTY_CHANGELIST_VALUE,
} }
FieldListFilter.register(lambda f: ( FieldListFilter.register(lambda f: f.remote_field, RelatedFieldListFilter)
bool(f.rel) if hasattr(f, 'rel') else
isinstance(f, ForeignObjectRel)), RelatedFieldListFilter)
class BooleanFieldListFilter(FieldListFilter): class BooleanFieldListFilter(FieldListFilter):
@ -371,13 +359,6 @@ class AllValuesFieldListFilter(FieldListFilter):
queryset = model_admin.get_queryset(request) queryset = model_admin.get_queryset(request)
else: else:
queryset = parent_model._default_manager.all() 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 self.lookup_choices = (queryset
.distinct() .distinct()
.order_by(field.name) .order_by(field.name)

View File

@ -203,7 +203,7 @@ class AdminReadonlyField(object):
else: else:
result_repr = linebreaksbr(result_repr) result_repr = linebreaksbr(result_repr)
else: 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())) result_repr = ", ".join(map(six.text_type, value.all()))
else: else:
result_repr = display_for_field(value, f) result_repr = display_for_field(value, f)

View File

@ -26,8 +26,6 @@ from django.core.urlresolvers import reverse
from django.db import models, router, transaction from django.db import models, router, transaction
from django.db.models.constants import LOOKUP_SEP from django.db.models.constants import LOOKUP_SEP
from django.db.models.fields import BLANK_CHOICE_DASH 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.formsets import DELETION_FIELD_NAME, all_valid
from django.forms.models import ( from django.forms.models import (
BaseInlineFormSet, inlineformset_factory, modelform_defines_fields, 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 # rendered output. formfield can be None if it came from a
# OneToOneField with parent_link=True or a M2M intermediary. # OneToOneField with parent_link=True or a M2M intermediary.
if formfield and db_field.name not in self.raw_id_fields: 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 = {} wrapper_kwargs = {}
if related_modeladmin: if related_modeladmin:
wrapper_kwargs.update( wrapper_kwargs.update(
@ -163,7 +161,7 @@ class BaseModelAdmin(six.with_metaclass(forms.MediaDefiningClass)):
can_delete_related=related_modeladmin.has_delete_permission(request), can_delete_related=related_modeladmin.has_delete_permission(request),
) )
formfield.widget = widgets.RelatedFieldWidgetWrapper( 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 return formfield
@ -202,11 +200,11 @@ class BaseModelAdmin(six.with_metaclass(forms.MediaDefiningClass)):
ordering. Otherwise don't specify the queryset, let the field decide ordering. Otherwise don't specify the queryset, let the field decide
(returns None in that case). (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: if related_admin is not None:
ordering = related_admin.get_ordering(request) ordering = related_admin.get_ordering(request)
if ordering is not None and ordering != (): 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 return None
def formfield_for_foreignkey(self, db_field, request=None, **kwargs): 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') db = kwargs.get('using')
if db_field.name in self.raw_id_fields: 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) self.admin_site, using=db)
elif db_field.name in self.radio_fields: elif db_field.name in self.radio_fields:
kwargs['widget'] = widgets.AdminRadioSelect(attrs={ 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 # If it uses an intermediary model that isn't auto created, don't show
# a field in admin. # 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 return None
db = kwargs.get('using') db = kwargs.get('using')
if db_field.name in self.raw_id_fields: 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) self.admin_site, using=db)
kwargs['help_text'] = '' kwargs['help_text'] = ''
elif db_field.name in (list(self.filter_vertical) + list(self.filter_horizontal)): 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: if k == lookup and v == value:
return True return True
parts = lookup.split(LOOKUP_SEP) relation_parts = []
prev_field = None
# Last term in lookup is a query term (__exact, __startswith etc) for part in lookup.split(LOOKUP_SEP):
# 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]:
try: try:
field = model._meta.get_field(part) field = model._meta.get_field(part)
except FieldDoesNotExist: except FieldDoesNotExist:
# Lookups on non-existent fields are ok, since they're ignored # Lookups on non-existent fields are ok, since they're ignored
# later. # later.
return True break
if hasattr(field, 'rel'): # It is allowed to filter on values that would be found from local
if field.rel is None: # model anyways. For example, if you filter on employee__department__id,
# This property or relation doesn't exist, but it's allowed # then the id value would be found already from employee__department_id.
# since it's ignored in ChangeList.get_filters(). if not prev_field or (prev_field.concrete and
return True field not in prev_field.get_path_info()[-1].target_fields):
model = field.rel.to relation_parts.append(part)
if hasattr(field.rel, 'get_related_field'): if not getattr(field, 'get_path_info', None):
rel_name = field.rel.get_related_field().name # This is not a relational field, so further parts
else: # must be transforms.
rel_name = None break
elif isinstance(field, ForeignObjectRel): prev_field = field
model = field.related_model model = field.get_path_info()[-1].to_opts.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()
if len(parts) == 1: if len(relation_parts) <= 1:
# Either a local field filter, or no fields at all.
return True return True
clean_lookup = LOOKUP_SEP.join(parts) clean_lookup = LOOKUP_SEP.join(relation_parts)
valid_lookups = [self.date_hierarchy] valid_lookups = [self.date_hierarchy]
for filter_item in self.list_filter: for filter_item in self.list_filter:
if isinstance(filter_item, type) and issubclass(filter_item, SimpleListFilter): 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: for related_object in related_objects:
related_model = related_object.related_model related_model = related_object.related_model
if (any(issubclass(model, related_model) for model in registered_models) and 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 True
return False return False
@ -1882,8 +1866,8 @@ class InlineModelAdmin(BaseModelAdmin):
# The model was auto-created as intermediary for a # The model was auto-created as intermediary for a
# ManyToMany-relationship, find the target model # ManyToMany-relationship, find the target model
for field in opts.fields: for field in opts.fields:
if field.rel and field.rel.to != self.parent_model: if field.remote_field and field.remote_field.model != self.parent_model:
opts = field.rel.to._meta opts = field.remote_field.model._meta
break break
codename = get_permission_codename('change', opts) codename = get_permission_codename('change', opts)
return request.user.has_perm("%s.%s" % (opts.app_label, codename)) return request.user.has_perm("%s.%s" % (opts.app_label, codename))

View File

@ -215,7 +215,7 @@ def items_for_result(cl, result, form):
if isinstance(value, (datetime.date, datetime.time)): if isinstance(value, (datetime.date, datetime.time)):
row_classes.append('nowrap') row_classes.append('nowrap')
else: else:
if isinstance(f.rel, models.ManyToOneRel): if isinstance(f.remote_field, models.ManyToOneRel):
field_val = getattr(result, f.name) field_val = getattr(result, f.name)
if field_val is None: if field_val is None:
result_repr = EMPTY_CHANGELIST_VALUE result_repr = EMPTY_CHANGELIST_VALUE

View File

@ -446,7 +446,7 @@ def reverse_field_path(model, path):
# Field should point to another model # Field should point to another model
if field.is_relation and not (field.auto_created and not field.concrete): if field.is_relation and not (field.auto_created and not field.concrete):
related_name = field.related_query_name() related_name = field.related_query_name()
parent = field.rel.to parent = field.remote_field.model
else: else:
related_name = field.field.name related_name = field.field.name
parent = field.related_model parent = field.related_model
@ -481,23 +481,3 @@ def remove_trailing_data_field(fields):
except NotRelationField: except NotRelationField:
fields = fields[:-1] fields = fields[:-1]
return fields 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

View File

@ -383,7 +383,7 @@ class ChangeList(object):
except FieldDoesNotExist: except FieldDoesNotExist:
pass pass
else: else:
if isinstance(field.rel, models.ManyToOneRel): if isinstance(field.remote_field, models.ManyToOneRel):
return True return True
return False return False

View File

@ -151,7 +151,7 @@ class ForeignKeyRawIdWidget(forms.TextInput):
super(ForeignKeyRawIdWidget, self).__init__(attrs) super(ForeignKeyRawIdWidget, self).__init__(attrs)
def render(self, name, value, attrs=None): def render(self, name, value, attrs=None):
rel_to = self.rel.to rel_to = self.rel.model
if attrs is None: if attrs is None:
attrs = {} attrs = {}
extra = [] extra = []
@ -196,9 +196,9 @@ class ForeignKeyRawIdWidget(forms.TextInput):
def label_for_value(self, value): def label_for_value(self, value):
key = self.rel.get_related_field().name key = self.rel.get_related_field().name
try: 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 '&nbsp;<strong>%s</strong>' % escape(Truncator(obj).words(14, truncate='...')) return '&nbsp;<strong>%s</strong>' % escape(Truncator(obj).words(14, truncate='...'))
except (ValueError, self.rel.to.DoesNotExist): except (ValueError, self.rel.model.DoesNotExist):
return '' return ''
@ -210,7 +210,7 @@ class ManyToManyRawIdWidget(ForeignKeyRawIdWidget):
def render(self, name, value, attrs=None): def render(self, name, value, attrs=None):
if attrs is None: if attrs is None:
attrs = {} 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 # The related object is registered with the same AdminSite
attrs['class'] = 'vManyToManyRawIdAdminField' attrs['class'] = 'vManyToManyRawIdAdminField'
if value: if value:
@ -248,7 +248,7 @@ class RelatedFieldWidgetWrapper(forms.Widget):
# Backwards compatible check for whether a user can add related # Backwards compatible check for whether a user can add related
# objects. # objects.
if can_add_related is None: 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 self.can_add_related = can_add_related
# XXX: The UX does not support multiple selected values. # XXX: The UX does not support multiple selected values.
multiple = getattr(widget, 'allow_multiple_selected', False) multiple = getattr(widget, 'allow_multiple_selected', False)
@ -280,7 +280,7 @@ class RelatedFieldWidgetWrapper(forms.Widget):
def render(self, name, value, *args, **kwargs): def render(self, name, value, *args, **kwargs):
from django.contrib.admin.views.main import IS_POPUP_VAR, TO_FIELD_VAR 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) info = (rel_opts.app_label, rel_opts.model_name)
self.widget.choices = self.choices self.widget.choices = self.choices
url_params = '&'.join("%s=%s" % param for param in [ url_params = '&'.join("%s=%s" % param for param in [

View File

@ -207,8 +207,8 @@ class ModelDetailView(BaseAdminDocsView):
# ForeignKey is a special case since the field will actually be a # ForeignKey is a special case since the field will actually be a
# descriptor that returns the other object # descriptor that returns the other object
if isinstance(field, models.ForeignKey): if isinstance(field, models.ForeignKey):
data_type = field.rel.to.__name__ data_type = field.remote_field.model.__name__
app_label = field.rel.to._meta.app_label app_label = field.remote_field.model._meta.app_label
verbose = utils.parse_rst( verbose = utils.parse_rst(
(_("the related `%(app_label)s.%(data_type)s` object") % { (_("the related `%(app_label)s.%(data_type)s` object") % {
'app_label': app_label, 'data_type': data_type, 'app_label': app_label, 'data_type': data_type,
@ -228,8 +228,8 @@ class ModelDetailView(BaseAdminDocsView):
# Gather many-to-many fields. # Gather many-to-many fields.
for field in opts.many_to_many: for field in opts.many_to_many:
data_type = field.rel.to.__name__ data_type = field.remote_field.model.__name__
app_label = field.rel.to._meta.app_label app_label = field.remote_field.model._meta.app_label
verbose = _("related `%(app_label)s.%(object_name)s` objects") % { verbose = _("related `%(app_label)s.%(object_name)s` objects") % {
'app_label': app_label, 'app_label': app_label,
'object_name': data_type, 'object_name': data_type,

View File

@ -31,7 +31,7 @@ class GroupAdmin(admin.ModelAdmin):
def formfield_for_manytomany(self, db_field, request=None, **kwargs): def formfield_for_manytomany(self, db_field, request=None, **kwargs):
if db_field.name == 'permissions': 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 # Avoid a major performance hit resolving permission names which
# triggers a content_type load: # triggers a content_type load:
kwargs['queryset'] = qs.select_related('content_type') kwargs['queryset'] = qs.select_related('content_type')

View File

@ -90,11 +90,11 @@ class Command(BaseCommand):
input_msg = capfirst(verbose_field_name) input_msg = capfirst(verbose_field_name)
if default_username: if default_username:
input_msg += " (leave blank to use '%s')" % 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 = force_str('%s%s: ' % (
input_msg, input_msg,
' (%s.%s)' % ( ' (%s.%s)' % (
username_rel.to._meta.object_name, username_rel.model._meta.object_name,
username_rel.field_name username_rel.field_name
) if username_rel else '') ) if username_rel else '')
) )
@ -114,8 +114,13 @@ class Command(BaseCommand):
field = self.UserModel._meta.get_field(field_name) field = self.UserModel._meta.get_field(field_name)
user_data[field_name] = options.get(field_name) user_data[field_name] = options.get(field_name)
while user_data[field_name] is None: while user_data[field_name] is None:
message = force_str('%s%s: ' % (capfirst(field.verbose_name), message = force_str('%s%s: ' % (
' (%s.%s)' % (field.rel.to._meta.object_name, field.rel.field_name) if field.rel else '')) 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) user_data[field_name] = self.get_input_data(field, message)
# Get a password # Get a password

View File

@ -39,6 +39,7 @@ class GenericForeignKey(object):
one_to_many = False one_to_many = False
one_to_one = False one_to_one = False
related_model = None related_model = None
remote_field = None
allow_unsaved_instance_assignment = False allow_unsaved_instance_assignment = False
@ -135,7 +136,7 @@ class GenericForeignKey(object):
id='contenttypes.E003', id='contenttypes.E003',
) )
] ]
elif field.rel.to != ContentType: elif field.remote_field.model != ContentType:
return [ return [
checks.Error( checks.Error(
"'%s.%s' is not a ForeignKey to 'contenttypes.ContentType'." % ( "'%s.%s' is not a ForeignKey to 'contenttypes.ContentType'." % (
@ -323,7 +324,7 @@ class GenericRelation(ForeignObject):
return errors return errors
def _check_generic_foreign_key_existence(self): def _check_generic_foreign_key_existence(self):
target = self.rel.to target = self.remote_field.model
if isinstance(target, ModelBase): if isinstance(target, ModelBase):
fields = target._meta.virtual_fields fields = target._meta.virtual_fields
if any(isinstance(field, GenericForeignKey) and if any(isinstance(field, GenericForeignKey) and
@ -348,16 +349,16 @@ class GenericRelation(ForeignObject):
def resolve_related_fields(self): def resolve_related_fields(self):
self.to_fields = [self.model._meta.pk.name] 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): def get_path_info(self):
opts = self.rel.to._meta opts = self.remote_field.model._meta
target = opts.pk 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): def get_reverse_path_info(self):
opts = self.model._meta 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)] return [PathInfo(from_opts, opts, (opts.pk,), self, not self.unique, False)]
def get_choices_default(self): def get_choices_default(self):
@ -371,7 +372,7 @@ class GenericRelation(ForeignObject):
kwargs['virtual_only'] = True kwargs['virtual_only'] = True
super(GenericRelation, self).contribute_to_class(cls, name, **kwargs) super(GenericRelation, self).contribute_to_class(cls, name, **kwargs)
self.model = cls self.model = cls
setattr(cls, self.name, ReverseGenericRelatedObjectsDescriptor(self.rel)) setattr(cls, self.name, ReverseGenericRelatedObjectsDescriptor(self.remote_field))
def set_attributes_from_rel(self): def set_attributes_from_rel(self):
pass pass
@ -387,7 +388,7 @@ class GenericRelation(ForeignObject):
for_concrete_model=self.for_concrete_model) for_concrete_model=self.for_concrete_model)
def get_extra_restriction(self, where_class, alias, remote_alias): 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 contenttype_pk = self.get_content_type().pk
cond = where_class() cond = where_class()
lookup = field.get_lookup('exact')(field.get_col(remote_alias), contenttype_pk) 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 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( "%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, self.model, for_concrete_model=self.for_concrete_model).pk,
"%s__in" % self.object_id_field_name: [obj.pk for obj in objs] "%s__in" % self.object_id_field_name: [obj.pk for obj in objs]
@ -421,7 +422,7 @@ class ReverseGenericRelatedObjectsDescriptor(ForeignRelatedObjectsDescriptor):
@cached_property @cached_property
def related_manager_cls(self): def related_manager_cls(self):
return create_generic_related_manager( return create_generic_related_manager(
self.rel.to._default_manager.__class__, self.rel.model._default_manager.__class__,
self.rel, self.rel,
) )
@ -439,7 +440,7 @@ def create_generic_related_manager(superclass, rel):
self.instance = instance self.instance = instance
self.model = rel.to self.model = rel.model
content_type = ContentType.objects.db_manager(instance._state.db).get_for_model( content_type = ContentType.objects.db_manager(instance._state.db).get_for_model(
instance, for_concrete_model=rel.field.for_concrete_model) instance, for_concrete_model=rel.field.for_concrete_model)

View File

@ -68,7 +68,7 @@ def generic_inlineformset_factory(model, form=ModelForm,
opts = model._meta opts = model._meta
# if there is no field called `ct_field` let the exception propagate # if there is no field called `ct_field` let the exception propagate
ct_field = opts.get_field(ct_field) 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) raise Exception("fk_name '%s' is not a ForeignKey to ContentType" % ct_field)
fk_field = opts.get_field(fk_field) # let the exception propagate fk_field = opts.get_field(fk_field) # let the exception propagate
if exclude is not None: if exclude is not None:

View File

@ -48,7 +48,7 @@ def shortcut(request, content_type_id, object_id):
# First, look for an many-to-many relationship to Site. # First, look for an many-to-many relationship to Site.
for field in opts.many_to_many: for field in opts.many_to_many:
if field.rel.to is Site: if field.remote_field.model is Site:
try: try:
# Caveat: In the case of multiple related Sites, this just # Caveat: In the case of multiple related Sites, this just
# selects the *first* one, which is arbitrary. # 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. # Next, look for a many-to-one relationship to Site.
if object_domain is None: if object_domain is None:
for field in obj._meta.fields: 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: try:
object_domain = getattr(obj, field.name).domain object_domain = getattr(obj, field.name).domain
except Site.DoesNotExist: except Site.DoesNotExist:

View File

@ -45,7 +45,7 @@ class GISLookup(Lookup):
# model field associated with the next field in the list # model field associated with the next field in the list
# until there's no more left. # until there's no more left.
while len(field_list): 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()) geo_fld = opts.get_field(field_list.pop())
except (FieldDoesNotExist, AttributeError): except (FieldDoesNotExist, AttributeError):
return False return False

View File

@ -229,7 +229,7 @@ class LayerMapping(object):
elif isinstance(model_field, models.ForeignKey): elif isinstance(model_field, models.ForeignKey):
if isinstance(ogr_name, dict): if isinstance(ogr_name, dict):
# Is every given related model mapping field in the Layer? # 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(): for rel_name, ogr_field in ogr_name.items():
idx = check_ogr_fld(ogr_field) idx = check_ogr_fld(ogr_field)
try: try:

View File

@ -33,7 +33,7 @@ class ArrayField(Field):
def check(self, **kwargs): def check(self, **kwargs):
errors = super(ArrayField, self).check(**kwargs) errors = super(ArrayField, self).check(**kwargs)
if self.base_field.rel: if self.base_field.remote_field:
errors.append( errors.append(
checks.Error( checks.Error(
'Base field for array cannot be a related field.', 'Base field for array cannot be a related field.',

View File

@ -184,16 +184,16 @@ def sort_dependencies(app_list):
# Now add a dependency for any FK relation with a model that # Now add a dependency for any FK relation with a model that
# defines a natural key # defines a natural key
for field in model._meta.fields: for field in model._meta.fields:
if hasattr(field.rel, 'to'): if field.remote_field:
rel_model = field.rel.to rel_model = field.remote_field.model
if hasattr(rel_model, 'natural_key') and rel_model != model: if hasattr(rel_model, 'natural_key') and rel_model != model:
deps.append(rel_model) deps.append(rel_model)
# Also add a dependency for any simple M2M relation with a model # Also add a dependency for any simple M2M relation with a model
# that defines a natural key. M2M relations with explicit through # that defines a natural key. M2M relations with explicit through
# models don't count as dependencies. # models don't count as dependencies.
for field in model._meta.many_to_many: for field in model._meta.many_to_many:
if field.rel.through._meta.auto_created: if field.remote_field.through._meta.auto_created:
rel_model = field.rel.to rel_model = field.remote_field.model
if hasattr(rel_model, 'natural_key') and rel_model != model: if hasattr(rel_model, 'natural_key') and rel_model != model:
deps.append(rel_model) deps.append(rel_model)
model_dependencies.append((model, deps)) model_dependencies.append((model, deps))

View File

@ -49,7 +49,7 @@ class Serializer(object):
concrete_model = obj._meta.concrete_model concrete_model = obj._meta.concrete_model
for field in concrete_model._meta.local_fields: for field in concrete_model._meta.local_fields:
if field.serialize: 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: if self.selected_fields is None or field.attname in self.selected_fields:
self.handle_field(obj, field) self.handle_field(obj, field)
else: else:

View File

@ -55,7 +55,7 @@ class Serializer(base.Serializer):
self._current[field.name] = field.value_to_string(obj) self._current[field.name] = field.value_to_string(obj)
def handle_fk_field(self, obj, field): 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) related = getattr(obj, field.name)
if related: if related:
value = related.natural_key() value = related.natural_key()
@ -68,8 +68,8 @@ class Serializer(base.Serializer):
self._current[field.name] = value self._current[field.name] = value
def handle_m2m_field(self, obj, field): def handle_m2m_field(self, obj, field):
if field.rel.through._meta.auto_created: if field.remote_field.through._meta.auto_created:
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'):
m2m_value = lambda value: value.natural_key() m2m_value = lambda value: value.natural_key()
else: else:
m2m_value = lambda value: force_text(value._get_pk_val(), strings_only=True) 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) field = Model._meta.get_field(field_name)
# Handle M2M relations # Handle M2M relations
if field.rel and isinstance(field.rel, models.ManyToManyRel): if field.remote_field and isinstance(field.remote_field, models.ManyToManyRel):
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(value): def m2m_convert(value):
if hasattr(value, '__iter__') and not isinstance(value, six.text_type): 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: 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: 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] m2m_data[field.name] = [m2m_convert(pk) for pk in field_value]
# Handle FK fields # 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 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): 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) obj = field.remote_field.model._default_manager.db_manager(db).get_by_natural_key(*field_value)
value = getattr(obj, field.rel.field_name) value = getattr(obj, field.remote_field.field_name)
# If this is a natural foreign key to an object that # If this is a natural foreign key to an object that
# has a FK/O2O as the foreign key, use the FK value # 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 value = value.pk
else: 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 data[field.attname] = value
else: 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: else:
data[field.attname] = None data[field.attname] = None

View File

@ -91,7 +91,7 @@ class Serializer(base.Serializer):
self._start_relational_field(field) self._start_relational_field(field)
related_att = getattr(obj, field.get_attname()) related_att = getattr(obj, field.get_attname())
if related_att is not None: 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) related = getattr(obj, field.name)
# If related object has a natural key, use it # If related object has a natural key, use it
related = related.natural_key() 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* serialized as references to the object's PK (i.e. the related *data*
is not dumped, just the relation). 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) 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 # If the objects in the m2m have a natural key, use it
def handle_m2m(value): def handle_m2m(value):
natural = value.natural_key() natural = value.natural_key()
@ -142,8 +142,8 @@ class Serializer(base.Serializer):
self.indent(2) self.indent(2)
self.xml.startElement("field", { self.xml.startElement("field", {
"name": field.name, "name": field.name,
"rel": field.rel.__class__.__name__, "rel": field.remote_field.__class__.__name__,
"to": smart_text(field.rel.to._meta), "to": smart_text(field.remote_field.model._meta),
}) })
@ -204,9 +204,9 @@ class Deserializer(base.Deserializer):
field = Model._meta.get_field(field_name) field = Model._meta.get_field(field_name)
# As is usually the case, relation fields get the special treatment. # 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) 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) data[field.attname] = self._handle_fk_field_node(field_node, field)
else: else:
if field_node.getElementsByTagName('None'): if field_node.getElementsByTagName('None'):
@ -228,43 +228,43 @@ class Deserializer(base.Deserializer):
if node.getElementsByTagName('None'): if node.getElementsByTagName('None'):
return None return None
else: 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') keys = node.getElementsByTagName('natural')
if keys: if keys:
# If there are 'natural' subelements, it must be a natural key # If there are 'natural' subelements, it must be a natural key
field_value = [getInnerText(k).strip() for k in keys] 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 = field.remote_field.model._default_manager.db_manager(self.db).get_by_natural_key(*field_value)
obj_pk = getattr(obj, field.rel.field_name) obj_pk = getattr(obj, field.remote_field.field_name)
# If this is a natural foreign key to an object that # If this is a natural foreign key to an object that
# has a FK/O2O as the foreign key, use the FK value # 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 obj_pk = obj_pk.pk
else: else:
# Otherwise, treat like a normal PK # Otherwise, treat like a normal PK
field_value = getInnerText(node).strip() 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 return obj_pk
else: else:
field_value = getInnerText(node).strip() 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): def _handle_m2m_field_node(self, node, field):
""" """
Handle a <field> node for a ManyToManyField. Handle a <field> 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): def m2m_convert(n):
keys = n.getElementsByTagName('natural') keys = n.getElementsByTagName('natural')
if keys: if keys:
# If there are 'natural' subelements, it must be a natural key # If there are 'natural' subelements, it must be a natural key
field_value = [getInnerText(k).strip() for k in keys] 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: else:
# Otherwise, treat like a normal PK value. # 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 return obj_pk
else: 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")] return [m2m_convert(c) for c in node.getElementsByTagName("object")]
def _get_model_from_node(self, node, attr): def _get_model_from_node(self, node, attr):

View File

@ -125,7 +125,7 @@ class BaseDatabaseIntrospection(object):
for f in model._meta.local_many_to_many: for f in model._meta.local_many_to_many:
# If this is an m2m using an intermediate table, # If this is an m2m using an intermediate table,
# we don't need to reset the sequence. # 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}) sequence_list.append({'table': f.m2m_db_table(), 'column': None})
return sequence_list return sequence_list

View File

@ -241,9 +241,9 @@ class BaseDatabaseSchemaEditor(object):
definition += " %s" % col_type_suffix definition += " %s" % col_type_suffix
params.extend(extra_params) params.extend(extra_params)
# FK # FK
if field.rel and field.db_constraint: if field.remote_field and field.db_constraint:
to_table = field.rel.to._meta.db_table to_table = field.remote_field.model._meta.db_table
to_column = field.rel.to._meta.get_field(field.rel.field_name).column to_column = field.remote_field.model._meta.get_field(field.remote_field.field_name).column
if self.connection.features.supports_foreign_keys: if self.connection.features.supports_foreign_keys:
self.deferred_sql.append(self._create_fk_sql(model, field, "_fk_%(to_table)s_%(to_column)s")) self.deferred_sql.append(self._create_fk_sql(model, field, "_fk_%(to_table)s_%(to_column)s"))
elif self.sql_create_inline_fk: elif self.sql_create_inline_fk:
@ -284,8 +284,8 @@ class BaseDatabaseSchemaEditor(object):
# Make M2M tables # Make M2M tables
for field in model._meta.local_many_to_many: for field in model._meta.local_many_to_many:
if field.rel.through._meta.auto_created: if field.remote_field.through._meta.auto_created:
self.create_model(field.rel.through) self.create_model(field.remote_field.through)
def delete_model(self, model): def delete_model(self, model):
""" """
@ -293,8 +293,8 @@ class BaseDatabaseSchemaEditor(object):
""" """
# Handle auto-created intermediary models # Handle auto-created intermediary models
for field in model._meta.local_many_to_many: for field in model._meta.local_many_to_many:
if field.rel.through._meta.auto_created: if field.remote_field.through._meta.auto_created:
self.delete_model(field.rel.through) self.delete_model(field.remote_field.through)
# Delete the table # Delete the table
self.execute(self.sql_delete_table % { self.execute(self.sql_delete_table % {
@ -377,8 +377,8 @@ class BaseDatabaseSchemaEditor(object):
table instead (for M2M fields) table instead (for M2M fields)
""" """
# Special-case implicit M2M tables # Special-case implicit M2M tables
if field.many_to_many and field.rel.through._meta.auto_created: if field.many_to_many and field.remote_field.through._meta.auto_created:
return self.create_model(field.rel.through) return self.create_model(field.remote_field.through)
# Get the column's definition # Get the column's definition
definition, params = self.column_sql(model, field, include_default=True) definition, params = self.column_sql(model, field, include_default=True)
# It might not actually have a column behind it # It might not actually have a column behind it
@ -409,7 +409,7 @@ class BaseDatabaseSchemaEditor(object):
if field.db_index and not field.unique: if field.db_index and not field.unique:
self.deferred_sql.append(self._create_index_sql(model, [field])) self.deferred_sql.append(self._create_index_sql(model, [field]))
# Add any FK constraints later # 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")) self.deferred_sql.append(self._create_fk_sql(model, field, "_fk_%(to_table)s_%(to_column)s"))
# Reset connection if required # Reset connection if required
if self.connection.features.connection_persists_old_columns: if self.connection.features.connection_persists_old_columns:
@ -421,13 +421,13 @@ class BaseDatabaseSchemaEditor(object):
but for M2Ms may involve deleting a table. but for M2Ms may involve deleting a table.
""" """
# Special-case implicit M2M tables # Special-case implicit M2M tables
if field.many_to_many and field.rel.through._meta.auto_created: if field.many_to_many and field.remote_field.through._meta.auto_created:
return self.delete_model(field.rel.through) return self.delete_model(field.remote_field.through)
# It might not actually have a column behind it # It might not actually have a column behind it
if field.db_parameters(connection=self.connection)['type'] is None: if field.db_parameters(connection=self.connection)['type'] is None:
return return
# Drop any FK constraints, MySQL requires explicit deletion # 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) fk_names = self._constraint_names(model, [field.column], foreign_key=True)
for fk_name in fk_names: for fk_name in fk_names:
self.execute(self._delete_constraint_sql(self.sql_delete_fk, model, fk_name)) 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'] old_type = old_db_params['type']
new_db_params = new_field.db_parameters(connection=self.connection) new_db_params = new_field.db_parameters(connection=self.connection)
new_type = new_db_params['type'] 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( raise ValueError(
"Cannot alter field %s into %s - they do not properly define " "Cannot alter field %s into %s - they do not properly define "
"db_type (are you using PostGIS 1.5 or badly-written custom " "db_type (are you using PostGIS 1.5 or badly-written custom "
"fields?)" % (old_field, new_field), "fields?)" % (old_field, new_field),
) )
elif old_type is None and new_type is None and ( elif old_type is None and new_type is None and (
old_field.rel.through and new_field.rel.through and old_field.remote_field.through and new_field.remote_field.through and
old_field.rel.through._meta.auto_created and old_field.remote_field.through._meta.auto_created and
new_field.rel.through._meta.auto_created): new_field.remote_field.through._meta.auto_created):
return self._alter_many_to_many(model, old_field, new_field, strict) return self._alter_many_to_many(model, old_field, new_field, strict)
elif old_type is None and new_type is None and ( elif old_type is None and new_type is None and (
old_field.rel.through and new_field.rel.through and old_field.remote_field.through and new_field.remote_field.through and
not old_field.rel.through._meta.auto_created and not old_field.remote_field.through._meta.auto_created and
not new_field.rel.through._meta.auto_created): not new_field.remote_field.through._meta.auto_created):
# Both sides have through models; this is a no-op. # Both sides have through models; this is a no-op.
return return
elif old_type is None or new_type is None: 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 # Drop any FK constraints, we'll remake them later
fks_dropped = set() 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) fk_names = self._constraint_names(model, [old_field.column], foreign_key=True)
if strict and len(fk_names) != 1: if strict and len(fk_names) != 1:
raise ValueError("Found wrong number (%s) of foreign key constraints for %s.%s" % ( 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? # Does it have a foreign key?
if (new_field.rel and if (new_field.remote_field and
(fks_dropped or not old_field.rel or not old_field.db_constraint) and (fks_dropped or not old_field.remote_field or not old_field.db_constraint) and
new_field.db_constraint): new_field.db_constraint):
self.execute(self._create_fk_sql(model, new_field, "_fk_%(to_table)s_%(to_column)s")) 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 # 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. Alters M2Ms to repoint their to= endpoints.
""" """
# Rename the through table # Rename the through table
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:
self.alter_db_table(old_field.rel.through, old_field.rel.through._meta.db_table, self.alter_db_table(old_field.remote_field.through, old_field.remote_field.through._meta.db_table,
new_field.rel.through._meta.db_table) new_field.remote_field.through._meta.db_table)
# Repoint the FK to the other side # Repoint the FK to the other side
self.alter_field( 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 - # 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) # 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()), old_field.remote_field.through._meta.get_field(old_field.m2m_reverse_field_name()),
new_field.rel.through._meta.get_field(new_field.m2m_reverse_field_name()), new_field.remote_field.through._meta.get_field(new_field.m2m_reverse_field_name()),
) )
self.alter_field( 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 # 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()), old_field.remote_field.through._meta.get_field(old_field.m2m_field_name()),
new_field.rel.through._meta.get_field(new_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=""): def _create_index_name(self, model, column_names, suffix=""):
@ -860,8 +860,8 @@ class BaseDatabaseSchemaEditor(object):
def _create_fk_sql(self, model, field, suffix): def _create_fk_sql(self, model, field, suffix):
from_table = model._meta.db_table from_table = model._meta.db_table
from_column = field.column from_column = field.column
to_table = field.related_field.model._meta.db_table to_table = field.target_field.model._meta.db_table
to_column = field.related_field.column to_column = field.target_field.column
suffix = suffix % { suffix = suffix % {
"to_table": to_table, "to_table": to_table,
"to_column": to_column, "to_column": to_column,

View File

@ -14,7 +14,7 @@ class DatabaseValidation(BaseDatabaseValidation):
errors = super(DatabaseValidation, self).check_field(field, **kwargs) errors = super(DatabaseValidation, self).check_field(field, **kwargs)
# Ignore any related fields. # 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) field_type = field.db_type(connection)
# Ignore any non-concrete fields # Ignore any non-concrete fields

View File

@ -346,7 +346,7 @@ WHEN (new.%(col_name)s IS NULL)
# continue to loop # continue to loop
break break
for f in model._meta.many_to_many: 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()) table_name = self.quote_name(f.m2m_db_table())
sequence_name = self._get_sequence_name(f.m2m_db_table()) sequence_name = self._get_sequence_name(f.m2m_db_table())
column_name = self.quote_name('id') column_name = self.quote_name('id')

View File

@ -172,7 +172,7 @@ class DatabaseOperations(BaseDatabaseOperations):
) )
break # Only one AutoField is allowed per model, so don't bother continuing. break # Only one AutoField is allowed per model, so don't bother continuing.
for f in model._meta.many_to_many: for f in model._meta.many_to_many:
if not f.rel.through: if not f.remote_field.through:
output.append( output.append(
"%s setval(pg_get_serial_sequence('%s','%s'), " "%s setval(pg_get_serial_sequence('%s','%s'), "
"coalesce(max(%s), 1), max(%s) %s null) %s %s;" % ( "coalesce(max(%s), 1), max(%s) %s null) %s %s;" % (

View File

@ -118,8 +118,8 @@ class DatabaseSchemaEditor(BaseDatabaseSchemaEditor):
del body[field.name] del body[field.name]
del mapping[field.column] del mapping[field.column]
# Remove any implicit M2M tables # Remove any implicit M2M tables
if field.many_to_many and field.rel.through._meta.auto_created: if field.many_to_many and field.remote_field.through._meta.auto_created:
return self.delete_model(field.rel.through) return self.delete_model(field.remote_field.through)
# Work inside a new app registry # Work inside a new app registry
apps = Apps() apps = Apps()
@ -215,8 +215,8 @@ class DatabaseSchemaEditor(BaseDatabaseSchemaEditor):
table instead (for M2M fields) table instead (for M2M fields)
""" """
# Special-case implicit M2M tables # Special-case implicit M2M tables
if field.many_to_many and field.rel.through._meta.auto_created: if field.many_to_many and field.remote_field.through._meta.auto_created:
return self.create_model(field.rel.through) return self.create_model(field.remote_field.through)
self._remake_table(model, create_fields=[field]) self._remake_table(model, create_fields=[field])
def remove_field(self, model, field): def remove_field(self, model, field):
@ -227,8 +227,8 @@ class DatabaseSchemaEditor(BaseDatabaseSchemaEditor):
# M2M fields are a special case # M2M fields are a special case
if field.many_to_many: if field.many_to_many:
# For implicit M2M tables, delete the auto-created table # For implicit M2M tables, delete the auto-created table
if field.rel.through._meta.auto_created: if field.remote_field.through._meta.auto_created:
self.delete_model(field.rel.through) self.delete_model(field.remote_field.through)
# For explicit "through" M2M fields, do nothing # For explicit "through" M2M fields, do nothing
# For everything else, remake. # For everything else, remake.
else: else:
@ -263,25 +263,25 @@ class DatabaseSchemaEditor(BaseDatabaseSchemaEditor):
""" """
Alters M2Ms to repoint their to= endpoints. 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. # The field name didn't change, but some options did; we have to propagate this altering.
self._remake_table( self._remake_table(
old_field.rel.through, old_field.remote_field.through,
alter_fields=[( alter_fields=[(
# We need the field that points to the target model, so we can tell alter_field to change it - # 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) # 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()), old_field.remote_field.through._meta.get_field(old_field.m2m_reverse_field_name()),
new_field.rel.through._meta.get_field(new_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()), override_uniques=(new_field.m2m_field_name(), new_field.m2m_reverse_field_name()),
) )
return return
# Make a new through table # Make a new through table
self.create_model(new_field.rel.through) self.create_model(new_field.remote_field.through)
# Copy the data across # Copy the data across
self.execute("INSERT INTO %s (%s) SELECT %s FROM %s" % ( 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([ ', '.join([
"id", "id",
new_field.m2m_column_name(), new_field.m2m_column_name(),
@ -292,7 +292,7 @@ class DatabaseSchemaEditor(BaseDatabaseSchemaEditor):
old_field.m2m_column_name(), old_field.m2m_column_name(),
old_field.m2m_reverse_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 # Delete the old through table
self.delete_model(old_field.rel.through) self.delete_model(old_field.remote_field.through)

View File

@ -78,7 +78,7 @@ class MigrationAutodetector(object):
fields_def = [] fields_def = []
for name, field in fields: for name, field in fields:
deconstruction = self.deep_deconstruct(field) 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'] del deconstruction[2]['to']
fields_def.append(deconstruction) fields_def.append(deconstruction)
return fields_def return fields_def
@ -163,11 +163,11 @@ class MigrationAutodetector(object):
old_model_state = self.from_state.models[app_label, old_model_name] old_model_state = self.from_state.models[app_label, old_model_name]
for field_name, field in old_model_state.fields: 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) 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) if (hasattr(old_field, "remote_field") and getattr(old_field.remote_field, "through", None)
and not old_field.rel.through._meta.auto_created): and not old_field.remote_field.through._meta.auto_created):
through_key = ( through_key = (
old_field.rel.through._meta.app_label, old_field.remote_field.through._meta.app_label,
old_field.rel.through._meta.model_name, old_field.remote_field.through._meta.model_name,
) )
self.through_users[through_key] = (app_label, old_model_name, field_name) self.through_users[through_key] = (app_label, old_model_name, field_name)
@ -460,20 +460,20 @@ class MigrationAutodetector(object):
related_fields = {} related_fields = {}
primary_key_rel = None primary_key_rel = None
for field in model_opts.local_fields: for field in model_opts.local_fields:
if field.rel: if field.remote_field:
if field.rel.to: if field.remote_field.model:
if field.primary_key: if field.primary_key:
primary_key_rel = field.rel.to primary_key_rel = field.remote_field.model
elif not field.rel.parent_link: elif not field.remote_field.parent_link:
related_fields[field.name] = field related_fields[field.name] = field
# through will be none on M2Ms on swapped-out models; # through will be none on M2Ms on swapped-out models;
# we can treat lack of through as auto_created=True, though. # 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 related_fields[field.name] = field
for field in model_opts.local_many_to_many: for field in model_opts.local_many_to_many:
if field.rel.to: if field.remote_field.model:
related_fields[field.name] = field 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 related_fields[field.name] = field
# Are there unique/index_together to defer? # Are there unique/index_together to defer?
unique_together = model_state.options.pop('unique_together', None) unique_together = model_state.options.pop('unique_together', None)
@ -522,13 +522,13 @@ class MigrationAutodetector(object):
dep_app_label = "__setting__" dep_app_label = "__setting__"
dep_object_name = swappable_setting dep_object_name = swappable_setting
else: else:
dep_app_label = field.rel.to._meta.app_label dep_app_label = field.remote_field.model._meta.app_label
dep_object_name = field.rel.to._meta.object_name dep_object_name = field.remote_field.model._meta.object_name
dependencies = [(dep_app_label, dep_object_name, None, True)] 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(( dependencies.append((
field.rel.through._meta.app_label, field.remote_field.through._meta.app_label,
field.rel.through._meta.object_name, field.remote_field.through._meta.object_name,
None, None,
True True
)) ))
@ -639,17 +639,17 @@ class MigrationAutodetector(object):
# Gather related fields # Gather related fields
related_fields = {} related_fields = {}
for field in model._meta.local_fields: for field in model._meta.local_fields:
if field.rel: if field.remote_field:
if field.rel.to: if field.remote_field.model:
related_fields[field.name] = field related_fields[field.name] = field
# through will be none on M2Ms on swapped-out models; # through will be none on M2Ms on swapped-out models;
# we can treat lack of through as auto_created=True, though. # 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 related_fields[field.name] = field
for field in model._meta.local_many_to_many: for field in model._meta.local_many_to_many:
if field.rel.to: if field.remote_field.model:
related_fields[field.name] = field 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 related_fields[field.name] = field
# Generate option removal first # Generate option removal first
unique_together = model_state.options.pop('unique_together', None) 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): 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: 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)) 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'] old_rel_to = old_field_dec[2]['to']
if old_rel_to in self.renamed_models_rel: if old_rel_to in self.renamed_models_rel:
old_field_dec[2]['to'] = self.renamed_models_rel[old_rel_to] 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) field = self.new_apps.get_model(app_label, model_name)._meta.get_field(field_name)
# Fields that are foreignkeys/m2ms depend on stuff # Fields that are foreignkeys/m2ms depend on stuff
dependencies = [] dependencies = []
if field.rel and field.rel.to: if field.remote_field and field.remote_field.model:
# Account for FKs to swappable models # Account for FKs to swappable models
swappable_setting = getattr(field, 'swappable_setting', None) swappable_setting = getattr(field, 'swappable_setting', None)
if swappable_setting is not None: if swappable_setting is not None:
dep_app_label = "__setting__" dep_app_label = "__setting__"
dep_object_name = swappable_setting dep_object_name = swappable_setting
else: else:
dep_app_label = field.rel.to._meta.app_label dep_app_label = field.remote_field.model._meta.app_label
dep_object_name = field.rel.to._meta.object_name dep_object_name = field.remote_field.model._meta.object_name
dependencies = [(dep_app_label, dep_object_name, None, True)] 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(( dependencies.append((
field.rel.through._meta.app_label, field.remote_field.through._meta.app_label,
field.rel.through._meta.object_name, field.remote_field.through._meta.object_name,
None, None,
True, True,
)) ))
@ -838,13 +838,13 @@ class MigrationAutodetector(object):
new_field = self.new_apps.get_model(app_label, model_name)._meta.get_field(field_name) 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 # Implement any model renames on relations; these are handled by RenameModel
# so we need to exclude them from the comparison # 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 = ( rename_key = (
new_field.rel.to._meta.app_label, new_field.remote_field.model._meta.app_label,
new_field.rel.to._meta.model_name, new_field.remote_field.model._meta.model_name,
) )
if rename_key in self.renamed_models: 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) old_field_dec = self.deep_deconstruct(old_field)
new_field_dec = self.deep_deconstruct(new_field) new_field_dec = self.deep_deconstruct(new_field)
if old_field_dec != new_field_dec: if old_field_dec != new_field_dec:

View File

@ -191,11 +191,11 @@ class AlterField(Operation):
# If the field is a relatedfield with an unresolved rel.to, just # If the field is a relatedfield with an unresolved rel.to, just
# set it equal to the other field side. Bandaid fix for AlterField # set it equal to the other field side. Bandaid fix for AlterField
# migrations that are part of a RenameModel change. # migrations that are part of a RenameModel change.
if from_field.rel and from_field.rel.to: if from_field.remote_field and from_field.remote_field.model:
if isinstance(from_field.rel.to, six.string_types): if isinstance(from_field.remote_field.model, six.string_types):
from_field.rel.to = to_field.rel.to from_field.remote_field.model = to_field.remote_field.model
elif to_field.rel and isinstance(to_field.rel.to, six.string_types): elif to_field.remote_field and isinstance(to_field.remote_field.model, six.string_types):
to_field.rel.to = from_field.rel.to to_field.remote_field.model = from_field.remote_field.model
if not self.preserve_default: if not self.preserve_default:
to_field.default = self.field.default to_field.default = self.field.default
schema_editor.alter_field(from_model, from_field, to_field) schema_editor.alter_field(from_model, from_field, to_field)

View File

@ -74,9 +74,9 @@ class CreateModel(Operation):
strings_to_check.append(base.split(".")[-1]) strings_to_check.append(base.split(".")[-1])
# Check we have no FKs/M2Ms with it # Check we have no FKs/M2Ms with it
for fname, field in self.fields: for fname, field in self.fields:
if field.rel: if field.remote_field:
if isinstance(field.rel.to, six.string_types): if isinstance(field.remote_field.model, six.string_types):
strings_to_check.append(field.rel.to.split(".")[-1]) strings_to_check.append(field.remote_field.model.split(".")[-1])
# Now go over all the strings and compare them # Now go over all the strings and compare them
for string in strings_to_check: for string in strings_to_check:
if string.lower() == name.lower(): if string.lower() == name.lower():
@ -181,7 +181,7 @@ class RenameModel(Operation):
for name, field in state.models[related_key].fields: for name, field in state.models[related_key].fields:
if name == related_object.field.name: if name == related_object.field.name:
field = field.clone() 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)) new_fields.append((name, field))
state.models[related_key].fields = new_fields state.models[related_key].fields = new_fields
state.reload_model(*related_key) 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) fields = zip(old_model._meta.local_many_to_many, new_model._meta.local_many_to_many)
for (old_field, new_field) in fields: for (old_field, new_field) in fields:
# Skip self-referential fields as these are renamed above. # 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 continue
# Rename the M2M table that's based on this model's name. # Rename the M2M table that's based on this model's name.
old_m2m_model = old_field.rel.through old_m2m_model = old_field.remote_field.through
new_m2m_model = new_field.rel.through new_m2m_model = new_field.remote_field.through
schema_editor.alter_db_table( schema_editor.alter_db_table(
new_m2m_model, new_m2m_model,
old_m2m_model._meta.db_table, 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 # 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): 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( schema_editor.alter_db_table(
new_field.rel.through, new_field.remote_field.through,
old_field.rel.through._meta.db_table, old_field.remote_field.through._meta.db_table,
new_field.rel.through._meta.db_table, new_field.remote_field.through._meta.db_table,
) )
def database_backwards(self, app_label, schema_editor, from_state, to_state): def database_backwards(self, app_label, schema_editor, from_state, to_state):

View File

@ -230,15 +230,15 @@ class MigrationOptimizer(object):
def reduce_create_model_add_field(self, operation, other, in_between): def reduce_create_model_add_field(self, operation, other, in_between):
if operation.name_lower == other.model_name_lower: if operation.name_lower == other.model_name_lower:
# Don't allow optimizations of FKs through models they reference # 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: for between in in_between:
# Check that it doesn't point to the model # 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): if between.references_model(object_name, app_label):
return None return None
# Check that it's not through the model # Check that it's not through the model
if getattr(other.field.rel, "through", None): if getattr(other.field.remote_field, "through", None):
app_label, object_name = self.model_to_key(other.field.rel.through) app_label, object_name = self.model_to_key(other.field.remote_field.through)
if between.references_model(object_name, app_label): if between.references_model(object_name, app_label):
return None return None
# OK, that's fine # OK, that's fine

View File

@ -94,9 +94,9 @@ class ProjectState(object):
model_state = self.models[(app_label, model_name)] model_state = self.models[(app_label, model_name)]
for name, field in model_state.fields: for name, field in model_state.fields:
if field.is_relation: if field.is_relation:
if field.rel.to == RECURSIVE_RELATIONSHIP_CONSTANT: if field.remote_field.model == RECURSIVE_RELATIONSHIP_CONSTANT:
continue 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())) related_models.add((rel_app_label, rel_model_name.lower()))
# Unregister all related models # Unregister all related models
@ -328,7 +328,7 @@ class ModelState(object):
# Deconstruct the fields # Deconstruct the fields
fields = [] fields = []
for field in model._meta.local_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 continue
if isinstance(field, OrderWrt): if isinstance(field, OrderWrt):
continue continue

View File

@ -199,7 +199,7 @@ class ModelBase(type):
# Locate OneToOneField instances. # Locate OneToOneField instances.
for field in base._meta.local_fields: for field in base._meta.local_fields:
if isinstance(field, OneToOneField): 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. # Do the appropriate setup for any model parents.
for base in parents: for base in parents:
@ -307,19 +307,19 @@ class ModelBase(type):
# certain it has been created # certain it has been created
def make_foreign_order_accessors(field, model, cls): def make_foreign_order_accessors(field, model, cls):
setattr( setattr(
field.rel.to, field.remote_field.model,
'get_%s_order' % cls.__name__.lower(), 'get_%s_order' % cls.__name__.lower(),
curry(method_get_order, cls) curry(method_get_order, cls)
) )
setattr( setattr(
field.rel.to, field.remote_field.model,
'set_%s_order' % cls.__name__.lower(), 'set_%s_order' % cls.__name__.lower(),
curry(method_set_order, cls) curry(method_set_order, cls)
) )
add_lazy_relation( add_lazy_relation(
cls, cls,
opts.order_with_respect_to, opts.order_with_respect_to,
opts.order_with_respect_to.rel.to, opts.order_with_respect_to.remote_field.model,
make_foreign_order_accessors make_foreign_order_accessors
) )
@ -382,7 +382,7 @@ class Model(six.with_metaclass(ModelBase)):
setattr(self, field.attname, val) setattr(self, field.attname, val)
kwargs.pop(field.name, None) kwargs.pop(field.name, None)
# Maintain compatibility with existing calls. # Maintain compatibility with existing calls.
if isinstance(field.rel, ManyToOneRel): if isinstance(field.remote_field, ManyToOneRel):
kwargs.pop(field.attname, None) kwargs.pop(field.attname, None)
# Now we're left with the unprocessed fields that *must* come from # 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. # This field will be populated on request.
continue continue
if kwargs: if kwargs:
if isinstance(field.rel, ForeignObjectRel): if isinstance(field.remote_field, ForeignObjectRel):
try: try:
# Assume object instance was passed in. # Assume object instance was passed in.
rel_obj = kwargs.pop(field.name) rel_obj = kwargs.pop(field.name)
@ -879,7 +879,7 @@ class Model(six.with_metaclass(ModelBase)):
def prepare_database_save(self, field): def prepare_database_save(self, field):
if self.pk is None: if self.pk is None:
raise ValueError("Unsaved model instance %r cannot be used in an ORM query." % self) 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): def clean(self):
""" """
@ -1238,20 +1238,20 @@ class Model(six.with_metaclass(ModelBase)):
fields = cls._meta.local_many_to_many fields = cls._meta.local_many_to_many
# Skip when the target model wasn't found. # 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. # 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: 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: if signature in seen_intermediary_signatures:
errors.append( errors.append(
checks.Error( checks.Error(
"The model has two many-to-many relations through " "The model has two many-to-many relations through "
"the intermediate model '%s.%s'." % ( "the intermediate model '%s.%s'." % (
f.rel.through._meta.app_label, f.remote_field.through._meta.app_label,
f.rel.through._meta.object_name f.remote_field.through._meta.object_name
), ),
hint=None, hint=None,
obj=cls, obj=cls,
@ -1448,7 +1448,7 @@ class Model(six.with_metaclass(ModelBase)):
) )
) )
else: else:
if isinstance(field.rel, models.ManyToManyRel): if isinstance(field.remote_field, models.ManyToManyRel):
errors.append( errors.append(
checks.Error( checks.Error(
"'%s' refers to a ManyToManyField '%s', but " "'%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: for f in cls._meta.local_many_to_many:
# Check if auto-generated name for the M2M field is too long # Check if auto-generated name for the M2M field is too long
# for the database. # 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() _, rel_name = m2m.get_attname_column()
if (m2m.db_column is None and rel_name is not None if (m2m.db_column is None and rel_name is not None
and len(rel_name) > allowed_len): 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): def method_set_order(ordered_obj, self, id_list, using=None):
if using is None: if using is None:
using = DEFAULT_DB_ALIAS 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 order_name = ordered_obj._meta.order_with_respect_to.name
# FIXME: It would be nice if there was an "update many" version of update # FIXME: It would be nice if there was an "update many" version of update
# for situations like this. # 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): 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 order_name = ordered_obj._meta.order_with_respect_to.name
pk_name = ordered_obj._meta.pk.name pk_name = ordered_obj._meta.pk.name
return [r[pk_name] for r in return [r[pk_name] for r in

View File

@ -14,7 +14,7 @@ class ProtectedError(IntegrityError):
def CASCADE(collector, field, sub_objs, using): 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) source_attr=field.name, nullable=field.null)
if field.null and not connections[using].features.can_defer_constraint_checks: if field.null and not connections[using].features.can_defer_constraint_checks:
collector.add_field_update(field, None, sub_objs) 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): def PROTECT(collector, field, sub_objs, using):
raise ProtectedError("Cannot delete some instances of model '%s' because " raise ProtectedError("Cannot delete some instances of model '%s' because "
"they are referenced through a protected foreign key: '%s.%s'" % ( "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 sub_objs
) )
@ -136,7 +136,7 @@ class Collector(object):
skipping parent -> child -> parent chain preventing fast delete of skipping parent -> child -> parent chain preventing fast delete of
the child. 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 return False
if not (hasattr(objs, 'model') and hasattr(objs, '_raw_delete')): if not (hasattr(objs, 'model') and hasattr(objs, '_raw_delete')):
return False return False
@ -153,7 +153,7 @@ class Collector(object):
# Foreign keys pointing to this model, both from m2m and other # Foreign keys pointing to this model, both from m2m and other
# models. # models.
for related in get_candidate_relations_to_delete(opts): 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 return False
for field in model._meta.virtual_fields: for field in model._meta.virtual_fields:
if hasattr(field, 'bulk_related_objects'): if hasattr(field, 'bulk_related_objects'):
@ -214,14 +214,13 @@ class Collector(object):
# object instance. # object instance.
parent_objs = [getattr(obj, ptr.name) for obj in new_objs] parent_objs = [getattr(obj, ptr.name) for obj in new_objs]
self.collect(parent_objs, source=model, self.collect(parent_objs, source=model,
source_attr=ptr.rel.related_name, source_attr=ptr.remote_field.related_name,
collect_related=False, collect_related=False,
reverse_dependency=True) reverse_dependency=True)
if collect_related: if collect_related:
for related in get_candidate_relations_to_delete(model._meta): for related in get_candidate_relations_to_delete(model._meta):
field = related.field field = related.field
if field.rel.on_delete == DO_NOTHING: if field.remote_field.on_delete == DO_NOTHING:
continue continue
batches = self.get_del_batches(new_objs, field) batches = self.get_del_batches(new_objs, field)
for batch in batches: for batch in batches:
@ -229,14 +228,14 @@ class Collector(object):
if self.can_fast_delete(sub_objs, from_field=field): if self.can_fast_delete(sub_objs, from_field=field):
self.fast_deletes.append(sub_objs) self.fast_deletes.append(sub_objs)
elif 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: for field in model._meta.virtual_fields:
if hasattr(field, 'bulk_related_objects'): if hasattr(field, 'bulk_related_objects'):
# Its something like generic foreign key. # Its something like generic foreign key.
sub_objs = field.bulk_related_objects(new_objs, self.using) sub_objs = field.bulk_related_objects(new_objs, self.using)
self.collect(sub_objs, self.collect(sub_objs,
source=model, source=model,
source_attr=field.rel.related_name, source_attr=field.remote_field.related_name,
nullable=True) nullable=True)
def related_objects(self, related, objs): def related_objects(self, related, objs):

View File

@ -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.functional import cached_property, curry, total_ordering, Promise
from django.utils.text import capfirst from django.utils.text import capfirst
from django.utils import timezone from django.utils import timezone
from django.utils.deprecation import RemovedInDjango21Warning
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from django.utils.encoding import (smart_text, force_text, force_bytes, from django.utils.encoding import (smart_text, force_text, force_bytes,
python_2_unicode_compatible) python_2_unicode_compatible)
@ -146,8 +147,8 @@ class Field(RegisterLookupMixin):
self.primary_key = primary_key self.primary_key = primary_key
self.max_length, self._unique = max_length, unique self.max_length, self._unique = max_length, unique
self.blank, self.null = blank, null self.blank, self.null = blank, null
self.rel = rel self.remote_field = rel
self.is_relation = self.rel is not None self.is_relation = self.remote_field is not None
self.default = default self.default = default
self.editable = editable self.editable = editable
self.serialize = serialize self.serialize = serialize
@ -240,6 +241,13 @@ class Field(RegisterLookupMixin):
else: else:
return [] 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): def _check_choices(self):
if self.choices: if self.choices:
if (isinstance(self.choices, six.string_types) or 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 # We don't have to deepcopy very much here, since most things are not
# intended to be altered after initial creation. # intended to be altered after initial creation.
obj = copy.copy(self) obj = copy.copy(self)
if self.rel: if self.remote_field:
obj.rel = copy.copy(self.rel) obj.remote_field = copy.copy(self.remote_field)
if hasattr(self.rel, 'field') and self.rel.field is self: if hasattr(self.remote_field, 'field') and self.remote_field.field is self:
obj.rel.field = obj obj.remote_field.field = obj
memodict[id(self)] = obj memodict[id(self)] = obj
return obj return obj
@ -811,10 +819,10 @@ class Field(RegisterLookupMixin):
not blank_defined else []) not blank_defined else [])
if self.choices: if self.choices:
return first_choice + 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() limit_choices_to = limit_choices_to or self.get_limit_choices_to()
if hasattr(self.rel, 'get_related_field'): if hasattr(self.remote_field, 'get_related_field'):
lst = [(getattr(x, self.rel.get_related_field().attname), lst = [(getattr(x, self.remote_field.get_related_field().attname),
smart_text(x)) smart_text(x))
for x in rel_model._default_manager.complex_filter( for x in rel_model._default_manager.complex_filter(
limit_choices_to)] limit_choices_to)]

File diff suppressed because it is too large Load Diff

View File

@ -26,8 +26,8 @@ def get_normalized_value(value, lhs):
# Account for one-to-one relations when sent a different model # Account for one-to-one relations when sent a different model
sources = lhs.output_field.get_path_info()[-1].target_fields sources = lhs.output_field.get_path_info()[-1].target_fields
for source in sources: for source in sources:
while not isinstance(value, source.model) and source.rel: while not isinstance(value, source.model) and source.remote_field:
source = source.rel.to._meta.get_field(source.rel.field_name) source = source.remote_field.model._meta.get_field(source.remote_field.field_name)
value_list.append(getattr(value, source.attname)) value_list.append(getattr(value, source.attname))
return tuple(value_list) return tuple(value_list)
if not isinstance(value, tuple): if not isinstance(value, tuple):

View File

@ -308,9 +308,9 @@ class Options(object):
# ideally, we'd just ask for field.related_model. However, related_model # 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 # 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. # 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: try:
field.rel.to._meta._expire_cache(forward=False) field.remote_field.model._meta._expire_cache(forward=False)
except AttributeError: except AttributeError:
pass pass
self._expire_cache() 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_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_relation = lambda f: not (f.is_relation and f.one_to_many)
is_not_a_generic_foreign_key = lambda f: not ( 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( return make_immutable_fields_list(
"fields", "fields",
@ -592,8 +592,8 @@ class Options(object):
children = chain.from_iterable(c._relation_tree children = chain.from_iterable(c._relation_tree
for c in self.concrete_model._meta.proxied_children for c in self.concrete_model._meta.proxied_children
if c is not self) if c is not self)
relations = (f.rel for f in children relations = (f.remote_field for f in children
if include_hidden or not f.rel.field.rel.is_hidden()) if include_hidden or not f.remote_field.field.remote_field.is_hidden())
fields = chain(fields, relations) fields = chain(fields, relations)
return list(fields) return list(fields)
@ -690,8 +690,8 @@ class Options(object):
if f.is_relation and f.related_model is not None if f.is_relation and f.related_model is not None
) )
for f in fields_with_relations: for f in fields_with_relations:
if not isinstance(f.rel.to, six.string_types): if not isinstance(f.remote_field.model, six.string_types):
related_objects_graph[f.rel.to._meta].append(f) related_objects_graph[f.remote_field.model._meta].append(f)
for model in all_models: for model in all_models:
# Set the relation_tree using the internal __dict__. In this way # Set the relation_tree using the internal __dict__. In this way
@ -804,8 +804,8 @@ class Options(object):
for field in all_fields: for field in all_fields:
# If hidden fields should be included or the relation is not # If hidden fields should be included or the relation is not
# intentionally hidden, add to the fields dict. # intentionally hidden, add to the fields dict.
if include_hidden or not field.rel.hidden: if include_hidden or not field.remote_field.hidden:
fields.append(field.rel) fields.append(field.remote_field)
if forward: if forward:
fields.extend( fields.extend(

View File

@ -1645,12 +1645,12 @@ class RelatedPopulator(object):
reverse = klass_info['reverse'] reverse = klass_info['reverse']
self.reverse_cache_name = None self.reverse_cache_name = None
if reverse: 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() self.reverse_cache_name = field.get_cache_name()
else: else:
self.cache_name = field.get_cache_name() self.cache_name = field.get_cache_name()
if field.unique: 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): def get_deferred_cls(self, klass_info, init_list):
model_cls = klass_info['model'] model_cls = klass_info['model']

View File

@ -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 * load_fields - the set of fields to be loaded on this model
* reverse - boolean, True if we are checking a reverse select related * reverse - boolean, True if we are checking a reverse select related
""" """
if not field.rel: if not field.remote_field:
return False return False
if field.rel.parent_link and not reverse: if field.remote_field.parent_link and not reverse:
return False return False
if restricted: if restricted:
if reverse and field.related_query_name() not in requested: if reverse and field.related_query_name() not in requested:

View File

@ -676,7 +676,7 @@ class SQLCompiler(object):
only_load.get(field_model)): only_load.get(field_model)):
continue continue
klass_info = { klass_info = {
'model': f.rel.to, 'model': f.remote_field.model,
'field': f, 'field': f,
'reverse': False, 'reverse': False,
'from_parent': False, 'from_parent': False,
@ -686,13 +686,13 @@ class SQLCompiler(object):
_, _, _, joins, _ = self.query.setup_joins( _, _, _, joins, _ = self.query.setup_joins(
[f.name], opts, root_alias) [f.name], opts, root_alias)
alias = joins[-1] 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: for col in columns:
select_fields.append(len(select)) select_fields.append(len(select))
select.append((col, None)) select.append((col, None))
klass_info['select_fields'] = select_fields klass_info['select_fields'] = select_fields
next_klass_infos = self.get_related_selections( 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) get_related_klass_infos(klass_info, next_klass_infos)
if restricted: if restricted:
@ -1003,7 +1003,7 @@ class SQLUpdateCompiler(SQLCompiler):
if val.contains_aggregate: if val.contains_aggregate:
raise FieldError("Aggregate functions are not allowed in this query") raise FieldError("Aggregate functions are not allowed in this query")
elif hasattr(val, 'prepare_database_save'): elif hasattr(val, 'prepare_database_save'):
if field.rel: if field.remote_field:
val = val.prepare_database_save(field) val = val.prepare_database_save(field)
else: else:
raise TypeError("Database is trying to update a relational field " raise TypeError("Database is trying to update a relational field "

View File

@ -608,7 +608,7 @@ class Query(object):
if is_reverse_o2o(source): if is_reverse_o2o(source):
cur_model = source.related_model cur_model = source.related_model
else: else:
cur_model = source.rel.to cur_model = source.remote_field.model
opts = cur_model._meta opts = cur_model._meta
# Even if we're "just passing through" this model, we must add # Even if we're "just passing through" this model, we must add
# both the current model's pk and the related reference field # both the current model's pk and the related reference field
@ -1303,7 +1303,7 @@ class Query(object):
opts = int_model._meta opts = int_model._meta
else: else:
final_field = opts.parents[int_model] 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 opts = int_model._meta
path.append(PathInfo(final_field.model._meta, opts, targets, final_field, False, True)) path.append(PathInfo(final_field.model._meta, opts, targets, final_field, False, True))
cur_names_with_path[1].append( cur_names_with_path[1].append(

View File

@ -589,8 +589,8 @@ class BaseModelFormSet(BaseFormSet):
If the field is a related field, fetch the concrete field's (that If the field is a related field, fetch the concrete field's (that
is, the ultimate pointed-to field's) to_python. is, the ultimate pointed-to field's) to_python.
""" """
while field.rel is not None: while field.remote_field is not None:
field = field.rel.get_related_field() field = field.remote_field.get_related_field()
return field.to_python return field.to_python
def _construct_form(self, i, **kwargs): def _construct_form(self, i, **kwargs):
@ -797,7 +797,7 @@ class BaseModelFormSet(BaseFormSet):
def pk_is_not_editable(pk): def pk_is_not_editable(pk):
return ((not pk.editable) or (pk.auto_created or isinstance(pk, AutoField)) 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 pk_is_not_editable(pk) or pk.name not in form.fields:
if form.is_bound: if form.is_bound:
# If we're adding the related instance, ignore its primary key # If we're adding the related instance, ignore its primary key
@ -813,7 +813,7 @@ class BaseModelFormSet(BaseFormSet):
except IndexError: except IndexError:
pk_value = None pk_value = None
if isinstance(pk, OneToOneField) or isinstance(pk, ForeignKey): 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: else:
qs = self.model._default_manager.get_queryset() qs = self.model._default_manager.get_queryset()
qs = qs.using(form.instance._state.db) qs = qs.using(form.instance._state.db)
@ -863,7 +863,7 @@ class BaseInlineFormSet(BaseModelFormSet):
def __init__(self, data=None, files=None, instance=None, def __init__(self, data=None, files=None, instance=None,
save_as_new=False, prefix=None, queryset=None, **kwargs): save_as_new=False, prefix=None, queryset=None, **kwargs):
if instance is None: if instance is None:
self.instance = self.fk.rel.to() self.instance = self.fk.remote_field.model()
else: else:
self.instance = instance self.instance = instance
self.save_as_new = save_as_new 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. # Set the fk value here so that the form can do its validation.
fk_value = self.instance.pk fk_value = self.instance.pk
if self.fk.rel.field_name != self.fk.rel.to._meta.pk.name: if self.fk.remote_field.field_name != self.fk.remote_field.model._meta.pk.name:
fk_value = getattr(self.instance, self.fk.rel.field_name) fk_value = getattr(self.instance, self.fk.remote_field.field_name)
fk_value = getattr(fk_value, 'pk', fk_value) fk_value = getattr(fk_value, 'pk', fk_value)
setattr(form.instance, self.fk.get_attname(), fk_value) setattr(form.instance, self.fk.get_attname(), fk_value)
return form return form
@classmethod @classmethod
def get_default_prefix(cls): 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): def save_new(self, form, commit=True):
# Ensure the latest copy of the related instance is present on each # 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 # Use commit=False so we can assign the parent key afterwards, then
# save the object. # save the object.
obj = form.save(commit=False) 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)) setattr(obj, self.fk.get_attname(), getattr(pk_value, 'pk', pk_value))
if commit: if commit:
obj.save() obj.save()
@ -932,8 +932,8 @@ class BaseInlineFormSet(BaseModelFormSet):
kwargs = { kwargs = {
'label': getattr(form.fields.get(name), 'label', capfirst(self.fk.verbose_name)) '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: if self.fk.remote_field.field_name != self.fk.remote_field.model._meta.pk.name:
kwargs['to_field'] = self.fk.rel.field_name kwargs['to_field'] = self.fk.remote_field.field_name
# If we're adding a new object, ignore a parent's auto-generated pk # If we're adding a new object, ignore a parent's auto-generated pk
# as it will be regenerated on the save request. # 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: if len(fks_to_parent) == 1:
fk = fks_to_parent[0] fk = fks_to_parent[0]
if not isinstance(fk, ForeignKey) or \ if not isinstance(fk, ForeignKey) or \
(fk.rel.to != parent_model and (fk.remote_field.model != parent_model and
fk.rel.to not in parent_model._meta.get_parent_list()): fk.remote_field.model not in parent_model._meta.get_parent_list()):
raise ValueError( raise ValueError(
"fk_name '%s' is not a ForeignKey to '%s.%s'." "fk_name '%s' is not a ForeignKey to '%s.%s'."
% (fk_name, parent_model._meta.app_label, parent_model._meta.object_name)) % (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 = [ fks_to_parent = [
f for f in opts.fields f for f in opts.fields
if isinstance(f, ForeignKey) if isinstance(f, ForeignKey)
and (f.rel.to == parent_model and (f.remote_field.model == parent_model
or f.rel.to in parent_model._meta.get_parent_list()) or f.remote_field.model in parent_model._meta.get_parent_list())
] ]
if len(fks_to_parent) == 1: if len(fks_to_parent) == 1:
fk = fks_to_parent[0] fk = fks_to_parent[0]

View File

@ -26,6 +26,10 @@ details on these changes.
compatibility layer which allows absolute URLs to be considered equal to compatibility layer which allows absolute URLs to be considered equal to
relative ones when the path is identical will also be removed. 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: .. _deprecation-removed-in-2.0:
2.0 2.0

View File

@ -348,6 +348,15 @@ versions:
Its parsing caused bugs with the current syntax, so support for the old syntax 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. 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 Miscellaneous
~~~~~~~~~~~~~ ~~~~~~~~~~~~~

View File

@ -437,7 +437,7 @@ class ForeignKeyRawIdWidgetTest(DjangoTestCase):
band.album_set.create( band.album_set.create(
name='Hybrid Theory', cover_art=r'albums\hybrid_theory.jpg' 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) w = widgets.ForeignKeyRawIdWidget(rel, widget_admin_site)
self.assertHTMLEqual( self.assertHTMLEqual(
@ -456,7 +456,7 @@ class ForeignKeyRawIdWidgetTest(DjangoTestCase):
core = models.Inventory.objects.create( core = models.Inventory.objects.create(
barcode=87, name='Core', parent=apple 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) w = widgets.ForeignKeyRawIdWidget(rel, widget_admin_site)
self.assertHTMLEqual( self.assertHTMLEqual(
w.render('test', core.parent_id, attrs={}), ( w.render('test', core.parent_id, attrs={}), (
@ -471,7 +471,7 @@ class ForeignKeyRawIdWidgetTest(DjangoTestCase):
# have no magnifying glass link. See #16542 # have no magnifying glass link. See #16542
big_honeycomb = models.Honeycomb.objects.create(location='Old tree') big_honeycomb = models.Honeycomb.objects.create(location='Old tree')
big_honeycomb.bee_set.create() 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) w = widgets.ForeignKeyRawIdWidget(rel, widget_admin_site)
self.assertHTMLEqual( self.assertHTMLEqual(
@ -484,7 +484,7 @@ class ForeignKeyRawIdWidgetTest(DjangoTestCase):
# no magnifying glass link. See #16542 # no magnifying glass link. See #16542
subject1 = models.Individual.objects.create(name='Subject #1') subject1 = models.Individual.objects.create(name='Subject #1')
models.Individual.objects.create(name='Child', parent=subject1) 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) w = widgets.ForeignKeyRawIdWidget(rel, widget_admin_site)
self.assertHTMLEqual( self.assertHTMLEqual(
@ -494,7 +494,7 @@ class ForeignKeyRawIdWidgetTest(DjangoTestCase):
def test_proper_manager_for_label_lookup(self): def test_proper_manager_for_label_lookup(self):
# see #9258 # 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) w = widgets.ForeignKeyRawIdWidget(rel, widget_admin_site)
hidden = models.Inventory.objects.create( hidden = models.Inventory.objects.create(
@ -521,7 +521,7 @@ class ManyToManyRawIdWidgetTest(DjangoTestCase):
m1 = models.Member.objects.create(name='Chester') m1 = models.Member.objects.create(name='Chester')
m2 = models.Member.objects.create(name='Mike') m2 = models.Member.objects.create(name='Mike')
band.members.add(m1, m2) 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) w = widgets.ManyToManyRawIdWidget(rel, widget_admin_site)
self.assertHTMLEqual( self.assertHTMLEqual(
@ -546,7 +546,7 @@ class ManyToManyRawIdWidgetTest(DjangoTestCase):
c1 = models.Company.objects.create(name='Doodle') c1 = models.Company.objects.create(name='Doodle')
c2 = models.Company.objects.create(name='Pear') c2 = models.Company.objects.create(name='Pear')
consultor1.companies.add(c1, c2) 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) w = widgets.ManyToManyRawIdWidget(rel, widget_admin_site)
self.assertHTMLEqual( self.assertHTMLEqual(
@ -562,14 +562,14 @@ class ManyToManyRawIdWidgetTest(DjangoTestCase):
class RelatedFieldWidgetWrapperTests(DjangoTestCase): class RelatedFieldWidgetWrapperTests(DjangoTestCase):
def test_no_can_add_related(self): 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() w = widgets.AdminRadioSelect()
# Used to fail with a name error. # Used to fail with a name error.
w = widgets.RelatedFieldWidgetWrapper(w, rel, widget_admin_site) w = widgets.RelatedFieldWidgetWrapper(w, rel, widget_admin_site)
self.assertFalse(w.can_add_related) self.assertFalse(w.can_add_related)
def test_select_multiple_widget_cant_change_delete_related(self): 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() widget = forms.SelectMultiple()
wrapper = widgets.RelatedFieldWidgetWrapper( wrapper = widgets.RelatedFieldWidgetWrapper(
widget, rel, widget_admin_site, widget, rel, widget_admin_site,
@ -582,7 +582,7 @@ class RelatedFieldWidgetWrapperTests(DjangoTestCase):
self.assertFalse(wrapper.can_delete_related) self.assertFalse(wrapper.can_delete_related)
def test_on_delete_cascade_rel_cant_delete_related(self): 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() widget = forms.Select()
wrapper = widgets.RelatedFieldWidgetWrapper( wrapper = widgets.RelatedFieldWidgetWrapper(
widget, rel, widget_admin_site, widget, rel, widget_admin_site,

View File

@ -1058,7 +1058,7 @@ class DBConstraintTestCase(TestCase):
self.assertEqual(models.Object.objects.count(), 2) self.assertEqual(models.Object.objects.count(), 2)
self.assertEqual(obj.related_objects.count(), 1) 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) intermediary_model.objects.create(from_object_id=obj.id, to_object_id=12345)
self.assertEqual(obj.related_objects.count(), 1) self.assertEqual(obj.related_objects.count(), 1)
self.assertEqual(intermediary_model.objects.count(), 2) self.assertEqual(intermediary_model.objects.count(), 2)

View File

@ -766,5 +766,5 @@ class TestRelatedObjectDeprecation(TestCase):
self.assertEqual(len(warns), 1) self.assertEqual(len(warns), 1)
self.assertEqual( self.assertEqual(
str(warns.pop().message), 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.'
) )

View File

@ -153,7 +153,7 @@ class DeletionTests(TestCase):
r = R.objects.create() r = R.objects.create()
m.m2m.add(r) m.m2m.add(r)
r.delete() r.delete()
through = M._meta.get_field('m2m').rel.through through = M._meta.get_field('m2m').remote_field.through
self.assertFalse(through.objects.exists()) self.assertFalse(through.objects.exists())
r = R.objects.create() r = R.objects.create()

View File

@ -178,8 +178,8 @@ class FieldDeconstructionTests(TestCase):
# Test basic pointing # Test basic pointing
from django.contrib.auth.models import Permission from django.contrib.auth.models import Permission
field = models.ForeignKey("auth.Permission") field = models.ForeignKey("auth.Permission")
field.rel.to = Permission field.remote_field.model = Permission
field.rel.field_name = "id" field.remote_field.field_name = "id"
name, path, args, kwargs = field.deconstruct() name, path, args, kwargs = field.deconstruct()
self.assertEqual(path, "django.db.models.ForeignKey") self.assertEqual(path, "django.db.models.ForeignKey")
self.assertEqual(args, []) self.assertEqual(args, [])

View File

@ -109,7 +109,7 @@ class ArticleTranslationDescriptor(ReverseSingleRelatedObjectDescriptor):
if instance is None: if instance is None:
raise AttributeError("%s must be accessed via instance" % self.field.name) raise AttributeError("%s must be accessed via instance" % self.field.name)
setattr(instance, self.cache_name, value) 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) setattr(value, self.field.related.get_cache_name(), instance)

View File

@ -565,12 +565,12 @@ class ManyToOneTests(TestCase):
p = Parent() p = Parent()
with self.assertRaisesMessage(ValueError, with self.assertRaisesMessage(ValueError,
'Cannot assign "%r": "%s" instance isn\'t saved in the database.' '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) Child(parent=p)
with self.assertRaisesMessage(ValueError, with self.assertRaisesMessage(ValueError,
'Cannot assign "%r": "%s" instance isn\'t saved in the database.' '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) ToFieldChild(parent=p)
# Creation using attname keyword argument and an id will cause the # 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 # Regression for #12190 -- Should be able to instantiate a FK outside
# of a model, and interrogate its related field. # of a model, and interrogate its related field.
cat = models.ForeignKey(Category) 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): def test_relation_unsaved(self):
# Test that the <field>_set manager does not join on Null value fields (#17541) # Test that the <field>_set manager does not join on Null value fields (#17541)

View File

@ -1068,7 +1068,7 @@ class AutodetectorTests(TestCase):
autodetector = MigrationAutodetector(before, after) autodetector = MigrationAutodetector(before, after)
changes = autodetector._detect_changes() changes = autodetector._detect_changes()
# The field name the FK on the book model points to # 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 # Now, we test the custom pk field name
before = self.make_project_state([]) before = self.make_project_state([])
@ -1076,7 +1076,7 @@ class AutodetectorTests(TestCase):
autodetector = MigrationAutodetector(before, after) autodetector = MigrationAutodetector(before, after)
changes = autodetector._detect_changes() changes = autodetector._detect_changes()
# The field name the FK on the book model points to # 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): def test_unmanaged_create(self):
"""Tests that the autodetector correctly deals with managed models.""" """Tests that the autodetector correctly deals with managed models."""
@ -1126,7 +1126,7 @@ class AutodetectorTests(TestCase):
autodetector = MigrationAutodetector(before, after) autodetector = MigrationAutodetector(before, after)
changes = autodetector._detect_changes() changes = autodetector._detect_changes()
# The field name the FK on the book model points to # 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 # Now, we test the custom pk field name
before = self.make_project_state([]) before = self.make_project_state([])
@ -1134,7 +1134,7 @@ class AutodetectorTests(TestCase):
autodetector = MigrationAutodetector(before, after) autodetector = MigrationAutodetector(before, after)
changes = autodetector._detect_changes() changes = autodetector._detect_changes()
# The field name the FK on the book model points to # 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") @override_settings(AUTH_USER_MODEL="thirdapp.CustomUser")
def test_swappable(self): def test_swappable(self):
@ -1159,7 +1159,7 @@ class AutodetectorTests(TestCase):
self.assertOperationTypes(changes, 'testapp', 0, ["AlterField"]) self.assertOperationTypes(changes, 'testapp', 0, ["AlterField"])
self.assertOperationAttributes(changes, 'testapp', 0, 0, model_name="author", name='user') self.assertOperationAttributes(changes, 'testapp', 0, 0, model_name="author", name='user')
fk_field = changes['testapp'][0].operations[0].field 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') self.assertEqual(to_model, 'thirdapp.CustomUser')
def test_add_field_with_default(self): def test_add_field_with_default(self):

View File

@ -479,7 +479,7 @@ class OperationTests(OperationTestBase):
self.assertNotIn(("test_rnmo", "pony"), new_state.models) self.assertNotIn(("test_rnmo", "pony"), new_state.models)
self.assertIn(("test_rnmo", "horse"), new_state.models) self.assertIn(("test_rnmo", "horse"), new_state.models)
# RenameModel also repoints all incoming FKs and M2Ms # 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.assertTableNotExists("test_rnmo_pony")
self.assertTableExists("test_rnmo_horse") self.assertTableExists("test_rnmo_horse")
if connection.features.supports_foreign_keys: if connection.features.supports_foreign_keys:
@ -490,7 +490,7 @@ class OperationTests(OperationTestBase):
# Test original state and database # Test original state and database
self.assertIn(("test_rnmo", "pony"), original_state.models) self.assertIn(("test_rnmo", "pony"), original_state.models)
self.assertNotIn(("test_rnmo", "horse"), 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.assertTableExists("test_rnmo_pony")
self.assertTableNotExists("test_rnmo_horse") self.assertTableNotExists("test_rnmo_horse")
if connection.features.supports_foreign_keys: if connection.features.supports_foreign_keys:
@ -515,7 +515,7 @@ class OperationTests(OperationTestBase):
self.assertNotIn(("test_rmwsrf", "rider"), new_state.models) self.assertNotIn(("test_rmwsrf", "rider"), new_state.models)
self.assertIn(("test_rmwsrf", "horserider"), new_state.models) self.assertIn(("test_rmwsrf", "horserider"), new_state.models)
# Remember, RenameModel also repoints all incoming FKs and M2Ms # 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 # Test the database alteration
self.assertTableExists("test_rmwsrf_rider") self.assertTableExists("test_rmwsrf_rider")
self.assertTableNotExists("test_rmwsrf_horserider") self.assertTableNotExists("test_rmwsrf_horserider")
@ -553,8 +553,8 @@ class OperationTests(OperationTestBase):
self.assertIn(("test_rmwsc", "littlehorse"), new_state.models) self.assertIn(("test_rmwsc", "littlehorse"), new_state.models)
# RenameModel shouldn't repoint the superclass's relations, only local ones # RenameModel shouldn't repoint the superclass's relations, only local ones
self.assertEqual( self.assertEqual(
project_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].rel.to 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 # Before running the migration we have a table for Shetland Pony, not Little Horse
self.assertTableExists("test_rmwsc_shetlandpony") self.assertTableExists("test_rmwsc_shetlandpony")
@ -612,7 +612,7 @@ class OperationTests(OperationTestBase):
pony.riders.add(rider) pony.riders.add(rider)
self.assertEqual(Pony.objects.count(), 2) self.assertEqual(Pony.objects.count(), 2)
self.assertEqual(Rider.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): def test_add_field(self):
""" """

View File

@ -155,7 +155,7 @@ class FieldFlagsTests(test.TestCase):
# Ensure all m2m reverses are m2m # Ensure all m2m reverses are m2m
for field in m2m_type_fields: 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.is_relation)
self.assertTrue(reverse_field.many_to_many) self.assertTrue(reverse_field.many_to_many)
self.assertTrue(reverse_field.related_model) self.assertTrue(reverse_field.related_model)
@ -171,7 +171,7 @@ class FieldFlagsTests(test.TestCase):
# Ensure all o2m reverses are m2o # Ensure all o2m reverses are m2o
for field in o2m_type_fields: for field in o2m_type_fields:
if field.concrete: if field.concrete:
reverse_field = field.rel reverse_field = field.remote_field
self.assertTrue(reverse_field.is_relation and reverse_field.many_to_one) self.assertTrue(reverse_field.is_relation and reverse_field.many_to_one)
def test_cardinality_m2o(self): def test_cardinality_m2o(self):
@ -213,6 +213,6 @@ class FieldFlagsTests(test.TestCase):
for field in AllFieldsModel._meta.get_fields(): for field in AllFieldsModel._meta.get_fields():
is_concrete_forward_field = field.concrete and field.related_model is_concrete_forward_field = field.concrete and field.related_model
if is_concrete_forward_field: 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.model, reverse_field.related_model)
self.assertEqual(field.related_model, reverse_field.model) self.assertEqual(field.related_model, reverse_field.model)

View File

@ -199,7 +199,7 @@ class ForeignKeyTests(test.TestCase):
self.assertEqual(warnings, expected_warnings) self.assertEqual(warnings, expected_warnings)
def test_related_name_converted_to_text(self): 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) self.assertIsInstance(rel_name, six.text_type)
def test_abstract_model_pending_lookups(self): def test_abstract_model_pending_lookups(self):

View File

@ -226,7 +226,7 @@ class RelationTreeTests(TestCase):
# Testing non hidden related objects # Testing non hidden related objects
self.assertEqual( self.assertEqual(
sorted([field.related_query_name() for field in Relation._meta._relation_tree 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([ sorted([
'fk_abstract_rel', 'fk_base_rel', 'fk_concrete_rel', 'fo_abstract_rel', 'fk_abstract_rel', 'fk_base_rel', 'fk_concrete_rel', 'fo_abstract_rel',
'fo_base_rel', 'fo_concrete_rel', 'm2m_abstract_rel', 'fo_base_rel', 'fo_concrete_rel', 'm2m_abstract_rel',

View File

@ -42,8 +42,8 @@ class Article(models.Model):
# Also set the tables for automatically created models # 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' 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' Reviewers._meta.db_table = 'model_options_articleref_reviewers'

View File

@ -136,7 +136,7 @@ class OneToOneTests(TestCase):
place = Place(name='User', address='London') place = Place(name='User', address='London')
with self.assertRaisesMessage(ValueError, with self.assertRaisesMessage(ValueError,
'Cannot assign "%r": "%s" instance isn\'t saved in the database.' '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) Restaurant.objects.create(place=place, serves_hot_dogs=True, serves_pizza=False)
bar = UndergroundBar() bar = UndergroundBar()
p = Place(name='User', address='London') p = Place(name='User', address='London')
@ -410,7 +410,7 @@ class OneToOneTests(TestCase):
be added to the related model. be added to the related model.
""" """
self.assertFalse( 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): def test_related_object(self):

View File

@ -82,7 +82,7 @@ class PickleabilityTestCase(TestCase):
m1 = M2MModel.objects.create() m1 = M2MModel.objects.create()
g1 = Group.objects.create(name='foof') g1 = Group.objects.create(name='foof')
m1.groups.add(g1) 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() original = m2m_through.objects.get()
dumped = pickle.dumps(original) dumped = pickle.dumps(original)
reloaded = pickle.loads(dumped) reloaded = pickle.loads(dumped)

View File

@ -35,12 +35,13 @@ class CustomManyToManyField(RelatedField):
super(CustomManyToManyField, self).__init__(**kwargs) super(CustomManyToManyField, self).__init__(**kwargs)
def contribute_to_class(self, cls, name, **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): if self.remote_field.symmetrical and (
self.rel.related_name = "%s_rel_+" % name 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) super(CustomManyToManyField, self).contribute_to_class(cls, name, **kwargs)
if not self.rel.through and not cls._meta.abstract and not cls._meta.swapped: if not self.remote_field.through and not cls._meta.abstract and not cls._meta.swapped:
self.rel.through = create_many_to_many_intermediary_model(self, cls) self.remote_field.through = create_many_to_many_intermediary_model(self, cls)
setattr(cls, self.name, ManyRelatedObjectsDescriptor(self.rel)) setattr(cls, self.name, ManyRelatedObjectsDescriptor(self.remote_field))
self.m2m_db_table = curry(self._get_m2m_db_table, cls._meta) self.m2m_db_table = curry(self._get_m2m_db_table, cls._meta)
def get_internal_type(self): def get_internal_type(self):

View File

@ -68,7 +68,7 @@ class SchemaTests(TransactionTestCase):
# Remove any M2M tables first # Remove any M2M tables first
for field in model._meta.local_many_to_many: for field in model._meta.local_many_to_many:
with atomic(): with atomic():
tbl = field.rel.through._meta.db_table tbl = field.remote_field.through._meta.db_table
if tbl in table_names: if tbl in table_names:
cursor.execute(connection.schema_editor().sql_delete_table % { cursor.execute(connection.schema_editor().sql_delete_table % {
"table": connection.ops.quote_name(tbl), "table": connection.ops.quote_name(tbl),
@ -244,7 +244,7 @@ class SchemaTests(TransactionTestCase):
with connection.schema_editor() as editor: with connection.schema_editor() as editor:
editor.add_field(LocalAuthorWithM2M, new_field) editor.add_field(LocalAuthorWithM2M, new_field)
# Make sure no FK constraint is present # 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(): for name, details in constraints.items():
if details['columns'] == ["tag_id"] and details['foreign_key']: if details['columns'] == ["tag_id"] and details['foreign_key']:
self.fail("FK constraint for tag_id found") self.fail("FK constraint for tag_id found")
@ -740,7 +740,7 @@ class SchemaTests(TransactionTestCase):
editor.create_model(TagM2MTest) editor.create_model(TagM2MTest)
editor.create_model(LocalBookWithM2M) editor.create_model(LocalBookWithM2M)
# Ensure there is now an m2m table there # 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") self.assertEqual(columns['tagm2mtest_id'][0], "IntegerField")
def test_m2m_create(self): def test_m2m_create(self):
@ -813,12 +813,12 @@ class SchemaTests(TransactionTestCase):
new_field = M2MFieldClass("schema.TagM2MTest", related_name="authors") new_field = M2MFieldClass("schema.TagM2MTest", related_name="authors")
new_field.contribute_to_class(LocalAuthorWithM2M, "tags") new_field.contribute_to_class(LocalAuthorWithM2M, "tags")
# Ensure there's no m2m table there # 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 # Add the field
with connection.schema_editor() as editor: with connection.schema_editor() as editor:
editor.add_field(LocalAuthorWithM2M, new_field) editor.add_field(LocalAuthorWithM2M, new_field)
# Ensure there is now an m2m table there # 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") self.assertEqual(columns['tagm2mtest_id'][0], "IntegerField")
# "Alter" the field. This should not rename the DB table to itself. # "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: with connection.schema_editor() as editor:
editor.remove_field(LocalAuthorWithM2M, new_field) editor.remove_field(LocalAuthorWithM2M, new_field)
# Ensure there's no m2m table there # 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): def test_m2m(self):
self._test_m2m(ManyToManyField) self._test_m2m(ManyToManyField)
@ -910,7 +910,7 @@ class SchemaTests(TransactionTestCase):
editor.create_model(TagM2MTest) editor.create_model(TagM2MTest)
editor.create_model(UniqueTest) editor.create_model(UniqueTest)
# Ensure the M2M exists and points to TagM2MTest # 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: if connection.features.supports_foreign_keys:
for name, details in constraints.items(): for name, details in constraints.items():
if details['columns'] == ["tagm2mtest_id"] and details['foreign_key']: if details['columns'] == ["tagm2mtest_id"] and details['foreign_key']:
@ -925,9 +925,9 @@ class SchemaTests(TransactionTestCase):
with connection.schema_editor() as editor: with connection.schema_editor() as editor:
editor.alter_field(LocalBookWithM2M, old_field, new_field) editor.alter_field(LocalBookWithM2M, old_field, new_field)
# Ensure old M2M is gone # 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 # 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: if connection.features.supports_foreign_keys:
for name, details in constraints.items(): for name, details in constraints.items():
if details['columns'] == ["uniquetest_id"] and details['foreign_key']: if details['columns'] == ["uniquetest_id"] and details['foreign_key']: