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