root / env / lib / python2.7 / site-packages / south / creator / freezer.py @ d1a4905f
History | View | Annotate | Download (7.02 KB)
1 |
"""
|
---|---|
2 |
Handles freezing of models into FakeORMs.
|
3 |
"""
|
4 |
|
5 |
import sys |
6 |
|
7 |
from django.db import models |
8 |
from django.db.models.base import ModelBase, Model |
9 |
from django.contrib.contenttypes.generic import GenericRelation |
10 |
|
11 |
from south.orm import FakeORM |
12 |
from south.utils import get_attribute, auto_through |
13 |
from south import modelsinspector |
14 |
|
15 |
def freeze_apps(apps): |
16 |
"""
|
17 |
Takes a list of app labels, and returns a string of their frozen form.
|
18 |
"""
|
19 |
if isinstance(apps, basestring): |
20 |
apps = [apps] |
21 |
frozen_models = set()
|
22 |
# For each app, add in all its models
|
23 |
for app in apps: |
24 |
for model in models.get_models(models.get_app(app)): |
25 |
# Only add if it's not abstract or proxy
|
26 |
if not model._meta.abstract and not getattr(model._meta, "proxy", False): |
27 |
frozen_models.add(model) |
28 |
# Now, add all the dependencies
|
29 |
for model in list(frozen_models): |
30 |
frozen_models.update(model_dependencies(model)) |
31 |
# Serialise!
|
32 |
model_defs = {} |
33 |
model_classes = {} |
34 |
for model in frozen_models: |
35 |
model_defs[model_key(model)] = prep_for_freeze(model) |
36 |
model_classes[model_key(model)] = model |
37 |
# Check for any custom fields that failed to freeze.
|
38 |
missing_fields = False
|
39 |
for key, fields in model_defs.items(): |
40 |
for field_name, value in fields.items(): |
41 |
if value is None: |
42 |
missing_fields = True
|
43 |
model_class = model_classes[key] |
44 |
field_class = model_class._meta.get_field_by_name(field_name)[0]
|
45 |
print " ! Cannot freeze field '%s.%s'" % (key, field_name) |
46 |
print " ! (this field has class %s.%s)" % (field_class.__class__.__module__, field_class.__class__.__name__) |
47 |
if missing_fields:
|
48 |
print "" |
49 |
print " ! South cannot introspect some fields; this is probably because they are custom" |
50 |
print " ! fields. If they worked in 0.6 or below, this is because we have removed the" |
51 |
print " ! models parser (it often broke things)." |
52 |
print " ! To fix this, read http://south.aeracode.org/wiki/MyFieldsDontWork" |
53 |
sys.exit(1)
|
54 |
|
55 |
return model_defs
|
56 |
|
57 |
def freeze_apps_to_string(apps): |
58 |
return pprint_frozen_models(freeze_apps(apps))
|
59 |
|
60 |
###
|
61 |
|
62 |
def model_key(model): |
63 |
"For a given model, return 'appname.modelname'."
|
64 |
return "%s.%s" % (model._meta.app_label, model._meta.object_name.lower()) |
65 |
|
66 |
def prep_for_freeze(model): |
67 |
"""
|
68 |
Takes a model and returns the ready-to-serialise dict (all you need
|
69 |
to do is just pretty-print it).
|
70 |
"""
|
71 |
fields = modelsinspector.get_model_fields(model, m2m=True)
|
72 |
# Remove useless attributes (like 'choices')
|
73 |
for name, field in fields.items(): |
74 |
fields[name] = remove_useless_attributes(field) |
75 |
# See if there's a Meta
|
76 |
fields['Meta'] = remove_useless_meta(modelsinspector.get_model_meta(model))
|
77 |
# Add in our own special items to track the object name and managed
|
78 |
fields['Meta']['object_name'] = model._meta.object_name # Special: not eval'able. |
79 |
if not getattr(model._meta, "managed", True): |
80 |
fields['Meta']['managed'] = repr(model._meta.managed) |
81 |
return fields
|
82 |
|
83 |
### Dependency resolvers
|
84 |
|
85 |
def model_dependencies(model, checked_models=None): |
86 |
"""
|
87 |
Returns a set of models this one depends on to be defined; things like
|
88 |
OneToOneFields as ID, ForeignKeys everywhere, etc.
|
89 |
"""
|
90 |
depends = set()
|
91 |
checked_models = checked_models or set() |
92 |
# Get deps for each field
|
93 |
for field in model._meta.fields + model._meta.many_to_many: |
94 |
depends.update(field_dependencies(field, checked_models)) |
95 |
# Add in any non-abstract bases
|
96 |
for base in model.__bases__: |
97 |
if issubclass(base, models.Model) and hasattr(base, '_meta') and not base._meta.abstract: |
98 |
depends.add(base) |
99 |
# Now recurse
|
100 |
new_to_check = depends - checked_models |
101 |
while new_to_check:
|
102 |
checked_model = new_to_check.pop() |
103 |
if checked_model == model or checked_model in checked_models: |
104 |
continue
|
105 |
checked_models.add(checked_model) |
106 |
deps = model_dependencies(checked_model, checked_models) |
107 |
# Loop through dependencies...
|
108 |
for dep in deps: |
109 |
# If the new dep is not already checked, add to the queue
|
110 |
if (dep not in depends) and (dep not in new_to_check) and (dep not in checked_models): |
111 |
new_to_check.add(dep) |
112 |
depends.add(dep) |
113 |
return depends
|
114 |
|
115 |
def field_dependencies(field, checked_models=None): |
116 |
checked_models = checked_models or set() |
117 |
depends = set()
|
118 |
arg_defs, kwarg_defs = modelsinspector.matching_details(field) |
119 |
for attrname, options in arg_defs + kwarg_defs.values(): |
120 |
if options.get("ignore_if_auto_through", False) and auto_through(field): |
121 |
continue
|
122 |
if options.get("is_value", False): |
123 |
value = attrname |
124 |
elif attrname == 'rel.through' and hasattr(getattr(field, 'rel', None), 'through_model'): |
125 |
# Hack for django 1.1 and below, where the through model is stored
|
126 |
# in rel.through_model while rel.through stores only the model name.
|
127 |
value = field.rel.through_model |
128 |
else:
|
129 |
try:
|
130 |
value = get_attribute(field, attrname) |
131 |
except AttributeError: |
132 |
if options.get("ignore_missing", False): |
133 |
continue
|
134 |
raise
|
135 |
if isinstance(value, Model): |
136 |
value = value.__class__ |
137 |
if not isinstance(value, ModelBase): |
138 |
continue
|
139 |
if getattr(value._meta, "proxy", False): |
140 |
value = value._meta.proxy_for_model |
141 |
if value in checked_models: |
142 |
continue
|
143 |
checked_models.add(value) |
144 |
depends.add(value) |
145 |
depends.update(model_dependencies(value, checked_models)) |
146 |
|
147 |
return depends
|
148 |
|
149 |
### Prettyprinters
|
150 |
|
151 |
def pprint_frozen_models(models): |
152 |
return "{\n %s\n }" % ",\n ".join([ |
153 |
"%r: %s" % (name, pprint_fields(fields))
|
154 |
for name, fields in sorted(models.items()) |
155 |
]) |
156 |
|
157 |
def pprint_fields(fields): |
158 |
return "{\n %s\n }" % ",\n ".join([ |
159 |
"%r: %r" % (name, defn)
|
160 |
for name, defn in sorted(fields.items()) |
161 |
]) |
162 |
|
163 |
### Output sanitisers
|
164 |
|
165 |
USELESS_KEYWORDS = ["choices", "help_text", "verbose_name"] |
166 |
USELESS_DB_KEYWORDS = ["related_name", "default", "blank"] # Important for ORM, not for DB. |
167 |
INDEX_KEYWORDS = ["db_index"]
|
168 |
|
169 |
def remove_useless_attributes(field, db=False, indexes=False): |
170 |
"Removes useless (for database) attributes from the field's defn."
|
171 |
# Work out what to remove, and remove it.
|
172 |
keywords = USELESS_KEYWORDS[:] |
173 |
if db:
|
174 |
keywords += USELESS_DB_KEYWORDS[:] |
175 |
if indexes:
|
176 |
keywords += INDEX_KEYWORDS[:] |
177 |
if field:
|
178 |
for name in keywords: |
179 |
if name in field[2]: |
180 |
del field[2][name] |
181 |
return field
|
182 |
|
183 |
USELESS_META = ["verbose_name", "verbose_name_plural"] |
184 |
def remove_useless_meta(meta): |
185 |
"Removes useless (for database) attributes from the table's meta."
|
186 |
if meta:
|
187 |
for name in USELESS_META: |
188 |
if name in meta: |
189 |
del meta[name]
|
190 |
return meta
|