debian-django-filter/django_filters/utils.py

137 lines
4.2 KiB
Python

import warnings
from django.conf import settings
from django.core.exceptions import FieldError
from django.db import models
from django.db.models.constants import LOOKUP_SEP
from django.db.models.expressions import Expression
from django.db.models.fields import FieldDoesNotExist
from django.db.models.fields.related import ForeignObjectRel
from django.utils import six, timezone
from .compat import remote_field, remote_model
from .exceptions import FieldLookupError
def deprecate(msg, level_modifier=0):
warnings.warn(
"%s See: https://django-filter.readthedocs.io/en/latest/migration.html" % msg,
DeprecationWarning, stacklevel=3 + level_modifier)
def try_dbfield(fn, field_class):
"""
Try ``fn`` with the DB ``field_class`` by walking its
MRO until a result is found.
ex::
_try_dbfield(field_dict.get, models.CharField)
"""
# walk the mro, as field_class could be a derived model field.
for cls in field_class.mro():
# skip if cls is models.Field
if cls is models.Field:
continue
data = fn(cls)
if data:
return data
# TODO: remove field_types arg with deprecations
def get_all_model_fields(model, field_types=None):
opts = model._meta
if field_types is not None:
return [
f.name for f in sorted(opts.fields + opts.many_to_many)
if not isinstance(f, models.AutoField) and
not (getattr(remote_field(f), 'parent_link', False)) and
f.__class__ in field_types
]
return [
f.name for f in sorted(opts.fields + opts.many_to_many)
if not isinstance(f, models.AutoField) and
not (getattr(remote_field(f), 'parent_link', False))
]
def get_model_field(model, field_name):
"""
Get a ``model`` field, traversing relationships
in the ``field_name``.
ex::
f = get_model_field(Book, 'author__first_name')
"""
parts = field_name.split(LOOKUP_SEP)
opts = model._meta
# walk relationships
for name in parts[:-1]:
try:
rel = opts.get_field(name)
except FieldDoesNotExist:
return None
if isinstance(rel, ForeignObjectRel):
opts = rel.related_model._meta
else:
opts = remote_model(rel)._meta
try:
return opts.get_field(parts[-1])
except FieldDoesNotExist:
return None
def resolve_field(model_field, lookup_expr):
"""
Resolves a ``lookup_expr`` into its final output field, given
the initial ``model_field``. The lookup expression should only contain
transforms and lookups, not intermediary model field parts.
Note:
This method is based on django.db.models.sql.query.Query.build_lookup
For more info on the lookup API:
https://docs.djangoproject.com/en/1.9/ref/models/lookups/
"""
query = model_field.model._default_manager.all().query
lhs = Expression(model_field)
lookups = lookup_expr.split(LOOKUP_SEP)
assert len(lookups) > 0
try:
while lookups:
name = lookups[0]
# If there is just one part left, try first get_lookup() so
# that if the lhs supports both transform and lookup for the
# name, then lookup will be picked.
if len(lookups) == 1:
final_lookup = lhs.get_lookup(name)
if not final_lookup:
# We didn't find a lookup. We are going to interpret
# the name as transform, and do an Exact lookup against
# it.
lhs = query.try_transform(lhs, name, lookups)
final_lookup = lhs.get_lookup('exact')
return lhs.output_field, final_lookup.lookup_name
lhs = query.try_transform(lhs, name, lookups)
lookups = lookups[1:]
except FieldError as e:
six.raise_from(FieldLookupError(model_field, lookup_expr), e)
def handle_timezone(value):
if settings.USE_TZ and timezone.is_naive(value):
return timezone.make_aware(value, timezone.get_default_timezone())
elif not settings.USE_TZ and timezone.is_aware(value):
return timezone.make_naive(value, timezone.UTC())
return value