Login

duplicate model object merging script

Author:
nstrite
Posted:
August 22, 2007
Language:
Python
Version:
.96
Score:
3 (after 3 ratings)

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

  1. Template tag - list punctuation for a list of items by shapiromatron 3 months, 1 week ago
  2. JSONRequestMiddleware adds a .json() method to your HttpRequests by cdcarter 3 months, 2 weeks ago
  3. Serializer factory with Django Rest Framework by julio 10 months, 1 week ago
  4. Image compression before saving the new model / work with JPG, PNG by Schleidens 11 months ago
  5. Help text hyperlinks by sa2812 11 months, 4 weeks ago

Comments

exogen (on September 14, 2008):

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.