Skip to content

Commit 4571d1b

Browse files
committed
2 parents 9a42777 + 12f774a commit 4571d1b

1 file changed

Lines changed: 88 additions & 15 deletions

File tree

test_utils/management/commands/makefixture.py

Lines changed: 88 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,38 @@
1-
#From http://www.djangosnippets.org/snippets/918/
1+
"""
2+
"Make fixture" command.
23
4+
Highly useful for making test fixtures. Use it to pick only few items
5+
from your data to serialize, restricted by primary keys. By default
6+
command also serializes foreign keys and m2m relations. You can turn
7+
off related items serialization with --skip-related option.
8+
9+
How to use:
10+
python manage.py makefixture
11+
12+
will display what models are installed
13+
14+
python manage.py makefixture User[:3]
15+
or
16+
python manage.py makefixture auth.User[:3]
17+
or
18+
python manage.py makefixture django.contrib.auth.User[:3]
19+
20+
will serialize users with ids 1 and 2, with assigned groups, permissions
21+
and content types.
22+
23+
python manage.py makefixture YourModel[3] YourModel[6:10]
24+
25+
will serialize YourModel with key 3 and keys 6 to 9 inclusively.
26+
27+
Of course, you can serialize whole tables, and also different tables at
28+
once, and use options of dumpdata:
29+
30+
python manage.py makefixture --format=xml --indent=4 YourModel[3] AnotherModel auth.User[:5] auth.Group
31+
"""
32+
# From http://www.djangosnippets.org/snippets/918/
33+
34+
#save into anyapp/management/commands/makefixture.py
35+
#or back into django/core/management/commands/makefixture.py
336
#v0.1 -- current version
437
#known issues:
538
#no support for generic relations
@@ -25,28 +58,47 @@ class Command(LabelCommand):
2558
option_list = BaseCommand.option_list + (
2659
make_option('--skip-related', default=True, action='store_false', dest='propagate',
2760
help='Specifies if we shall not add related objects.'),
61+
make_option('--reverse', default=[], action='append', dest='reverse',
62+
help="Reverse relations to follow (e.g. 'Job.task_set')."),
2863
make_option('--format', default='json', dest='format',
2964
help='Specifies the output serialization format for fixtures.'),
3065
make_option('--indent', default=None, dest='indent', type='int',
3166
help='Specifies the indent level to use when pretty-printing output'),
3267
)
33-
68+
def handle_reverse(self, **options):
69+
follow_reverse = options.get('reverse', [])
70+
to_reverse = {}
71+
for arg in follow_reverse:
72+
try:
73+
model_name, related_set_name = arg.rsplit(".", 1)
74+
except:
75+
raise CommandError("Bad fieldname on '--reverse %s'" % arg)
76+
model = self.get_model_from_name(model_name)
77+
try:
78+
getattr(model, related_set_name)
79+
except AttributeError:
80+
raise CommandError("Field '%s' does not exist on model '%s'" % (
81+
related_set_name, model_name))
82+
to_reverse.setdefault(model, []).append(related_set_name)
83+
return to_reverse
84+
3485
def handle_models(self, models, **options):
3586
format = options.get('format','json')
3687
indent = options.get('indent',None)
3788
show_traceback = options.get('traceback', False)
3889
propagate = options.get('propagate', True)
39-
90+
follow_reverse = self.handle_reverse(**options)
91+
4092
# Check that the serialization format exists; this is a shortcut to
4193
# avoid collating all the objects and _then_ failing.
4294
if format not in serializers.get_public_serializer_formats():
4395
raise CommandError("Unknown serialization format: %s" % format)
44-
96+
4597
try:
4698
serializers.get_serializer(format)
4799
except KeyError:
48100
raise CommandError("Unknown serialization format: %s" % format)
49-
101+
50102
objects = []
51103
for model, slice in models:
52104
if isinstance(slice, basestring):
@@ -61,7 +113,7 @@ def handle_models(self, models, **options):
61113
objects.extend(items)
62114
else:
63115
raise CommandError("Wrong slice: %s" % slice)
64-
116+
65117
all = objects
66118
if propagate:
67119
collected = set([(x.__class__, x.pk) for x in all])
@@ -70,6 +122,7 @@ def handle_models(self, models, **options):
70122
for x in objects:
71123
if DEBUG:
72124
print "Adding %s[%s]" % (model_name(x), x.pk)
125+
# follow forward relation fields
73126
for f in x.__class__._meta.fields + x.__class__._meta.many_to_many:
74127
if isinstance(f, ForeignKey):
75128
new = getattr(x, f.name) # instantiate object
@@ -81,9 +134,16 @@ def handle_models(self, models, **options):
81134
if new and not (new.__class__, new.pk) in collected:
82135
collected.add((new.__class__, new.pk))
83136
related.append(new)
137+
# follow reverse relations as requested
138+
for reverse_field in follow_reverse.get(x.__class__, []):
139+
mgr = getattr(x, reverse_field)
140+
for new in mgr.all():
141+
if new and not (new.__class__, new.pk) in collected:
142+
collected.add((new.__class__, new.pk))
143+
related.append(new)
84144
objects = related
85145
all.extend(objects)
86-
146+
87147
try:
88148
return serializers.serialize(format, all, indent=indent)
89149
except Exception, e:
@@ -94,6 +154,24 @@ def handle_models(self, models, **options):
94154
def get_models(self):
95155
return [(m, model_name(m)) for m in get_models()]
96156

157+
def get_model_from_name(self, search):
158+
"""Given a name of a model, return the model object associated with it
159+
160+
The name can be either fully specified or uniquely matching the
161+
end of the model name. e.g.
162+
django.contrib.auth.User
163+
or
164+
auth.User
165+
raises CommandError if model can't be found or uniquely determined
166+
"""
167+
models = [model for model, name in self.get_models()
168+
if name.endswith('.'+name) or name == search]
169+
if not models:
170+
raise CommandError("Unknown model: %s" % search)
171+
if len(models)>1:
172+
raise CommandError("Ambiguous model name: %s" % search)
173+
return models[0]
174+
97175
def handle_label(self, labels, **options):
98176
parsed = []
99177
for label in labels:
@@ -105,13 +183,8 @@ def handle_label(self, labels, **options):
105183
slice = pks.rstrip(']').split(':', 1)
106184
elif pks:
107185
slice = pks.rstrip(']')
108-
models = [model for model, name in self.get_models()
109-
if name.endswith('.'+search) or name == search]
110-
if not models:
111-
raise CommandError("Wrong model: %s" % search)
112-
if len(models)>1:
113-
raise CommandError("Ambiguous model name: %s" % search)
114-
parsed.append((models[0], slice))
186+
model = self.get_model_from_name(search)
187+
parsed.append((model, slice))
115188
return self.handle_models(parsed, **options)
116189

117190
def list_models(self):
@@ -125,5 +198,5 @@ def handle(self, *labels, **options):
125198
output = []
126199
label_output = self.handle_label(labels, **options)
127200
if label_output:
128-
output.append(label_output)
201+
output.append(label_output)
129202
return '\n'.join(output)

0 commit comments

Comments
 (0)