Use this function to merge model objects (i.e. Users, Organizations, Polls, Etc.) and migrate all of the related fields from the alias objects the primary object.
Usage:
from django.contrib.auth.models import User
primary_user = User.objects.get(email='[email protected]')
duplicate_user = User.objects.get(email='[email protected]')
merge_model_objects(primary_user, duplicate_user)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 | from django.db import transaction
from django.db.models import get_models
from django.contrib.contenttypes.generic import GenericForeignKey
@transaction.commit_manually
def merge_model_objects(primary_object, *alias_objects):
"""
Use this function to merge model objects (i.e. Users, Organizations, Polls, Etc.) and migrate all of the related fields from the alias objects the primary object.
Usage:
from django.contrib.auth.models import User
primary_user = User.objects.get(email='[email protected]')
duplicate_user = User.objects.get(email='[email protected]')
merge_model_objects(primary_user, duplicate_user)
"""
# Get a list of all GenericForeignKeys in all models
# TODO: this is a bit of a hack, since the generics framework should provide a similar
# method to the ForeignKey field for accessing the generic related fields.
generic_fields = []
for model in get_models():
for field_name, field in filter(lambda x: isinstance(x[1], GenericForeignKey), model.__dict__.iteritems()):
generic_fields.append(field)
# Loop through all alias objects and migrate their data to the primary object.
for alias_object in alias_objects:
try:
# Migrate all foreign key references from alias object to primary object.
for related_object in alias_object._meta.get_all_related_objects():
# The variable name on the alias_object model.
alias_varname = related_object.get_accessor_name()
# The variable name on the related model.
obj_varname = related_object.field.name
related_objects = getattr(alias_object, alias_varname)
for obj in related_objects.all():
try:
setattr(obj, obj_varname, primary_object)
obj.save()
except Exception, e:
print 'Exception: %s' % str(e)
while True:
user_response = raw_input("Do you wish to continue the migration of this object (y/[n])? ")
if user_response == '' or user_response == 'n':
raise Exception('User Aborted Merge.')
elif user_response == 'y':
break
else:
print "Error: you must choose 'y' or 'n'."
print ""
# Migrate all many to many references from alias object to primary object.
for related_many_object in alias_object._meta.get_all_related_many_to_many_objects():
alias_varname = related_many_object.get_accessor_name()
obj_varname = related_many_object.field.name
related_many_objects = getattr(alias_object, alias_varname)
for obj in related_many_objects.all():
try:
getattr(obj, obj_varname).remove(alias_object)
getattr(obj, obj_varname).add(primary_object)
except:
print 'Exception: %s' % str(e)
while True:
user_response = raw_input("Do you wish to continue the migration of this object (y/[n])? ")
if user_response == '' or user_response == 'n':
raise Exception('User Aborted Merge.')
elif user_response == 'y':
break
else:
print "Error: you must choose 'y' or 'n'."
print ""
# Migrate all generic foreign key references from alias object to primary object.
for field in generic_fields:
filter_kwargs = {}
filter_kwargs[field.fk_field] = alias_object._get_pk_val()
filter_kwargs[field.ct_field] = field.get_content_type(alias_object)
for generic_related_object in field.model.objects.filter(**filter_kwargs):
setattr(generic_related_object, field.name, primary_object)
generic_related_object.save()
while True:
user_response = raw_input("Do you wish to keep, delete, or abort the object (%s) %s (k/d/a)? " % (alias_object._get_pk_val(), str(alias_object)))
if user_response == 'a':
raise Exception('User Aborted Merge.')
elif user_response == 'd':
alias_object.delete()
break
elif user_response == 'k':
break
else:
print "Error: you must enter a valid value (k, d, a)."
except:
transaction.rollback()
else:
transaction.commit()
return primary_object
|
More like this
- Template tag - list punctuation for a list of items by shapiromatron 10 months, 3 weeks ago
- JSONRequestMiddleware adds a .json() method to your HttpRequests by cdcarter 11 months ago
- Serializer factory with Django Rest Framework by julio 1 year, 5 months ago
- Image compression before saving the new model / work with JPG, PNG by Schleidens 1 year, 6 months ago
- Help text hyperlinks by sa2812 1 year, 7 months ago
Comments
Wow, I wrote this same code last night. This is great when you're importing models and relationships from e.g. a spreadsheet with typos.
#
Please login first before commenting.