Created
August 17, 2011 19:21
-
-
Save EBNull/1152373 to your computer and use it in GitHub Desktop.
Returns all foreign key relations to a django model and their accessors
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#Some utilities to work with django models and foreign keys. | |
#Especially useful for finding (and writing out) code to illuminate information about relations. | |
# | |
#To try it out, run show_relation_accessors(model) in a python prompt, or to be more general, get_related_instance_ids_code(model) | |
# | |
#-2011 CBWhiz | |
# | |
# | |
#Usage sample: | |
# from django.db import transaction | |
# | |
# c = get_related_objects(HatedModel.objects.filter(id=24576)) | |
# c.data.keys() | |
# [<class 'app1.models.HatedModel'>, <class 'app2.models.MissionCriticalModel'>] | |
# | |
# transaction.enter_transaction_management() | |
# exec get_replace_object_relations_code(HatedModel, 24576, 12740) | |
# c = get_related_objects(HatedModel.objects.filter(id=24576)) | |
# c.data.keys() | |
# [<class 'app1.models.HatedModel'>] | |
# transaction.commit() | |
from django.db.models.deletion import Collector | |
def get_related_objects(qs, using='default'): | |
"""Returns a Collector instance whose 'data' attribute is a mapping of Models to a set of dependent instances of that model""" | |
c = Collector(using) | |
c.collect(qs) | |
return c | |
def get_relations(model, global_accessors=False, instance_filter=True): | |
"""Returns all (non gfk) foreign key relations to a django model and their accessors""" | |
for ro in model._meta.get_all_related_objects(include_hidden=True): | |
an = "%s.%s"%('%s', ro.get_accessor_name()) | |
if an.endswith('+') or global_accessors: | |
#m2m | |
if instance_filter: | |
an = "%s.objects.filter(%s=%s)"%(ro.model.__name__, ro.field.name, '%s') | |
else: | |
an = "%s.objects.filter(%s__isnull=False)"%(ro.model.__name__, ro.field.name) | |
else: | |
an += '.all()' | |
yield ( | |
model.__name__, | |
ro.field.rel.get_related_field().name, | |
ro.model._meta.app_label, | |
ro.model.__name__, | |
ro.field.name, | |
an | |
) | |
def get_relations_gfk(model, global_accessors=False, instance_filter=True): | |
for rf in model._meta.many_to_many: | |
if not rf.rel.through: | |
#rf is a GenericRelation (RelatedField) | |
ct = ContentType.objects.get_for_model(model).pk | |
if global_accessors: | |
an = "%s.objects.filter(%s=%s)"%(rf.rel.to.__name__, rf.content_type_field_name, ct) | |
if instance_filter: | |
an += ".filter(%s=%%s)"%(rf.object_id_field_name) | |
else: | |
an = "%%s.%s.all()"%(rf.name, ) | |
yield ( | |
model.__name__, | |
rf.m2m_target_field_name(), | |
rf.rel.to._meta.app_label, | |
rf.rel.to.__name__, | |
rf.object_id_field_name, | |
rf.content_type_field_name, | |
rf.name, | |
an, | |
) | |
def get_related_instance_ids_code(model): | |
r = list(get_relations(model, True, False)) + list(get_relations_gfk(model, True, False)) | |
imports = ["from django.db.models.loading import get_model"] | |
qsgroups = [] | |
for i in r: | |
imports.append("%s = get_model('%s', '%s')"%(i[3], i[2], i[3])) | |
qsgroups.append(i[-1] + '.values_list("%s", flat=True)'%(i[4])) | |
ld = locals() | |
pycode = '\n'.join(imports) + "\nqslist=[\n\t%s\n]"%(",\n\t".join(qsgroups)) | |
return pycode | |
def get_unrelated_instance_qs(model): | |
exec get_related_instance_ids_code(model); | |
from django.db.models import Q | |
filt = [] | |
for qs in qslist: | |
filt.append(~Q(pk__in=qs)) | |
return model.objects.filter(reduce(lambda x, y: x & y, filt)) | |
def get_replace_object_relations_code(model, oldid, newid): | |
"""Returns python code that will replace an object's relations to point to a different object (of the same type)""" | |
r = list(get_relations(model, True, False)) + list(get_relations_gfk(model, True, False)) | |
imports = ["from django.db.models.loading import get_model"] | |
qsgroups = [] | |
for i in r: | |
imports.append("%s = get_model('%s', '%s')"%(i[3], i[2], i[3])) | |
qsgroups.append(i[-1] + '.filter(%s=%s).update(%s=%s)'%(i[4], oldid, i[4], newid)) | |
ld = locals() | |
pycode = '\n'.join(imports) + "\n\n%s"%("\n".join(qsgroups)) | |
return pycode | |
def massreplace(model, idmap): | |
"""Replace all references to the instances of a model with other instances, given an {old_pk:new_pk} map""" | |
for v in idmap.values(): | |
assert v not in idmap, "ID overlap" | |
for k, v in idmap.iteritems(): | |
exec get_replace_object_relations_code(model, k, v) | |
c = get_related_objects(model.objects.filter(id=k)) | |
assert len(c.data.keys()) == 1, "Replacment from %s to %s still left %s relations!"%(k, v, len(c.data.keys())) | |
def show_relation_accessors(model): | |
r = list(get_relations(model)) + list(get_relations_gfk(model)) | |
for i in r: | |
print i[-1]%('instance') |
https://docs.djangoproject.com/en/2.1/ref/models/meta/
As part of the formalization of the Model._meta API (from the django.db.models.options.Options class), a number of methods and properties have been deprecated and will be removed in Django 1.10.
MyModel._meta.get_all_related_objects() becomes:
[
f for f in MyModel._meta.get_fields()
if (f.one_to_many or f.one_to_one)
and f.auto_created and not f.concrete
]
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Seeing the same thing. Guess this was for a much older version of django