Login

Make fixtures and follow relationships.

Author:
andybak
Posted:
July 1, 2015
Language:
Python
Version:
1.6
Score:
0 (after 0 ratings)

Inspired and based on https://djangosnippets.org/snippets/918/

Improvements:

  • Supports natural keys
  • Uses Django's Collector so hopefully follows reverse relationships
  • Fixes problem when you don't specify a slice
  • PEP8 etc.
  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
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
# Based on: https://djangosnippets.org/snippets/918/
# Altered to use: http://stackoverflow.com/questions/2233883/get-all-related-django-model-objects
# v0.1 -- current version
# Known issues:
# No support for generic relations

from optparse import make_option
from django.contrib.admin.utils import NestedObjects
from django.core import serializers
from django.core.management.base import BaseCommand
from django.core.management.base import CommandError
from django.core.management.base import LabelCommand
from django.db.models.loading import get_models

DEBUG = False


def model_name(m):
    module = m.__module__.split('.')[:-1]  # remove .models
    return ".".join(module + [m._meta.object_name])


class Command(LabelCommand):
    help = 'Output the contents of the database as a fixture of the given format.'
    args = 'modelname[pk] or modelname[id1:id2] repeated one or more times'
    option_list = BaseCommand.option_list + (
        make_option('--skip-related', default=True, action='store_false', dest='propagate',
                    help='Specifies if we shall not add related objects.'),
        make_option('--format', default='json', dest='format',
                    help='Specifies the output serialization format for fixtures.'),
        make_option('--indent', default=None, dest='indent', type='int',
                    help='Specifies the indent level to use when pretty-printing output'),
        make_option('--natural-foreign', default=False, action='store_true', dest='use_natural_foreign_keys',
                    help=''),
        make_option('--natural-primary', default=False, action='store_true', dest='use_natural_primary_keys',
                    help=''),
    )
    
    @staticmethod
    def handle_models(models, **options):
        output_format = options.get('format', 'json')
        indent = options.get('indent', None)
        show_traceback = options.get('traceback', False)
        propagate = options.get('propagate', True)
        use_natural_foreign_keys = options.get('use_natural_foreign_keys', False)
        use_natural_primary_keys = options.get('use_natural_primary_keys', False)
        
        # Check that the serialization format exists; this is a shortcut to
        # avoid collating all the objects and _then_ failing.
        if output_format not in serializers.get_public_serializer_formats():
            raise CommandError("Unknown serialization format: %s" % output_format)

        try:
            serializers.get_serializer(output_format)
        except KeyError:
            raise CommandError("Unknown serialization format: %s" % output_format)

        objects = set()
        for model, model_slice in models:
            if isinstance(model_slice, basestring):
                objects.add(*list(model._default_manager.filter(pk__exact=model_slice)))
            elif not model_slice or type(model_slice) is list:
                items = model._default_manager.all()
                if model_slice and model_slice[0]:
                    items = items.filter(pk__gte=model_slice[0])
                if model_slice and model_slice[1]:
                    items = items.filter(pk__lt=model_slice[1])
                items = items.order_by(model._meta.pk.attname)
                objects.add(*list(items))
            else:
                raise CommandError("Wrong slice: %s" % model_slice)
        
        def flatten(container):
            for i in container:
                if isinstance(i, list) or isinstance(i, tuple):
                    for j in flatten(i):
                        yield j
                else:
                    yield i
                    
        if propagate:
            root_objects = list(objects)
            collector = NestedObjects(using='default')
            collector.collect(root_objects)
        
        return serializers.serialize(
            output_format,
            flatten(collector.nested()),
            indent=indent,
            use_natural_foreign_keys=use_natural_foreign_keys,
            use_natural_primary_keys=use_natural_primary_keys,
        ) 

    @staticmethod
    def get_models():
        return [(m, model_name(m)) for m in get_models()]

    def handle_label(self, labels, **options):
        parsed = []
        for label in labels:
            search, pks = label, ''
            if '[' in label:
                search, pks = label.split('[', 1)
            model_slice = ''
            if ':' in pks:
                model_slice = pks.rstrip(']').split(':', 1)
            elif pks:
                model_slice = pks.rstrip(']')
            model_slice = model_slice if model_slice != '' else ['', '']
            models = [model for model, name in self.get_models()
                      if name.endswith('.' + search) or name == search]
            if not models:
                raise CommandError("Wrong model: %s" % search)
            if len(models) > 1:
                raise CommandError("Ambiguous model name: %s" % search)
            parsed.append((models[0], model_slice))
        return self.handle_models(parsed, **options)

    def list_models(self):
        names = [name for _model, name in self.get_models()]
        raise CommandError('Neither model name nor slice given. Installed model names: \n%s' % ",\n".join(names))

    def handle(self, *labels, **options):
        if not labels:
            self.list_models()

        output = []
        label_output = self.handle_label(labels, **options)
        if label_output:
            output.append(label_output)
        return '\n'.join(output)

More like this

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

Comments

oliver (on July 20, 2015):

Hi, would you have sample instructions on how to use this?

#

Please login first before commenting.