1 """Graphical user interface for SQLObject's model design"""
2
3 import os
4 import string
5 import shutil
6
7 import pkg_resources
8
9 import cherrypy
10
11 try:
12 import sqlobject
13 except ImportError:
14 sqlobject = None
15
16 import turbogears
17 from turbogears import controllers, expose
18 from turbogears.util import get_model, get_package_name, get_project_name
19
20
21 model_template = """from datetime import datetime
22 from turbogears.database import PackageHub
23 from sqlobject import *
24 from turbogears import identity
25
26 hub = PackageHub('%s')
27 __connection__ = hub
28
29 %s
30 """
31
32 session_file_name = 'model_designer.tmp'
36 """By Robin Parmar & Martin Miller from Python Cookbook 2. edition"""
37
38 if os.path.isfile(file_spec):
39
40 if vtype not in ('copy', 'rename'):
41 raise ValueError('Unknown vtype %r' % (vtype,))
42
43
44 n, e = os.path.splitext(file_spec)
45
46
47 if len(e) == 4 and e[1:].isdigit():
48 num = 1 + int(e[1:])
49 root = n
50 else:
51 num = 0
52 root = file_spec
53
54
55 for i in xrange(num, 1000):
56 new_file = '%s.%03d' % (root, i)
57 if not os.path.exists(new_file):
58 if vtype == 'copy':
59 shutil.copy(file_spec, new_file)
60 else:
61 os.rename(file_spec, new_file)
62 return True
63 raise RuntimeError("Can't %s %r, all names taken" % (vtype, file_spec))
64 return False
65
66
67 -class Designer(controllers.RootController):
68 """Designer for SQLObject models.
69
70 Create your classes, define your fields and manage your relations.
71 Visualize and generate code for SQLObject models.
72
73 """
74
75 __label__ = 'ModelDesigner'
76 __version__ = '0.1'
77 __author__ = 'Ronald Jaramillo'
78 __email__ = 'ronald@checkandshare.com'
79 __copyright__ = 'Copyright 2005 Ronald Jaramillo'
80 __license__ = 'MIT'
81
82 baseTemplate = 'turbogears.toolbox.designer'
83 model = None
84 icon = "/tg_static/images/designer.png"
85
92
95
100
102 static_directory = pkg_resources.resource_filename(__name__, 'static')
103 turbogears.config.update({'/tg_toolbox/designer': {
104 'tools.staticdir.on' : True,
105 'tools.staticdir.dir': static_directory}})
106
108 """Given a column representation return the column type."""
109 column_type = '%r' % column
110 return column_type.split()[0][1:].split('.')[-1]
111
113 try:
114 default = column.default
115 except AttributeError:
116 return ''
117 if default == sqlobject.sqlbuilder.NoDefault:
118 return ''
119 if column_type in ('SOIntCol', 'SOFloatCol', 'SOStringCol',
120 'SODecimalCol', 'SOCurrencyCol'):
121 return default
122 elif column_type == 'SODateTimeCol':
123 d = '%s' % default
124 return ':'.join(d.split(':')[:-1])
125 elif column_type == 'SODateCol':
126 d = '%s' % default
127 return d.split(None, 1)[0]
128 return ''
129
130 - def load_column(self, column_name, column, model_object):
131 props = {}
132 props['type'] = self.column_type(column)
133 props['column_name'] = column_name
134 props['name'] = column_name
135 props['column_default'] = self.column_default(column, props['type'])
136 try:
137 props['title'] = getattr(column, 'title') or ''
138 except AttributeError:
139 props['title'] = ''
140 if props['type'] == 'SOStringCol':
141 props['length'] = column.length
142 props['varchar'] = column.varchar
143 if props['type'] == 'SOEnumCol':
144 props['enum_values'] = column.enumValues
145 if props['type'] == 'SOForeignKey':
146 props['join_type'] = 'MultipleJoin'
147 props['other_class_name'] = column.foreignKey
148 props['other_method_name'] = self.other_method_name(
149 column, model_object.__name__)
150 if props['type'] in ('SOMultipleJoin','SORelatedJoin'):
151 props['other_class_name'] = column.otherClassName
152 props['join_type'] = props['type'].replace('SO', '')
153 props['other_method_name'] = self.other_method_join_name(
154 column, model_object.__name__)
155
156 props['type'] = props['type'].replace('SO','')
157 return props
158
160 for col in column.otherClass.sqlmeta.columnList:
161 if type(col) == sqlobject.SOForeignKey:
162 if col.foreignKey == model_object_name:
163 return col.origName
164 return 'id'
165
167 other_class_name = column.foreignKey
168 model_object = getattr(self.get_model_name(), other_class_name)
169 for col in model_object.sqlmeta.joins:
170 if col.otherClassName == model_object_name:
171 return col.joinMethodName
172 return 'id'
173
175 """Check if the object is a direct subclass of InheritableSQLObject"""
176 return 'sqlobject.inheritance.InheritableSQLObject' in str(obj.__bases__)
177
179 columns = {}
180
181 for column_name in model_object.sqlmeta.columns:
182 column = model_object.sqlmeta.columns[column_name]
183 origname = column.origName
184 if model_object._inheritable and column_name == 'childName':
185 continue
186 columns[origname] = self.load_column(origname, column, model_object)
187
188 for column in model_object.sqlmeta.joins:
189 columns[column.joinMethodName] = self.load_column(
190 column.joinMethodName, column, model_object)
191 return columns
192
193 @expose('json')
195 model = self.get_model_name()
196 current_model = {}
197 current_model['name'] = get_package_name()
198 current_model['models'] = {}
199 current_model['ordered_models'] = []
200
201 for m in dir(model):
202 if m in ('SQLObject', 'InheritableSQLObject'):
203 continue
204 model_object = getattr(model, m)
205 if isinstance(model_object, type) and issubclass(
206 model_object, sqlobject.SQLObject):
207 parent_class = 'SQLObject'
208 if model_object._inheritable:
209 parent_class = model_object._parentClass.__name__
210 columns = self.load_columns(model_object)
211 current_model['ordered_models'].append(m)
212 current_model['models'][m] = {
213 'name': m,
214 'parent_class': parent_class,
215 'table_name': model_object.sqlmeta.table,
216 'id_name': model_object.sqlmeta.idName,
217 'columns': columns,
218 'relations': {}}
219 return dict(model=current_model)
220
222
223 if isinstance(name, unicode):
224 name = name.encode('ascii', 'replace')
225
226 name = name.translate(string.maketrans('', ''), string.punctuation)
227
228 name = ''.join([x.title() for x in name.split()])
229 sessions_directory = pkg_resources.resource_filename(__name__,
230 os.path.join('static', 'sessions'))
231 for idx in range(100):
232 postfix = idx and '_%d' % idx or ''
233 test_name = '%s%s.js' % (name, postfix)
234 full_name = os.path.join(sessions_directory, test_name)
235 if not os.path.exists(full_name):
236 return full_name
237
238 @expose('json')
248
252
265
278
279 @expose('json')
281 status_code, status = self.save_model(code)
282 if not status_code:
283 return dict(status=status)
284 tmp_name = 'tmp_model'
285
286
287 model_path = self.model_path()
288 tmp_model_path = model_path.replace('model.py','%s.py' % tmp_name)
289 try:
290 shutil.copy(model_path, tmp_model_path)
291 except IOError, e:
292 return dict(status='Failed to create a temporary model file: %s' % e)
293
294 model = self.get_model_name()
295 package = __import__(get_package_name(), {}, {}, [tmp_name])
296 tmp_model = getattr(package, tmp_name)
297
298 ok = []
299 fail = []
300 classes = order.split(',')
301 for class_name in classes:
302 try:
303 obj = getattr(tmp_model, class_name)
304 obj.createTable(ifNotExists=True)
305 setattr(model, class_name, obj)
306 ok.append('Table created for class %s' % class_name)
307 except Exception, e:
308 fail.append('Failed to create table for class %s: %s'
309 % (class_name,e))
310
311 if len(ok):
312 status += '\nTables Created: \n%s' % '\n'.join(ok)
313 if len(fail):
314 status += '\nFailed to create Tables:\n%s' % '\n'.join(fail)
315
316 try:
317 os.remove(tmp_model_path)
318 except IOError, e:
319 print "Fail to remove temporary model file: %s" % e
320
321 return dict(status=status)
322
324 sessions_directory = pkg_resources.resource_filename(
325 __name__, os.path.join('static', 'sessions'))
326 return [x.replace('.js','')
327 for x in os.listdir(sessions_directory) if x.endswith('.js')]
328
331
332 @expose('json')
334 sessions_directory = pkg_resources.resource_filename(
335 __name__, os.path.join('static', 'sessions'))
336 full_path = os.path.join(sessions_directory,'%s.js' % name)
337 if not os.path.exists(full_path):
338 return dict()
339 return dict(session=open(full_path,'r').read())
340
341 @expose('json')
346
347 @expose('json')
348 - def save(self, code):
351
352 @expose('json')
355
356 @expose('%s.modelDesigner' % baseTemplate)
362