web/lib/django_extensions/management/commands/dumpscript.py
author ymh <ymh.work@gmail.com>
Wed, 20 Jan 2010 12:37:40 +0100
changeset 3 526ebd3988b0
permissions -rw-r--r--
replace pocketfilms occurence by blinkster
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
3
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
     1
#!/usr/bin/env python
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
     2
# -*- coding: UTF-8 -*-
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
     3
"""
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
     4
      Title: Dumpscript management command
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
     5
    Project: Hardytools (queryset-refactor version)
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
     6
     Author: Will Hardy (http://willhardy.com.au)
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
     7
       Date: June 2008
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
     8
      Usage: python manage.py dumpscript appname > scripts/scriptname.py
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
     9
  $Revision: 217 $
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    10
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    11
Description: 
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    12
    Generates a Python script that will repopulate the database using objects.
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    13
    The advantage of this approach is that it is easy to understand, and more
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    14
    flexible than directly populating the database, or using XML.
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    15
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    16
    * It also allows for new defaults to take effect and only transfers what is
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    17
      needed.
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    18
    * If a new database schema has a NEW ATTRIBUTE, it is simply not
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    19
      populated (using a default value will make the transition smooth :)
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    20
    * If a new database schema REMOVES AN ATTRIBUTE, it is simply ignored
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    21
      and the data moves across safely (I'm assuming we don't want this
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    22
      attribute anymore.
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    23
    * Problems may only occur if there is a new model and is now a required
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    24
      ForeignKey for an existing model. But this is easy to fix by editing the
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    25
      populate script :)
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    26
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    27
Improvements:
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    28
    See TODOs and FIXMEs scattered throughout :-)
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    29
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    30
"""
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    31
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    32
import sys
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    33
from django.db import models
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    34
from django.core.exceptions import ObjectDoesNotExist
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    35
from django.core.management.base import BaseCommand
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    36
from django.utils.encoding import smart_unicode, force_unicode
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    37
from django.contrib.contenttypes.models import ContentType
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    38
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    39
class Command(BaseCommand):
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    40
    help = 'Dumps the data as a customised python script.'
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    41
    args = '[appname ...]'
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    42
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    43
    def handle(self, *app_labels, **options):
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    44
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    45
        # Get the models we want to export
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    46
        models = get_models(app_labels)
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    47
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    48
        # A dictionary is created to keep track of all the processed objects,
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    49
        # so that foreign key references can be made using python variable names.
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    50
        # This variable "context" will be passed around like the town bicycle.
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    51
        context = {}
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    52
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    53
        # Create a dumpscript object and let it format itself as a string
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    54
        print Script(models=models, context=context)
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    55
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    56
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    57
def get_models(app_labels):
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    58
    """ Gets a list of models for the given app labels, with some exceptions. 
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    59
        TODO: If a required model is referenced, it should also be included.
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    60
        Or at least discovered with a get_or_create() call.
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    61
    """
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    62
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    63
    from django.db.models import get_app, get_apps, get_model
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    64
    from django.db.models import get_models as get_all_models
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    65
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    66
    # These models are not to be output, e.g. because they can be generated automatically
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    67
    # TODO: This should be "appname.modelname" string
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    68
    from django.contrib.contenttypes.models import ContentType
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    69
    EXCLUDED_MODELS = (ContentType, )
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    70
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    71
    models = []
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    72
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    73
    # If no app labels are given, return all
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    74
    if not app_labels:
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    75
        for app in get_apps():
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    76
            models += [ m for m in get_all_models(app) if m not in EXCLUDED_MODELS ]
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    77
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    78
    # Get all relevant apps
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    79
    for app_label in app_labels:
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    80
        # If a specific model is mentioned, get only that model
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    81
        if "." in app_label:
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    82
            app_label, model_name = app_label.split(".", 1)
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    83
            models.append(get_model(app_label, model_name))
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    84
        # Get all models for a given app
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    85
        else:
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    86
            models += [ m for m in get_all_models(get_app(app_label)) if m not in EXCLUDED_MODELS ]
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    87
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    88
    return models
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    89
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    90
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    91
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    92
class Code(object):
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    93
    """ A snippet of python script. 
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    94
        This keeps track of import statements and can be output to a string.
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    95
        In the future, other features such as custom indentation might be included
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    96
        in this class.
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    97
    """
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    98
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    99
    def __init__(self):
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   100
        self.imports = {}
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   101
        self.indent = -1 
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   102
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   103
    def __str__(self):
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   104
        """ Returns a string representation of this script. 
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   105
        """
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   106
        if self.imports:
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   107
            sys.stderr.write(repr(self.import_lines))
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   108
            return flatten_blocks([""] + self.import_lines + [""] + self.lines, num_indents=self.indent)
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   109
        else:
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   110
            return flatten_blocks(self.lines, num_indents=self.indent)
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   111
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   112
    def get_import_lines(self):
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   113
        """ Takes the stored imports and converts them to lines
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   114
        """
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   115
        if self.imports:
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   116
            return [ "from %s import %s" % (value, key) for key, value in self.imports.items() ]
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   117
        else:
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   118
            return []
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   119
    import_lines = property(get_import_lines)
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   120
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   121
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   122
class ModelCode(Code):
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   123
    " Produces a python script that can recreate data for a given model class. "
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   124
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   125
    def __init__(self, model, context={}):
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   126
        self.model = model
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   127
        self.context = context
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   128
        self.instances = []
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   129
        self.indent = 0
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   130
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   131
    def get_imports(self):
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   132
        """ Returns a dictionary of import statements, with the variable being
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   133
            defined as the key. 
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   134
        """
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   135
        return { self.model.__name__: smart_unicode(self.model.__module__) }
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   136
    imports = property(get_imports)
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   137
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   138
    def get_lines(self):
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   139
        """ Returns a list of lists or strings, representing the code body. 
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   140
            Each list is a block, each string is a statement.
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   141
        """
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   142
        code = []
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   143
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   144
        for counter, item in enumerate(self.model.objects.all()):
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   145
            instance = InstanceCode(instance=item, id=counter+1, context=self.context)
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   146
            self.instances.append(instance)
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   147
            if instance.waiting_list:
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   148
                code += instance.lines
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   149
 
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   150
        # After each instance has been processed, try again.
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   151
        # This allows self referencing fields to work.
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   152
        for instance in self.instances:
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   153
            if instance.waiting_list:
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   154
                code += instance.lines
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   155
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   156
        return code
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   157
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   158
    lines = property(get_lines)
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   159
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   160
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   161
class InstanceCode(Code):
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   162
    " Produces a python script that can recreate data for a given model instance. "
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   163
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   164
    def __init__(self, instance, id, context={}):
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   165
        """ We need the instance in question and an id """
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   166
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   167
        self.instance = instance
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   168
        self.model = self.instance.__class__
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   169
        self.context = context
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   170
        self.variable_name = "%s_%s" % (self.instance._meta.db_table, id)
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   171
        self.skip_me = None
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   172
        self.instantiated = False
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   173
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   174
        self.indent  = 0 
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   175
        self.imports = {}
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   176
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   177
        self.waiting_list = list(self.model._meta.fields)
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   178
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   179
        self.many_to_many_waiting_list = {} 
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   180
        for field in self.model._meta.many_to_many:
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   181
            self.many_to_many_waiting_list[field] = list(getattr(self.instance, field.name).all())
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   182
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   183
    def get_lines(self, force=False):
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   184
        """ Returns a list of lists or strings, representing the code body. 
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   185
            Each list is a block, each string is a statement.
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   186
            
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   187
            force (True or False): if an attribute object cannot be included, 
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   188
            it is usually skipped to be processed later. With 'force' set, there
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   189
            will be no waiting: a get_or_create() call is written instead.
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   190
        """
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   191
        code_lines = []
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   192
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   193
        # Don't return anything if this is an instance that should be skipped
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   194
        if self.skip():
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   195
            return []
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   196
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   197
        # Initialise our new object
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   198
        # e.g. model_name_35 = Model()
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   199
        code_lines += self.instantiate()
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   200
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   201
        # Add each field
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   202
        # e.g. model_name_35.field_one = 1034.91
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   203
        #      model_name_35.field_two = "text"
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   204
        code_lines += self.get_waiting_list()
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   205
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   206
        if force:
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   207
            # TODO: Check that M2M are not affected
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   208
            code_lines += self.get_waiting_list(force=force)
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   209
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   210
        # Print the save command for our new object
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   211
        # e.g. model_name_35.save()
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   212
        if code_lines:
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   213
            code_lines.append("%s.save()\n" % (self.variable_name))
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   214
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   215
        code_lines += self.get_many_to_many_lines(force=force)
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   216
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   217
        return code_lines
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   218
    lines = property(get_lines)
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   219
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   220
    def skip(self):
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   221
        """ Determine whether or not this object should be skipped.
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   222
            If this model is a parent of a single subclassed instance, skip it.
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   223
            The subclassed instance will create this parent instance for us.
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   224
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   225
            TODO: Allow the user to force its creation?
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   226
        """
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   227
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   228
        if self.skip_me is not None:
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   229
            return self.skip_me
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   230
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   231
        try:
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   232
            # Django trunk since r7722 uses CollectedObjects instead of dict
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   233
            from django.db.models.query import CollectedObjects
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   234
            sub_objects = CollectedObjects()
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   235
        except ImportError:
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   236
            # previous versions don't have CollectedObjects
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   237
            sub_objects = {}
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   238
        self.instance._collect_sub_objects(sub_objects)
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   239
        if reduce(lambda x, y: x+y, [self.model in so._meta.parents for so in sub_objects.keys()]) == 1:
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   240
            pk_name = self.instance._meta.pk.name
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   241
            key = '%s_%s' % (self.model.__name__, getattr(self.instance, pk_name))
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   242
            self.context[key] = None
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   243
            self.skip_me = True
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   244
        else:
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   245
            self.skip_me = False
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   246
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   247
        return self.skip_me
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   248
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   249
    def instantiate(self):
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   250
        " Write lines for instantiation "
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   251
        # e.g. model_name_35 = Model()
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   252
        code_lines = []
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   253
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   254
        if not self.instantiated:
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   255
            code_lines.append("%s = %s()" % (self.variable_name, self.model.__name__))
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   256
            self.instantiated = True
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   257
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   258
            # Store our variable name for future foreign key references
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   259
            pk_name = self.instance._meta.pk.name
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   260
            key = '%s_%s' % (self.model.__name__, getattr(self.instance, pk_name))
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   261
            self.context[key] = self.variable_name
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   262
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   263
        return code_lines
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   264
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   265
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   266
    def get_waiting_list(self, force=False):
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   267
        " Add lines for any waiting fields that can be completed now. "
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   268
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   269
        code_lines = []
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   270
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   271
        # Process normal fields
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   272
        for field in list(self.waiting_list):
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   273
            try:
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   274
                # Find the value, add the line, remove from waiting list and move on
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   275
                value = get_attribute_value(self.instance, field, self.context, force=force)
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   276
                code_lines.append('%s.%s = %s' % (self.variable_name, field.name, value))
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   277
                self.waiting_list.remove(field)
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   278
            except SkipValue, e:
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   279
                # Remove from the waiting list and move on
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   280
                self.waiting_list.remove(field)
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   281
                continue
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   282
            except DoLater, e:
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   283
                # Move on, maybe next time
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   284
                continue
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   285
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   286
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   287
        return code_lines
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   288
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   289
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   290
    def get_many_to_many_lines(self, force=False):
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   291
        """ Generates lines that define many to many relations for this instance. """
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   292
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   293
        lines = []
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   294
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   295
        for field, rel_items in self.many_to_many_waiting_list.items():
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   296
            for rel_item in list(rel_items):
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   297
                try:
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   298
                    pk_name = rel_item._meta.pk.name
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   299
                    key = '%s_%s' % (rel_item.__class__.__name__, getattr(rel_item, pk_name))
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   300
                    value = "%s" % self.context[key]
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   301
                    lines.append('%s.%s.add(%s)' % (self.variable_name, field.name, value))
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   302
                    self.many_to_many_waiting_list[field].remove(rel_item)
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   303
                except KeyError:
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   304
                    if force:
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   305
                        value = "%s.objects.get(%s=%s)" % (rel_item._meta.object_name, pk_name, getattr(rel_item, pk_name))
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   306
                        lines.append('%s.%s.add(%s)' % (self.variable_name, field.name, value))
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   307
                        self.many_to_many_waiting_list[field].remove(rel_item)
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   308
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   309
        if lines:
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   310
            lines.append("")
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   311
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   312
        return lines
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   313
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   314
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   315
class Script(Code):
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   316
    " Produces a complete python script that can recreate data for the given apps. "
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   317
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   318
    def __init__(self, models, context={}):
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   319
        self.models = models
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   320
        self.context = context
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   321
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   322
        self.indent = -1 
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   323
        self.imports = {}
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   324
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   325
    def get_lines(self):
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   326
        """ Returns a list of lists or strings, representing the code body. 
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   327
            Each list is a block, each string is a statement.
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   328
        """
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   329
        code = [ self.FILE_HEADER.strip() ]
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   330
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   331
        # Queue and process the required models
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   332
        for model_class in queue_models(self.models, context=self.context):
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   333
            sys.stderr.write('Processing model: %s\n' % model_class.model.__name__)
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   334
            code.append(model_class.import_lines)
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   335
            code.append("")
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   336
            code.append(model_class.lines)
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   337
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   338
        # Process left over foreign keys from cyclic models
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   339
        for model in self.models:
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   340
            sys.stderr.write('Re-processing model: %s\n' % model.model.__name__)
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   341
            for instance in model.instances:
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   342
                if instance.waiting_list or instance.many_to_many_waiting_list:
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   343
                    code.append(instance.get_lines(force=True))
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   344
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   345
        return code
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   346
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   347
    lines = property(get_lines)
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   348
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   349
    # A user-friendly file header
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   350
    FILE_HEADER = """
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   351
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   352
#!/usr/bin/env python
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   353
# -*- coding: utf-8 -*-
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   354
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   355
# This file has been automatically generated, changes may be lost if you
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   356
# go and generate it again. It was generated with the following command:
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   357
# %s
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   358
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   359
import datetime
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   360
from decimal import Decimal
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   361
from django.contrib.contenttypes.models import ContentType
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   362
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   363
def run():
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   364
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   365
""" % " ".join(sys.argv)
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   366
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   367
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   368
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   369
# HELPER FUNCTIONS
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   370
#-------------------------------------------------------------------------------
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   371
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   372
def flatten_blocks(lines, num_indents=-1):
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   373
    """ Takes a list (block) or string (statement) and flattens it into a string
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   374
        with indentation. 
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   375
    """
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   376
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   377
    # The standard indent is four spaces
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   378
    INDENTATION = " " * 4
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   379
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   380
    if not lines:
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   381
        return ""
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   382
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   383
    # If this is a string, add the indentation and finish here
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   384
    if isinstance(lines, basestring):
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   385
        return INDENTATION * num_indents + lines
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   386
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   387
    # If this is not a string, join the lines and recurse
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   388
    return "\n".join([ flatten_blocks(line, num_indents+1) for line in lines ])
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   389
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   390
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   391
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   392
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   393
def get_attribute_value(item, field, context, force=False):
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   394
    """ Gets a string version of the given attribute's value, like repr() might. """
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   395
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   396
    # Find the value of the field, catching any database issues
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   397
    try:
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   398
        value = getattr(item, field.name)
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   399
    except ObjectDoesNotExist:
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   400
        raise SkipValue('Could not find object for %s.%s, ignoring.\n' % (item.__class__.__name__, field.name))
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   401
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   402
    # AutoField: We don't include the auto fields, they'll be automatically recreated
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   403
    if isinstance(field, models.AutoField):
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   404
        raise SkipValue()
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   405
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   406
    # Some databases (eg MySQL) might store boolean values as 0/1, this needs to be cast as a bool
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   407
    elif isinstance(field, models.BooleanField) and value is not None:
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   408
        return repr(bool(value))
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   409
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   410
    # Post file-storage-refactor, repr() on File/ImageFields no longer returns the path
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   411
    elif isinstance(field, models.FileField):
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   412
        return repr(force_unicode(value))
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   413
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   414
    # ForeignKey fields, link directly using our stored python variable name
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   415
    elif isinstance(field, models.ForeignKey) and value is not None:
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   416
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   417
        # Special case for contenttype foreign keys: no need to output any
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   418
        # content types in this script, as they can be generated again 
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   419
        # automatically.
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   420
        # NB: Not sure if "is" will always work
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   421
        if field.rel.to is ContentType:
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   422
            return 'ContentType.objects.get(app_label="%s", model="%s")' % (value.app_label, value.model)
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   423
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   424
        # Generate an identifier (key) for this foreign object
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   425
        pk_name = value._meta.pk.name
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   426
        key = '%s_%s' % (value.__class__.__name__, getattr(value, pk_name))
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   427
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   428
        if key in context:
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   429
            variable_name = context[key]
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   430
            # If the context value is set to None, this should be skipped.
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   431
            # This identifies models that have been skipped (inheritance)
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   432
            if variable_name is None:
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   433
                raise SkipValue()
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   434
            # Return the variable name listed in the context 
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   435
            return "%s" % variable_name
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   436
        elif force:
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   437
            return "%s.objects.get(%s=%s)" % (value._meta.object_name, pk_name, getattr(value, pk_name))
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   438
        else:
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   439
            raise DoLater('(FK) %s.%s\n' % (item.__class__.__name__, field.name))
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   440
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   441
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   442
    # A normal field (e.g. a python built-in)
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   443
    else:
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   444
        return repr(value)
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   445
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   446
def queue_models(models, context):
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   447
    """ Works an an appropriate ordering for the models.
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   448
        This isn't essential, but makes the script look nicer because 
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   449
        more instances can be defined on their first try.
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   450
    """
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   451
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   452
    # Max number of cycles allowed before we call it an infinite loop.
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   453
    MAX_CYCLES = 5
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   454
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   455
    model_queue = []
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   456
    number_remaining_models = len(models)
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   457
    allowed_cycles = MAX_CYCLES
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   458
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   459
    while number_remaining_models > 0:
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   460
        previous_number_remaining_models = number_remaining_models
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   461
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   462
        model = models.pop(0)
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   463
        
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   464
        # If the model is ready to be processed, add it to the list
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   465
        if check_dependencies(model, model_queue):
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   466
            model_class = ModelCode(model=model, context=context)
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   467
            model_queue.append(model_class)
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   468
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   469
        # Otherwise put the model back at the end of the list
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   470
        else:
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   471
            models.append(model)
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   472
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   473
        # Check for infinite loops. 
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   474
        # This means there is a cyclic foreign key structure
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   475
        # That cannot be resolved by re-ordering
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   476
        number_remaining_models = len(models)
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   477
        if number_remaining_models == previous_number_remaining_models:
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   478
            allowed_cycles -= 1
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   479
            if allowed_cycles <= 0:
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   480
                # Add the remaining models, but do not remove them from the model list
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   481
                missing_models = [ ModelCode(model=m, context=context) for m in models ]
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   482
                model_queue += missing_models
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   483
                # Replace the models with the model class objects 
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   484
                # (sure, this is a little bit of hackery)
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   485
                models[:] = missing_models
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   486
                break
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   487
        else:
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   488
            allowed_cycles = MAX_CYCLES
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   489
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   490
    return model_queue
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   491
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   492
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   493
def check_dependencies(model, model_queue):
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   494
    " Check that all the depenedencies for this model are already in the queue. "
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   495
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   496
    # A list of allowed links: existing fields, itself and the special case ContentType
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   497
    allowed_links = [ m.model.__name__ for m in model_queue ] + [model.__name__, 'ContentType']
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   498
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   499
    # For each ForeignKey or ManyToMany field, check that a link is possible
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   500
    for field in model._meta.fields + model._meta.many_to_many:
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   501
        if field.rel and field.rel.to.__name__ not in allowed_links:
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   502
            return False
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   503
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   504
    return True
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   505
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   506
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   507
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   508
# EXCEPTIONS
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   509
#-------------------------------------------------------------------------------
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   510
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   511
class SkipValue(Exception):
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   512
    """ Value could not be parsed or should simply be skipped. """
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   513
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   514
class DoLater(Exception):
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   515
    """ Value could not be parsed or should simply be skipped. """