__all__ = (
from django.db.models.base import ModelBase
from django.db.models.options import Options
"""Return a model metaclass that ensures a certain field order, as
specified by ``requests``. This is useful in inheritance scenarios,
where the order cannot be controlled declaratively.
__metaclass__ = set_field_order(('name', 'aliases',))
``requests`` is expected to be tuple of field names, or an iterable
of such tuples.
Ultimately, the model field order should be irrelevant and the form
system might be a better place to do this. Generic admin appliations
should approach this as newforms-admin does, registering this sort
of information with the application by way of a custom class, or
something similar. Once newforms-admin lands, applications uses this
should probably consider switching to using the same config data as
This used to just change the Options (_meta) field_cache order, but
ModelForms started to resort the fields on it's own.
if not requests:
# support simplified syntax for single sort requests
if not isinstance(requests, (list,tuple)):
requests = (requests,)
def __new__(cls, name, bases, attrs):
model = super(OrderMetaclass, cls).__new__(cls, name, bases, attrs)
# Start with all fields in default order. Create a copy, because
# we don't care to actually change it's order. Make sure it is
# sorted (will be according to Field.creation_counter).
fields = sorted(list(model._meta.fields))
# Change the order of the fields in this list according to
# user's request.
for request in requests:
anchor_idx = None
for to_be_ordered in request:
# Find the current index of this field.
for i in range(0, len(fields)):
if fields[i].name == to_be_ordered:
field_idx = i
raise ValueError('Invalid field name "%s"' % to_be_ordered)
# First field in the request is the anchor, all the
# other listed fields will be inserted after it.
if anchor_idx is None:
anchor_idx = field_idx
field = fields[field_idx]
if field_idx > anchor_idx:
anchor_idx += 1
# Rewrite the whole model's creation_counters to match the new
# field order.
for idx, field in enumerate(fields):
field.creation_counter = idx + 1 # start with 1
from django.db.models import AutoField
"""Create a copy of a model instance.
Works in model inheritance case where ``instance.pk = None`` is
not good enough, since the subclass instance refers to the
parent_link's primary key during save.
M2M relationships are currently not handled, i.e. they are not
See also Django #4027.
initial = dict([(f.name, getattr(obj, f.name))
for f in obj._meta.fields
if not isinstance(f, AutoField) and\
not f in obj._meta.parents.values()])
from django.db.models.deletion import Collector
from django.db import router
def has_related_objects(obj, ignore=):
"""Returns ``True`` if the model instance ``obj`` has any related
objects depending on it.
This allows you to check, for example, whether a delete would be
cascading, i.e. cause other objects to be deleted, too.
You may pass a list of model classes in ``ignore`` - dependencies
from those models will then not be considered.
With respect to model inheritance, parent model records will be
ignored, while subclass model will count. For example, say ``B``
inherits ``A``. Now, when deleting ``b``, ``a`` would not be
considered a related object by this function. However, when
deleting ``a``, ``b`` would.
# TODO: untested with multi-level inheritance, multiple parents
# TODO: add tests
model = type(obj)
collector = Collector(using=router.db_for_write(model))
for so_klass, items in collector.data.items():
# Make sure there are instances for this relation.
if not len(items):
# Auto-created m2m models we don't care about. The admin ignores
# those in it's ``django.contrib.admin.util.NestedObjects`` class.
# Also ignore any subclasses (in case of inheritance).
if issubclass(so_klass, model):
# Does the user explicitely ask us to ignore this model?
if so_klass in ignore:
# -> So there is at least one related object that counts