def get_model_from_path_string(root_model, path):
""" Return a model class for a related model
root_model is the class of the initial model
path is like foo__bar where bar is related to foo
"""
for path_section in path.split('__'):
if path_section:
try:
field, model, direct, m2m = _get_field_by_name(root_model, path_section)
except FieldDoesNotExist:
return root_model
if direct:
if _get_remote_field(field):
try:
root_model = _get_remote_field(field).parent_model()
except AttributeError:
root_model = _get_remote_field(field).model
else:
if hasattr(field, 'related_model'):
root_model = field.related_model
else:
root_model = field.model
return root_model
python类FieldDoesNotExist()的实例源码
def get_field(self, model):
'''
Return the django model field for model in context, following relations.
'''
if not hasattr(model, '_meta'):
return
field = None
for bit in self.bits:
try:
field = model._meta.get_field(bit)
except FieldDoesNotExist:
break
if hasattr(field, 'remote_field'):
rel = getattr(field, 'remote_field', None)
model = getattr(rel, 'model', model)
# !!! Support only for Django <= 1.8
# Remove this when support for Django 1.8 is over
else:
rel = getattr(field, 'rel', None)
model = getattr(rel, 'to', model)
return field
def get_field(self, model):
'''
Return the django model field for model in context, following relations.
'''
if not hasattr(model, '_meta'):
return
field = None
for bit in self.bits:
try:
field = model._meta.get_field(bit)
except FieldDoesNotExist:
break
if hasattr(field, 'remote_field'):
rel = getattr(field, 'remote_field', None)
model = getattr(rel, 'model', model)
# !!! Support only for Django <= 1.8
# Remove this when support for Django 1.8 is over
else:
rel = getattr(field, 'rel', None)
model = getattr(rel, 'to', model)
return field
def render(self, context):
# Let any VariableDoesNotExist raised bubble up
args = [self.node.resolve(context)]
if self.foreign_key is not None:
app_label, model_name, fk_attr = self.foreign_key.split('.')
cls = apps.get_model(app_label, model_name)
if cls is None:
raise template.TemplateSyntaxError(
_('drilldown_tree_for_node tag was given an invalid model: %s') %
'.'.join([app_label, model_name])
)
try:
cls._meta.get_field(fk_attr)
except FieldDoesNotExist:
raise template.TemplateSyntaxError(
_('drilldown_tree_for_node tag was given an invalid model field: %s') % fk_attr
)
args.extend([cls, fk_attr, self.count_attr, self.cumulative])
context[self.context_var] = drilldown_tree_for_node(*args)
return ''
def get_verbose_name(model, lookup):
"""Verbose name introspection of ORM models.
Parameters:
- model: the django model
- lookup: name of the field to find verbose name of.
Foreign key lookups is supported, ie. "othermodel__otherfield"
"""
if '__' not in lookup:
return model._meta.get_field(lookup).verbose_name
foreign_key, lookup = lookup.split('__', 1)
try:
foreign_model = model._meta.get_field(foreign_key).rel.to
return get_verbose_name(foreign_model, lookup)
except FieldDoesNotExist:
pass
related = model._meta.get_all_related_objects()
related += model._meta.get_all_related_many_to_many_objects()
for obj in related:
if obj.get_accessor_name() == foreign_key:
return get_verbose_name(obj.model, lookup)
raise FieldDoesNotExist
def is_valid_field(self, model, field):
"""
Return true if the field exists within the model (or in the related
model specified using the Django ORM __ notation)
"""
components = field.split('__', 1)
try:
field, _parent_model, _direct, _m2m = \
model._meta.get_field_by_name(components[0])
# reverse relation
if isinstance(field, _RelatedObject):
return self.is_valid_field(field.model, components[1])
# foreign key
if field.rel and len(components) == 2:
return self.is_valid_field(field.rel.to, components[1])
return True
except FieldDoesNotExist:
return False
def _label(model, value_list, datakeys=None):
"""Make labels for the table head.
Returns a list of tuples. Each tuple contains the verbose label and a key
that can be used for sort parameters in the URL.
"""
attrs = []
labels = []
for value in value_list:
if value in datakeys:
labels.extend(datakeys[value])
attrs.extend(datakeys[value])
else:
attrs.append(value)
try:
labels.append(get_verbose_name(model, value))
except FieldDoesNotExist:
labels.append(value)
return zip(labels, attrs)
def _validate_field_name(self):
field_names = self.model._meta.get_all_field_names()
# If a custom name is provided, make sure the field exists on the model
if self.__field_name is not None and self.__field_name not in field_names:
raise ValueError("%s couldn't find a field named %s in %s." % \
(self.__class__.__name__, self.__field_name, self.model._meta.object_name))
# Otherwise, see if there is a field called either 'site' or 'sites'
else:
for potential_name in ['site', 'sites']:
if potential_name in field_names:
self.__field_name = potential_name
self.__is_validated = True
break
# Now do a type check on the field (FK or M2M only)
try:
field = self.model._meta.get_field(self.__field_name)
if not isinstance(field, (models.ForeignKey, models.ManyToManyField)):
raise TypeError("%s must be a ForeignKey or ManyToManyField." %self.__field_name)
except FieldDoesNotExist:
raise ValueError("%s couldn't find a field named %s in %s." % \
(self.__class__.__name__, self.__field_name, self.model._meta.object_name))
self.__is_validated = True
def validate_list_display(self, cls, model):
" Validate that list_display only contains fields or usable attributes. "
if hasattr(cls, 'list_display'):
check_isseq(cls, 'list_display', cls.list_display)
for idx, field in enumerate(cls.list_display):
if not callable(field):
if not hasattr(cls, field):
if not hasattr(model, field):
try:
model._meta.get_field(field)
except models.FieldDoesNotExist:
raise ImproperlyConfigured("%s.list_display[%d], %r is not a callable or an attribute of %r or found in the model %r."
% (cls.__name__, idx, field, cls.__name__, model._meta.object_name))
else:
# getattr(model, field) could be an X_RelatedObjectsDescriptor
f = fetch_attr(cls, model, "list_display[%d]" % idx, field)
if isinstance(f, models.ManyToManyField):
raise ImproperlyConfigured("'%s.list_display[%d]', '%s' is a ManyToManyField which is not supported."
% (cls.__name__, idx, field))
def get_ordering_field(self, field_name):
"""
Returns the proper model field name corresponding to the given
field_name to use for ordering. field_name may either be the name of a
proper model field or the name of a method (on the admin or model) or a
callable with the 'admin_order_field' attribute. Returns None if no
proper model field name can be matched.
"""
try:
field = self.lookup_opts.get_field(field_name)
return field.name
except models.FieldDoesNotExist:
# See whether field_name is a name of a non-field
# that allows sorting.
if callable(field_name):
attr = field_name
elif hasattr(self.model_admin, field_name):
attr = getattr(self.model_admin, field_name)
else:
attr = getattr(self.model, field_name)
return getattr(attr, 'admin_order_field', None)
def _has_field(cls, name):
try:
cls._meta.get_field(name)
return True
except models.fields.FieldDoesNotExist:
return hasattr(cls, name)
def get_attname(self, cls):
try:
field = self.get_field(cls)
return field.attname
except models.fields.FieldDoesNotExist:
return self.field_name
def get_definition_model(self, cls):
try:
field = self.get_field(cls)
return field.model
except models.fields.FieldDoesNotExist:
# Find where it was defined by walking the inheritance tree
for base_cls in inspect.getmro(cls):
if self.field_name in base_cls.__dict__:
return base_cls
def get_type(self, cls):
if 'type' in self.kwargs:
return self.kwargs['type']
try:
field = self.get_field(cls)
return field.get_internal_type()
except models.fields.FieldDoesNotExist:
return 'CharField'
def get_value(self, obj):
try:
field = self.get_field(obj.__class__)
value = field.value_from_object(obj)
if hasattr(field, 'get_searchable_content'):
value = field.get_searchable_content(value)
return value
except models.fields.FieldDoesNotExist:
value = getattr(obj, self.field_name, None)
if hasattr(value, '__call__'):
value = value()
return value
def _get_model_fields(self, field_names, declared_fields, extra_kwargs):
"""
Returns all the model fields that are being mapped to by fields
on the serializer class.
Returned as a dict of 'model field name' -> 'model field'.
Used internally by `get_uniqueness_field_options`.
"""
model = getattr(self.Meta, 'model')
model_fields = {}
for field_name in field_names:
if field_name in declared_fields:
# If the field is declared on the serializer
field = declared_fields[field_name]
source = field.source or field_name
else:
try:
source = extra_kwargs[field_name]['source']
except KeyError:
source = field_name
if '.' in source or source == '*':
# Model fields will always have a simple source mapping,
# they can't be nested attribute lookups.
continue
try:
field = model._meta.get_field(source)
if isinstance(field, DjangoModelField):
model_fields[source] = field
except FieldDoesNotExist:
pass
return model_fields
# Determine the validators to apply...
def _get_model_fields(self, field_names, declared_fields, extra_kwargs):
"""
Returns all the model fields that are being mapped to by fields
on the serializer class.
Returned as a dict of 'model field name' -> 'model field'.
Used internally by `get_uniqueness_field_options`.
"""
model = getattr(self.Meta, 'model')
model_fields = {}
for field_name in field_names:
if field_name in declared_fields:
# If the field is declared on the serializer
field = declared_fields[field_name]
source = field.source or field_name
else:
try:
source = extra_kwargs[field_name]['source']
except KeyError:
source = field_name
if '.' in source or source == '*':
# Model fields will always have a simple source mapping,
# they can't be nested attribute lookups.
continue
try:
field = model._meta.get_field(source)
if isinstance(field, DjangoModelField):
model_fields[source] = field
except FieldDoesNotExist:
pass
return model_fields
# Determine the validators to apply...
def _get_and_render_with(self, name, render_func, default):
bound_column = self.table.columns[name]
value = None
accessor = A(bound_column.accessor)
# We need to take special care here to allow get_FOO_display()
# methods on a model to be used if available. See issue #30.
penultimate, remainder = accessor.penultimate(self.record)
# If the penultimate is a model and the remainder is a field
# using choices, use get_FOO_display().
if isinstance(penultimate, models.Model):
try:
field = accessor.get_field(self.record)
display_fn = getattr(penultimate, 'get_%s_display' % remainder,
None)
if getattr(field, 'choices', ()) and display_fn:
value = display_fn()
remainder = None
except FieldDoesNotExist:
pass
# Fall back to just using the original accessor
if remainder:
try:
value = accessor.resolve(self.record)
except Exception:
# we need to account for non-field based columns (issue #257)
is_linkcolumn = isinstance(bound_column.column, BaseLinkColumn)
if is_linkcolumn and bound_column.column.text is not None:
return render_func(bound_column)
if value in bound_column.column.empty_values:
return default
return render_func(bound_column, value)
def _get_and_render_with(self, name, render_func, default):
bound_column = self.table.columns[name]
value = None
accessor = A(bound_column.accessor)
# We need to take special care here to allow get_FOO_display()
# methods on a model to be used if available. See issue #30.
penultimate, remainder = accessor.penultimate(self.record)
# If the penultimate is a model and the remainder is a field
# using choices, use get_FOO_display().
if isinstance(penultimate, models.Model):
try:
field = accessor.get_field(self.record)
display_fn = getattr(penultimate, 'get_%s_display' % remainder,
None)
if getattr(field, 'choices', ()) and display_fn:
value = display_fn()
remainder = None
except FieldDoesNotExist:
pass
# Fall back to just using the original accessor
if remainder:
try:
value = accessor.resolve(self.record)
except Exception:
# we need to account for non-field based columns (issue #257)
is_linkcolumn = isinstance(bound_column.column, BaseLinkColumn)
if is_linkcolumn and bound_column.column.text is not None:
return render_func(bound_column)
if value in bound_column.column.empty_values:
return default
return render_func(bound_column, value)
def is_field_nullable(model_class, field_name):
""" Helper function that checks if a field is nullable or not.
It also handles the case of foreign key fields with the '_id' suffix
"""
try:
return model_class._meta.get_field(field_name).null
except FieldDoesNotExist:
# Make sure it's not foreign key
if field_name.endswith('_id'):
return model_class._meta.get_field(field_name[:-3]).null
raise
def _check_geo_field(cls, opts, lookup):
"""
Utility for checking the given lookup with the given model options.
The lookup is a string either specifying the geographic field, e.g.
'point, 'the_geom', or a related lookup on a geographic field like
'address__point'.
If a GeometryField exists according to the given lookup on the model
options, it will be returned. Otherwise returns None.
"""
# This takes into account the situation where the lookup is a
# lookup to a related geographic field, e.g., 'address__point'.
field_list = lookup.split(LOOKUP_SEP)
# Reversing so list operates like a queue of related lookups,
# and popping the top lookup.
field_list.reverse()
fld_name = field_list.pop()
try:
geo_fld = opts.get_field(fld_name)
# If the field list is still around, then it means that the
# lookup was for a geometry field across a relationship --
# thus we keep on getting the related model options and the
# model field associated with the next field in the list
# until there's no more left.
while len(field_list):
opts = geo_fld.rel.to._meta
geo_fld = opts.get_field(field_list.pop())
except (FieldDoesNotExist, AttributeError):
return False
# Finally, make sure we got a Geographic field and return.
if isinstance(geo_fld, GeometryField):
return geo_fld
else:
return False
def check_field_spec(self, cls, model, flds, label):
"""
Validate the fields specification in `flds` from a ModelAdmin subclass
`cls` for the `model` model. Use `label` for reporting problems to the user.
The fields specification can be a ``fields`` option or a ``fields``
sub-option from a ``fieldsets`` option component.
"""
for fields in flds:
# The entry in fields might be a tuple. If it is a standalone
# field, make it into a tuple to make processing easier.
if type(fields) != tuple:
fields = (fields,)
for field in fields:
if field in cls.readonly_fields:
# Stuff can be put in fields that isn't actually a
# model field if it's in readonly_fields,
# readonly_fields will handle the validation of such
# things.
continue
try:
f = model._meta.get_field(field)
except models.FieldDoesNotExist:
# If we can't find a field on the model that matches, it could be an
# extra field on the form; nothing to check so move on to the next field.
continue
if isinstance(f, models.ManyToManyField) and not f.rel.through._meta.auto_created:
raise ImproperlyConfigured("'%s.%s' "
"can't include the ManyToManyField field '%s' because "
"'%s' manually specifies a 'through' model." % (
cls.__name__, label, field, field))
def validate_readonly_fields(self, cls, model):
" Validate that readonly_fields refers to proper attribute or field. "
if hasattr(cls, "readonly_fields"):
check_isseq(cls, "readonly_fields", cls.readonly_fields)
for idx, field in enumerate(cls.readonly_fields):
if not callable(field):
if not hasattr(cls, field):
if not hasattr(model, field):
try:
model._meta.get_field(field)
except models.FieldDoesNotExist:
raise ImproperlyConfigured("%s.readonly_fields[%d], %r is not a callable or an attribute of %r or found in the model %r."
% (cls.__name__, idx, field, cls.__name__, model._meta.object_name))
def validate_list_editable(self, cls, model):
"""
Validate that list_editable is a sequence of editable fields from
list_display without first element.
"""
if hasattr(cls, 'list_editable') and cls.list_editable:
check_isseq(cls, 'list_editable', cls.list_editable)
for idx, field_name in enumerate(cls.list_editable):
try:
field = model._meta.get_field_by_name(field_name)[0]
except models.FieldDoesNotExist:
raise ImproperlyConfigured("'%s.list_editable[%d]' refers to a "
"field, '%s', not defined on %s.%s."
% (cls.__name__, idx, field_name, model._meta.app_label, model.__name__))
if field_name not in cls.list_display:
raise ImproperlyConfigured("'%s.list_editable[%d]' refers to "
"'%s' which is not defined in 'list_display'."
% (cls.__name__, idx, field_name))
if field_name in cls.list_display_links:
raise ImproperlyConfigured("'%s' cannot be in both '%s.list_editable'"
" and '%s.list_display_links'"
% (field_name, cls.__name__, cls.__name__))
if not cls.list_display_links and cls.list_display[0] in cls.list_editable:
raise ImproperlyConfigured("'%s.list_editable[%d]' refers to"
" the first field in list_display, '%s', which can't be"
" used unless list_display_links is set."
% (cls.__name__, idx, cls.list_display[0]))
if not field.editable:
raise ImproperlyConfigured("'%s.list_editable[%d]' refers to a "
"field, '%s', which isn't editable through the admin."
% (cls.__name__, idx, field_name))
def get_field(cls, model, label, field):
try:
return model._meta.get_field(field)
except models.FieldDoesNotExist:
raise ImproperlyConfigured("'%s.%s' refers to field '%s' that is missing from model '%s.%s'."
% (cls.__name__, label, field, model._meta.app_label, model.__name__))
def fetch_attr(cls, model, label, field):
try:
return model._meta.get_field(field)
except models.FieldDoesNotExist:
pass
try:
return getattr(model, field)
except AttributeError:
raise ImproperlyConfigured("'%s.%s' refers to '%s' that is neither a field, method or property of model '%s.%s'."
% (cls.__name__, label, field, model._meta.app_label, model.__name__))
def mark_whodid(self, user, sender, instance, **kwargs):
try:
instance._meta.get_field_by_name('created_by')
instance.created_by = user
except FieldDoesNotExist:
pass
def lookup_allowed(self, lookup, value):
model = self.model
# Check FKey lookups that are allowed, so that popups produced by
# ForeignKeyRawIdWidget, on the basis of ForeignKey.limit_choices_to,
# are allowed to work.
for l in model._meta.related_fkey_lookups:
for k, v in widgets.url_params_from_lookup_dict(l).items():
if k == lookup and v == value:
return True
parts = lookup.split(LOOKUP_SEP)
# Last term in lookup is a query term (__exact, __startswith etc)
# This term can be ignored.
if len(parts) > 1 and parts[-1] in QUERY_TERMS:
parts.pop()
# Special case -- foo__id__exact and foo__id queries are implied
# if foo has been specificially 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:
field = model._meta.get_field(part)
except FieldDoesNotExist:
# Lookups on non-existants fields are ok, since they're ignored
# later.
return True
if hasattr(field, 'rel'):
model = field.rel.to
rel_name = field.rel.get_related_field().name
elif is_related_field(field):
model = field.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:
return True
clean_lookup = LOOKUP_SEP.join(parts)
return clean_lookup in self.list_filter
def lookup_allowed(self, lookup, value):
model = self.model
# Check FKey lookups that are allowed, so that popups produced by
# ForeignKeyRawIdWidget, on the basis of ForeignKey.limit_choices_to,
# are allowed to work.
for l in model._meta.related_fkey_lookups:
for k, v in widgets.url_params_from_lookup_dict(l).items():
if k == lookup and v == value:
return True
parts = lookup.split(LOOKUP_SEP)
# Last term in lookup is a query term (__exact, __startswith etc)
# This term can be ignored.
if len(parts) > 1 and parts[-1] in QUERY_TERMS:
parts.pop()
# Special case -- foo__id__exact and foo__id queries are implied
# if foo has been specificially 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:
field = model._meta.get_field(part)
except FieldDoesNotExist:
# Lookups on non-existants fields are ok, since they're ignored
# later.
return True
if hasattr(field, 'rel'):
model = field.rel.to
rel_name = field.rel.get_related_field().name
elif is_related_field(field):
model = field.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:
return True
clean_lookup = LOOKUP_SEP.join(parts)
return clean_lookup in self.list_filter
def lookup_allowed(self, lookup, value):
model = self.model
# Check FKey lookups that are allowed, so that popups produced by
# ForeignKeyRawIdWidget, on the basis of ForeignKey.limit_choices_to,
# are allowed to work.
for l in model._meta.related_fkey_lookups:
for k, v in widgets.url_params_from_lookup_dict(l).items():
if k == lookup and v == value:
return True
parts = lookup.split(LOOKUP_SEP)
# Last term in lookup is a query term (__exact, __startswith etc)
# This term can be ignored.
if len(parts) > 1 and parts[-1] in QUERY_TERMS:
parts.pop()
# Special case -- foo__id__exact and foo__id queries are implied
# if foo has been specificially 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:
field = model._meta.get_field(part)
except FieldDoesNotExist:
# Lookups on non-existants fields are ok, since they're ignored
# later.
return True
if hasattr(field, 'rel'):
model = field.rel.to
rel_name = field.rel.get_related_field().name
elif is_related_field(field):
model = field.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:
return True
clean_lookup = LOOKUP_SEP.join(parts)
return clean_lookup in self.list_filter