Show
Ignore:
Timestamp:
03/02/07 19:59:34 (3 years ago)
Author:
akaihola
Message:

dbpickle now supports many-to-many relations.

Files:
1 modified

Legend:

Unmodified
Added
Removed
  • trunk/dbpickle/dbpickle.py

    r82 r83  
    2929 
    3030This version handles ForeignKeys which refer to the related model's 
    31 primary key.  ManyToMany and OneToOne relations are not supported. 
     31primary key.  ManyToMany relations are now supported, but OneToOne 
     32relations are not. 
    3233 
    3334You can also use you own plugin for pre-processing objects before 
     
    7677def dump(filepath): 
    7778    """ 
    78     Pickle all Django objects from the database and save them into the given file. 
    79     """ 
    80     data = {} 
     79    Pickle all Django objects from the database and save them into the 
     80    given file. 
     81    """ 
     82    objects = {}   # model instances 
     83    m2m_lists = [] # ManyToMany relations between model instances 
    8184    for model in models.get_models(): 
    8285        meta = model._meta 
    8386        app, model_name = meta.app_label, meta.module_name 
     87 
     88        # get all many-to-many relation field names for this model 
     89        m2ms = [m2m.name for m2m in meta.many_to_many] 
     90         
    8491        for obj in model.objects.all(): 
    8592            logging.info('dumping %s.%s %s' % (app, model_name, obj)) 
    86             data[app, model_name, obj._get_pk_val()] = obj 
    87     cPickle.dump(data, file(filepath, 'w')) 
     93            pk = obj._get_pk_val() 
     94            objects[app, model_name, pk] = obj 
     95             
     96            for m2m in m2ms: 
     97                # store many-to-many related objects for every 
     98                # many-to-many relation of this object 
     99                foreign_objs = getattr(obj, m2m).all() 
     100                logging.info('dumping %s.%s.%s x %d' % (app, model_name, m2m, len(foreign_objs))) 
     101                m2m_lists.append((obj, m2m, tuple(foreign_objs))) 
     102 
     103    # pickle all objects and many-to-many relations on disk 
     104    cPickle.dump((objects, m2m_lists), file(filepath, 'w')) 
    88105 
    89106 
     
    95112    """ 
    96113 
     114    # load the plugin if specified on the command line 
    97115    if pluginpath: 
    98116        plugin = load_source('plugin', pluginpath) 
     
    100118        plugin = None 
    101119 
     120    # get the hook functions from the plugin 
    102121    hooks = {} 
    103122    for hookname in 'pre_save', 'pre_walk': 
    104123        hooks[hookname] = getattr(plugin, '%s_hook' % hookname, lambda obj: False) 
    105124     
    106     # unpickle objects from file 
    107     data = cPickle.load(file(filepath)) 
    108  
    109     # delete objects from all models to be loaded 
    110     models = set([obj.__class__ for obj in data.itervalues()]) 
     125    # unpickle objects and many-to-many relations from disk 
     126    objects, m2m_lists = cPickle.load(file(filepath)) 
     127 
     128    # Find distinct models of all unpickled objects and delete all 
     129    # objects before loading.  Note that models which have not been 
     130    # dumped are not emptied. 
     131    models = set( [obj.__class__ for obj in objects.itervalues()] ) 
    111132    for model in models: 
    112133        for obj in model._default_manager.all(): 
     
    114135 
    115136    # load all objects 
    116     while data: 
    117         key, obj = data.popitem() 
    118         load_recursive(data, obj, hooks) 
     137    while objects: 
     138        key, obj = objects.popitem() 
     139        load_recursive(objects, obj, hooks) 
     140 
     141    # load all many-to-many relations 
     142    for obj1, m2m, foreign_objs in m2m_lists: 
     143        meta1 = obj1._meta 
     144        for obj2 in foreign_objs: 
     145            meta2 = obj2._meta 
     146            logging.info('loading ManyToMany %s.%s.%s -> %s.%s.%s' % ( 
     147                meta1.app_label, meta1.module_name, obj1._get_pk_val(), 
     148                meta2.app_label, meta2.module_name, obj2._get_pk_val())) 
     149            getattr(obj1, m2m).add(obj2) 
    119150 
    120151load = transaction.commit_on_success(load) 
    121152 
    122153 
    123 def load_recursive(data, obj, hooks): 
     154def load_recursive(objects, obj, hooks): 
    124155    """ 
    125156    Save the given object into the database.  If the object has 
     
    128159    """ 
    129160 
     161    meta = obj._meta 
     162     
    130163    hooks['pre_walk'](obj) 
    131     for field in obj._meta.fields: 
     164    for field in meta.fields: 
    132165        if isinstance(field, models.ForeignKey): 
    133             related_app = field.rel.to._meta.app_label 
    134             related_model = field.rel.to._meta.module_name 
     166            related_meta = field.rel.to._meta 
     167            related_app = related_meta.app_label 
     168            related_model = related_meta.module_name 
    135169            related_pk_val = getattr(obj, field.name+'_id') 
    136170            try: 
    137                 related_obj = data.pop((related_app, 
    138                                         related_model, 
    139                                         related_pk_val)) 
    140                 load_recursive(data, related_obj, hooks) 
     171                related_obj = objects.pop((related_app, 
     172                                           related_model, 
     173                                           related_pk_val)) 
     174                load_recursive(objects, related_obj, hooks) 
    141175            except KeyError: 
    142176                logging.debug('probably loaded already: ' 
    143177                              '%(related_app)s.%(related_model)s ' 
    144178                              '%(related_pk_val)s' % locals()) 
    145     if logging.getLogger().level <= logging.INFO: 
    146         # Must go through this extra step so str(obj) isn't evaluated 
    147         # if no logging message is to be printed.  Sometimes --quiet 
    148         # is needed to avoid error messages when __repr__ or __str__ 
    149         # tries to look up objects not yet loaded. 
    150         logging.info('loading %s' % obj) 
     179                                       
     180    logging.info('loading %s.%s %s' % ( 
     181        meta.app_label, 
     182        meta.module_name, 
     183        obj._get_pk_val())) 
    151184    try: 
    152185        hooks['pre_save'](obj)