from django.conf import settings
from django.db import models
from django.db.models.query import QuerySet
from django.utils.encoding import smart_str
from functools import partial
def get_ignored_fields(model, setting_var_name):
"""
returns list of ignored fields which must not be modified
"""
ignored_setting = getattr(settings, setting_var_name, {})
ignored_app = ignored_setting.get(model._meta.app_label, {})
if ignored_app:
ignored_fields = ignored_app.get(model._meta.model_name, [])
else:
ignored_fields = []
return ignored_fields
[docs]def clone_instance(instance, fieldnames=None):
"""
returns a copy of the passed instance.
.. warning: All fields are copied, even primary key
:param instance: :py:class:`django.db.models.Model` instance
:return: :py:class:`django.db.models.Model` instance
"""
if fieldnames is None:
fieldnames = [fld.name for fld in instance._meta.fields]
new_kwargs = dict([(name, getattr(instance, name)) for name in fieldnames])
return instance.__class__(**new_kwargs)
# def get_copy_of_instance(instance):
# return instance.__class__.objects.get(pk=instance.pk)
def get_attr(obj, attr, default=None):
"""Recursive get object's attribute. May use dot notation.
>>> class C: pass
>>> a = C()
>>> a.b = C()
>>> a.b.c = 4
>>> get_attr(a, 'b.c')
4
>>> get_attr(a, 'b.c.y', None)
>>> get_attr(a, 'b.c.y', 1)
1
"""
if '.' not in attr:
ret = getattr(obj, attr, default)
else:
L = attr.split('.')
ret = get_attr(getattr(obj, L[0], default), '.'.join(L[1:]), default)
if isinstance(ret, BaseException):
raise ret
return ret
def getattr_or_item(obj, name):
"""
works indifferently on dict or objects, retrieving the
'name' attribute or item
:param obj: dict or object
:param name: attribute or item name
:return:
>>> from django.contrib.auth.models import Permission
>>> p = Permission(name='perm')
>>> d ={'one': 1, 'two': 2}
>>> getattr_or_item(d, 'one')
1
>>> print(getattr_or_item(p, 'name'))
perm
"""
# this change type from type to dict in python3.9
# >>> getattr_or_item({}, "!!!")
# Traceback (most recent call last):
# ...
# AttributeError: dict object has no attribute/item '!!!'
try:
ret = get_attr(obj, name, AttributeError())
except AttributeError:
try:
ret = obj[name]
except (KeyError, TypeError):
raise AttributeError("%s object has no attribute/item '%s'" % (obj.__class__.__name__, name))
return ret
[docs]def get_field_value(obj, field, usedisplay=True, raw_callable=False):
"""
returns the field value or field representation if get_FIELD_display exists
:param obj: :class:`django.db.models.Model` instance
:param field: :class:`django.db.models.Field` instance or ``basestring`` fieldname
:param usedisplay: boolean if True return the get_FIELD_display() result
:return: field value
>>> from django.contrib.auth.models import Permission
>>> p = Permission(name='perm')
>>> get_field_value(p, 'name') == 'perm'
True
>>> get_field_value(p, None)
Traceback (most recent call last):
...
ValueError: Invalid value for parameter `field`: Should be a field name or a Field instance
"""
if isinstance(field, str):
fieldname = field
elif isinstance(field, models.Field):
fieldname = field.name
else:
raise ValueError('Invalid value for parameter `field`: Should be a field name or a Field instance')
if usedisplay and hasattr(obj, 'get_%s_display' % fieldname):
value = getattr(obj, 'get_%s_display' % fieldname)()
else:
value = getattr_or_item(obj, fieldname)
if hasattr(value, 'all'):
value = ';'.join(smart_str(obj) for obj in value.all())
if not raw_callable and callable(value):
value = value()
if isinstance(value, models.Model):
return smart_str(value)
if isinstance(value, str):
value = smart_str(value)
return value
[docs]def get_field_by_path(model, field_path):
"""
get a Model class or instance and a path to a attribute, returns the field object
:param model: :class:`django.db.models.Model`
:param field_path: string path to the field
:return: :class:`django.db.models.Field`
>>> from django.contrib.auth.models import Permission
>>> p = Permission(name='perm')
>>> get_field_by_path(Permission, 'content_type').name
'content_type'
>>> p = Permission(name='perm')
>>> get_field_by_path(p, 'content_type.app_label').name
'app_label'
"""
parts = field_path.split('.')
target = parts[0]
if target in get_all_field_names(model):
field_object, model, direct, m2m = get_field_by_name(model, target)
if isinstance(field_object, models.fields.related.ForeignKey):
if parts[1:]:
return get_field_by_path(field_object.related_model, '.'.join(parts[1:]))
else:
return field_object
else:
return field_object
return None
[docs]def get_verbose_name(model_or_queryset, field):
"""
returns the value of the ``verbose_name`` of a field
typically used in the templates where you can have a dynamic queryset
:param model_or_queryset: target object
:type model_or_queryset: :class:`django.db.models.Model`, :class:`django.db.query.Queryset`
:param field: field to get the verbose name
:type field: :class:`django.db.models.Field`, basestring
:return: translated field verbose name
:rtype: unicode
Valid uses:
>>> from django.contrib.auth.models import User, Permission
>>> user = User()
>>> p = Permission()
>>> get_verbose_name(user, 'username') == 'username'
True
>>> get_verbose_name(User, 'username') == 'username'
True
>>> get_verbose_name(User.objects.all(), 'username') == 'username'
True
>>> get_verbose_name(User.objects, 'username') == 'username'
True
>>> get_verbose_name(User.objects, user._meta.fields[0]) == 'ID'
True
>>> get_verbose_name(p, 'content_type.model') == 'python model class name'
True
"""
if isinstance(model_or_queryset, models.Manager):
model = model_or_queryset.model
elif isinstance(model_or_queryset, QuerySet):
model = model_or_queryset.model
elif isinstance(model_or_queryset, models.Model):
model = model_or_queryset
elif type(model_or_queryset) is models.base.ModelBase:
model = model_or_queryset
else:
raise ValueError('`get_verbose_name` expects Manager, Queryset or Model as first parameter (got %s)' % type(
model_or_queryset))
if isinstance(field, str):
field = get_field_by_path(model, field)
elif isinstance(field, models.Field):
field = field
else:
raise ValueError('`get_verbose_name` field_path must be string or Field class')
return field.verbose_name
def flatten(iterable):
"""
flatten(sequence) -> list
Returns a single, flat list which contains all elements retrieved
from the sequence and all recursively contained sub-sequences
(iterables).
:param sequence: any object that implements iterable protocol (see: :ref:`typeiter`)
:return: list
Examples:
>>> from adminactions.utils import flatten
>>> [1, 2, [3,4], (5,6)]
[1, 2, [3, 4], (5, 6)]
>>> flatten([[[1,2,3], (42,None)], [4,5], [6], 7, (8,9,10)])
[1, 2, 3, 42, None, 4, 5, 6, 7, 8, 9, 10]"""
result = list()
for el in iterable:
if hasattr(el, "__iter__") and not isinstance(el, str):
result.extend(flatten(el))
else:
result.append(el)
return list(result)
def get_field_by_name(model, name):
field = model._meta.get_field(name)
direct = not field.auto_created or field.concrete
return field, field.model, direct, field.many_to_many
def model_has_field(model, field_name):
return field_name in [f.name for f in model._meta.get_fields()]
def get_all_related_objects(model):
return [f for f in model._meta.get_fields()
if (f.one_to_many or f.one_to_one) and f.auto_created]
def get_all_field_names(model):
from itertools import chain
return list(set(chain.from_iterable((field.name, field.attname)
if hasattr(field, 'attname') else (field.name,)
for field in model._meta.get_fields()
if not (field.many_to_one and field.related_model is None))))
def curry(func, *a, **kw):
return partial(func, *a, **kw)