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:
parent
f9c70bb3a1
commit
8f30556329
|
@ -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', "
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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))
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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 ' <strong>%s</strong>' % escape(Truncator(obj).words(14, truncate='...'))
|
return ' <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 [
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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')
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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.',
|
||||||
|
|
|
@ -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))
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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):
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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')
|
||||||
|
|
|
@ -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;" % (
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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):
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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):
|
||||||
|
|
|
@ -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
|
@ -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):
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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']
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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 "
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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]
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
~~~~~~~~~~~~~
|
~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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.'
|
||||||
)
|
)
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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, [])
|
||||||
|
|
|
@ -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)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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):
|
||||||
|
|
|
@ -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):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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):
|
||||||
|
|
|
@ -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',
|
||||||
|
|
|
@ -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'
|
||||||
|
|
|
@ -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):
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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):
|
||||||
|
|
|
@ -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']:
|
||||||
|
|
Loading…
Reference in New Issue