sqlobject 3.13.1__py2.py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (149) hide show
  1. sqlobject/__init__.py +15 -0
  2. sqlobject/__version__.py +8 -0
  3. sqlobject/boundattributes.py +124 -0
  4. sqlobject/cache.py +376 -0
  5. sqlobject/classregistry.py +138 -0
  6. sqlobject/col.py +2187 -0
  7. sqlobject/compat.py +59 -0
  8. sqlobject/conftest.py +64 -0
  9. sqlobject/constraints.py +77 -0
  10. sqlobject/converters.py +278 -0
  11. sqlobject/dbconnection.py +1137 -0
  12. sqlobject/dberrors.py +54 -0
  13. sqlobject/declarative.py +211 -0
  14. sqlobject/events.py +365 -0
  15. sqlobject/firebird/__init__.py +8 -0
  16. sqlobject/firebird/firebirdconnection.py +463 -0
  17. sqlobject/include/__init__.py +1 -0
  18. sqlobject/include/hashcol.py +109 -0
  19. sqlobject/include/tests/__init__.py +1 -0
  20. sqlobject/include/tests/test_hashcol.py +84 -0
  21. sqlobject/index.py +189 -0
  22. sqlobject/inheritance/__init__.py +551 -0
  23. sqlobject/inheritance/iteration.py +106 -0
  24. sqlobject/inheritance/tests/__init__.py +1 -0
  25. sqlobject/inheritance/tests/test_aggregates.py +23 -0
  26. sqlobject/inheritance/tests/test_asdict.py +48 -0
  27. sqlobject/inheritance/tests/test_deep_inheritance.py +114 -0
  28. sqlobject/inheritance/tests/test_destroy_cascade.py +23 -0
  29. sqlobject/inheritance/tests/test_foreignKey.py +94 -0
  30. sqlobject/inheritance/tests/test_indexes.py +42 -0
  31. sqlobject/inheritance/tests/test_inheritance.py +164 -0
  32. sqlobject/inheritance/tests/test_inheritance_tree.py +50 -0
  33. sqlobject/joins.py +656 -0
  34. sqlobject/main.py +1880 -0
  35. sqlobject/manager/__init__.py +1 -0
  36. sqlobject/manager/command.py +1309 -0
  37. sqlobject/maxdb/__init__.py +8 -0
  38. sqlobject/maxdb/maxdbconnection.py +320 -0
  39. sqlobject/maxdb/readme.txt +27 -0
  40. sqlobject/mssql/__init__.py +8 -0
  41. sqlobject/mssql/mssqlconnection.py +417 -0
  42. sqlobject/mysql/__init__.py +8 -0
  43. sqlobject/mysql/mysqlconnection.py +562 -0
  44. sqlobject/postgres/__init__.py +8 -0
  45. sqlobject/postgres/pgconnection.py +619 -0
  46. sqlobject/sqlbuilder.py +1655 -0
  47. sqlobject/sqlite/__init__.py +8 -0
  48. sqlobject/sqlite/sqliteconnection.py +418 -0
  49. sqlobject/sresults.py +377 -0
  50. sqlobject/styles.py +174 -0
  51. sqlobject/sybase/__init__.py +8 -0
  52. sqlobject/sybase/sybaseconnection.py +183 -0
  53. sqlobject/tests/__init__.py +1 -0
  54. sqlobject/tests/dbtest.py +363 -0
  55. sqlobject/tests/test_ForeignKey.py +133 -0
  56. sqlobject/tests/test_ForeignKey_cascade.py +150 -0
  57. sqlobject/tests/test_NoneValuedResultItem.py +33 -0
  58. sqlobject/tests/test_SQLMultipleJoin.py +65 -0
  59. sqlobject/tests/test_SQLRelatedJoin.py +114 -0
  60. sqlobject/tests/test_SingleJoin.py +30 -0
  61. sqlobject/tests/test_aggregates.py +75 -0
  62. sqlobject/tests/test_aliases.py +49 -0
  63. sqlobject/tests/test_asdict.py +18 -0
  64. sqlobject/tests/test_auto.py +218 -0
  65. sqlobject/tests/test_basic.py +359 -0
  66. sqlobject/tests/test_blob.py +34 -0
  67. sqlobject/tests/test_boundattributes.py +67 -0
  68. sqlobject/tests/test_cache.py +57 -0
  69. sqlobject/tests/test_class_hash.py +22 -0
  70. sqlobject/tests/test_columns_order.py +18 -0
  71. sqlobject/tests/test_combining_joins.py +47 -0
  72. sqlobject/tests/test_comparison.py +24 -0
  73. sqlobject/tests/test_compat.py +10 -0
  74. sqlobject/tests/test_complex_sorting.py +77 -0
  75. sqlobject/tests/test_constraints.py +43 -0
  76. sqlobject/tests/test_converters.py +276 -0
  77. sqlobject/tests/test_create_drop.py +29 -0
  78. sqlobject/tests/test_csvexport.py +101 -0
  79. sqlobject/tests/test_csvimport.py +21 -0
  80. sqlobject/tests/test_cyclic_reference.py +55 -0
  81. sqlobject/tests/test_datetime.py +171 -0
  82. sqlobject/tests/test_dbconnection.py +17 -0
  83. sqlobject/tests/test_decimal.py +63 -0
  84. sqlobject/tests/test_declarative.py +83 -0
  85. sqlobject/tests/test_default_style.py +65 -0
  86. sqlobject/tests/test_delete.py +74 -0
  87. sqlobject/tests/test_distinct.py +36 -0
  88. sqlobject/tests/test_empty.py +22 -0
  89. sqlobject/tests/test_enum.py +75 -0
  90. sqlobject/tests/test_events.py +142 -0
  91. sqlobject/tests/test_exceptions.py +40 -0
  92. sqlobject/tests/test_expire.py +29 -0
  93. sqlobject/tests/test_groupBy.py +45 -0
  94. sqlobject/tests/test_identity.py +29 -0
  95. sqlobject/tests/test_indexes.py +100 -0
  96. sqlobject/tests/test_inheritance.py +35 -0
  97. sqlobject/tests/test_joins.py +160 -0
  98. sqlobject/tests/test_joins_conditional.py +143 -0
  99. sqlobject/tests/test_jsonbcol.py +53 -0
  100. sqlobject/tests/test_jsoncol.py +56 -0
  101. sqlobject/tests/test_lazy.py +165 -0
  102. sqlobject/tests/test_md5.py +13 -0
  103. sqlobject/tests/test_mysql.py +50 -0
  104. sqlobject/tests/test_new_joins.py +165 -0
  105. sqlobject/tests/test_parse_uri.py +147 -0
  106. sqlobject/tests/test_paste.py +92 -0
  107. sqlobject/tests/test_perConnection.py +20 -0
  108. sqlobject/tests/test_pickle.py +42 -0
  109. sqlobject/tests/test_picklecol.py +44 -0
  110. sqlobject/tests/test_postgres.py +82 -0
  111. sqlobject/tests/test_reparent_sqlmeta.py +33 -0
  112. sqlobject/tests/test_schema.py +30 -0
  113. sqlobject/tests/test_select.py +218 -0
  114. sqlobject/tests/test_select_through.py +63 -0
  115. sqlobject/tests/test_setters.py +23 -0
  116. sqlobject/tests/test_slice.py +59 -0
  117. sqlobject/tests/test_sorting.py +80 -0
  118. sqlobject/tests/test_sqlbuilder.py +134 -0
  119. sqlobject/tests/test_sqlbuilder_dbspecific.py +66 -0
  120. sqlobject/tests/test_sqlbuilder_importproxy.py +55 -0
  121. sqlobject/tests/test_sqlbuilder_joins_instances.py +90 -0
  122. sqlobject/tests/test_sqlite.py +160 -0
  123. sqlobject/tests/test_sqlmeta_idName.py +52 -0
  124. sqlobject/tests/test_sqlobject_admin.py +38 -0
  125. sqlobject/tests/test_string_id.py +79 -0
  126. sqlobject/tests/test_style.py +36 -0
  127. sqlobject/tests/test_subqueries.py +142 -0
  128. sqlobject/tests/test_transactions.py +122 -0
  129. sqlobject/tests/test_unicode.py +84 -0
  130. sqlobject/tests/test_uuidcol.py +40 -0
  131. sqlobject/tests/test_validation.py +125 -0
  132. sqlobject/tests/test_views.py +159 -0
  133. sqlobject/util/__init__.py +1 -0
  134. sqlobject/util/csvexport.py +210 -0
  135. sqlobject/util/csvimport.py +366 -0
  136. sqlobject/util/moduleloader.py +35 -0
  137. sqlobject/util/threadinglocal.py +6 -0
  138. sqlobject/versioning/__init__.py +120 -0
  139. sqlobject/versioning/test/__init__.py +0 -0
  140. sqlobject/versioning/test/test_version.py +183 -0
  141. sqlobject/views.py +151 -0
  142. sqlobject/wsgi_middleware.py +109 -0
  143. sqlobject-3.13.1.data/scripts/sqlobject-admin +36 -0
  144. sqlobject-3.13.1.data/scripts/sqlobject-convertOldURI +19 -0
  145. sqlobject-3.13.1.dist-info/METADATA +177 -0
  146. sqlobject-3.13.1.dist-info/RECORD +149 -0
  147. sqlobject-3.13.1.dist-info/WHEEL +6 -0
  148. sqlobject-3.13.1.dist-info/entry_points.txt +4 -0
  149. sqlobject-3.13.1.dist-info/top_level.txt +1 -0
sqlobject/joins.py ADDED
@@ -0,0 +1,656 @@
1
+ from itertools import count
2
+ from . import boundattributes
3
+ from . import classregistry
4
+ from . import events
5
+ from . import sresults
6
+ from . import styles
7
+ from . import sqlbuilder
8
+ from .styles import capword
9
+
10
+ __all__ = ['MultipleJoin', 'SQLMultipleJoin', 'RelatedJoin', 'SQLRelatedJoin',
11
+ 'SingleJoin', 'ManyToMany', 'OneToMany']
12
+
13
+ creationOrder = count()
14
+ NoDefault = sqlbuilder.NoDefault
15
+
16
+
17
+ def getID(obj):
18
+ try:
19
+ return obj.id
20
+ except AttributeError:
21
+ return int(obj)
22
+
23
+
24
+ class Join(object):
25
+
26
+ def __init__(self, otherClass=None, **kw):
27
+ kw['otherClass'] = otherClass
28
+ self.kw = kw
29
+ self._joinMethodName = self.kw.pop('joinMethodName', None)
30
+ self.creationOrder = next(creationOrder)
31
+
32
+ def _set_joinMethodName(self, value):
33
+ assert self._joinMethodName == value or self._joinMethodName is None, \
34
+ "You have already given an explicit joinMethodName (%s), " \
35
+ "and you are now setting it to %s" % (self._joinMethodName, value)
36
+ self._joinMethodName = value
37
+
38
+ def _get_joinMethodName(self):
39
+ return self._joinMethodName
40
+
41
+ joinMethodName = property(_get_joinMethodName, _set_joinMethodName)
42
+ name = joinMethodName
43
+
44
+ def withClass(self, soClass):
45
+ if 'joinMethodName' in self.kw:
46
+ self._joinMethodName = self.kw['joinMethodName']
47
+ del self.kw['joinMethodName']
48
+ return self.baseClass(creationOrder=self.creationOrder,
49
+ soClass=soClass,
50
+ joinDef=self,
51
+ joinMethodName=self._joinMethodName,
52
+ **self.kw)
53
+
54
+
55
+ # A join is separate from a foreign key, i.e., it is
56
+ # many-to-many, or one-to-many where the *other* class
57
+ # has the foreign key.
58
+
59
+
60
+ class SOJoin(object):
61
+
62
+ def __init__(self,
63
+ creationOrder,
64
+ soClass=None,
65
+ otherClass=None,
66
+ joinColumn=None,
67
+ joinMethodName=None,
68
+ orderBy=NoDefault,
69
+ joinDef=None):
70
+ self.creationOrder = creationOrder
71
+ self.soClass = soClass
72
+ self.joinDef = joinDef
73
+ self.otherClassName = otherClass
74
+ classregistry.registry(soClass.sqlmeta.registry).addClassCallback(
75
+ otherClass, self._setOtherClass)
76
+ self.joinColumn = joinColumn
77
+ self.joinMethodName = joinMethodName
78
+ self._orderBy = orderBy
79
+ if not self.joinColumn:
80
+ # Here we set up the basic join, which is
81
+ # one-to-many, where the other class points to
82
+ # us.
83
+ self.joinColumn = styles.getStyle(
84
+ self.soClass).tableReference(self.soClass.sqlmeta.table)
85
+
86
+ def orderBy(self):
87
+ if self._orderBy is NoDefault:
88
+ self._orderBy = self.otherClass.sqlmeta.defaultOrder
89
+ return self._orderBy
90
+ orderBy = property(orderBy)
91
+
92
+ def _setOtherClass(self, cls):
93
+ self.otherClass = cls
94
+
95
+ def hasIntermediateTable(self):
96
+ return False
97
+
98
+ def _applyOrderBy(self, results, defaultSortClass):
99
+ if self.orderBy is not None:
100
+ doSort(results, self.orderBy)
101
+ return results
102
+
103
+
104
+ class MinType(object):
105
+ """Sort less than everything, for handling None's in the results"""
106
+ # functools.total_ordering would simplify this
107
+
108
+ def __lt__(self, other):
109
+ if self is other:
110
+ return False
111
+ return True
112
+
113
+ def __eq__(self, other):
114
+ return self is other
115
+
116
+ def __gt__(self, other):
117
+ return False
118
+
119
+ def __le__(self, other):
120
+ return True
121
+
122
+ def __ge__(self, other):
123
+ if self is other:
124
+ return True
125
+ return False
126
+
127
+
128
+ Min = MinType()
129
+
130
+
131
+ def doSort(results, orderBy):
132
+ if isinstance(orderBy, (tuple, list)):
133
+ if len(orderBy) == 1:
134
+ orderBy = orderBy[0]
135
+ else:
136
+ # Rely on stable sort results, since this is simpler
137
+ # than trying to munge everything into a single sort key
138
+ doSort(results, orderBy[0])
139
+ doSort(results, orderBy[1:])
140
+ return
141
+ if isinstance(orderBy, sqlbuilder.DESC) \
142
+ and isinstance(orderBy.expr, sqlbuilder.SQLObjectField):
143
+ orderBy = '-' + orderBy.expr.original
144
+ elif isinstance(orderBy, sqlbuilder.SQLObjectField):
145
+ orderBy = orderBy.original
146
+ # @@: but we don't handle more complex expressions for orderings
147
+ if orderBy.startswith('-'):
148
+ orderBy = orderBy[1:]
149
+ reverse = True
150
+ else:
151
+ reverse = False
152
+
153
+ def sortkey(x, attr=orderBy):
154
+ a = getattr(x, attr)
155
+ if a is None:
156
+ return Min
157
+ return a
158
+ results.sort(key=sortkey, reverse=reverse)
159
+
160
+
161
+ # This is a one-to-many
162
+
163
+
164
+ class SOMultipleJoin(SOJoin):
165
+
166
+ def __init__(self, addRemoveName=None, **kw):
167
+ # addRemovePrefix is something like @@
168
+ SOJoin.__init__(self, **kw)
169
+
170
+ # Here we generate the method names
171
+ if not self.joinMethodName:
172
+ name = self.otherClassName[0].lower() + self.otherClassName[1:]
173
+ if name.endswith('s'):
174
+ name += "es"
175
+ else:
176
+ name += "s"
177
+ self.joinMethodName = name
178
+ if addRemoveName:
179
+ self.addRemoveName = addRemoveName
180
+ else:
181
+ self.addRemoveName = capword(self.otherClassName)
182
+
183
+ def performJoin(self, inst):
184
+ ids = inst._connection._SO_selectJoin(
185
+ self.otherClass,
186
+ self.joinColumn,
187
+ inst.id)
188
+ if inst.sqlmeta._perConnection:
189
+ conn = inst._connection
190
+ else:
191
+ conn = None
192
+ return self._applyOrderBy(
193
+ [self.otherClass.get(id, conn) for (id,) in ids if id is not None],
194
+ self.otherClass)
195
+
196
+ def _dbNameToPythonName(self):
197
+ for column in self.otherClass.sqlmeta.columns.values():
198
+ if column.dbName == self.joinColumn:
199
+ return column.name
200
+ return self.soClass.sqlmeta.style.dbColumnToPythonAttr(self.joinColumn)
201
+
202
+
203
+ class MultipleJoin(Join):
204
+ baseClass = SOMultipleJoin
205
+
206
+
207
+ class SOSQLMultipleJoin(SOMultipleJoin):
208
+
209
+ def performJoin(self, inst):
210
+ if inst.sqlmeta._perConnection:
211
+ conn = inst._connection
212
+ else:
213
+ conn = None
214
+ pythonColumn = self._dbNameToPythonName()
215
+ results = self.otherClass.select(
216
+ getattr(self.otherClass.q, pythonColumn) == inst.id,
217
+ connection=conn)
218
+ return results.orderBy(self.orderBy)
219
+
220
+
221
+ class SQLMultipleJoin(Join):
222
+ baseClass = SOSQLMultipleJoin
223
+
224
+
225
+ # This is a many-to-many join, with an intermediary table
226
+
227
+
228
+ class SORelatedJoin(SOMultipleJoin):
229
+
230
+ def __init__(self,
231
+ otherColumn=None,
232
+ intermediateTable=None,
233
+ createRelatedTable=True,
234
+ **kw):
235
+ self.intermediateTable = intermediateTable
236
+ self.otherColumn = otherColumn
237
+ self.createRelatedTable = createRelatedTable
238
+ SOMultipleJoin.__init__(self, **kw)
239
+ classregistry.registry(
240
+ self.soClass.sqlmeta.registry).addClassCallback(
241
+ self.otherClassName, self._setOtherRelatedClass)
242
+
243
+ def _setOtherRelatedClass(self, otherClass):
244
+ if not self.intermediateTable:
245
+ names = [self.soClass.sqlmeta.table,
246
+ otherClass.sqlmeta.table]
247
+ names.sort()
248
+ self.intermediateTable = '%s_%s' % (names[0], names[1])
249
+ if not self.otherColumn:
250
+ self.otherColumn = self.soClass.sqlmeta.style.tableReference(
251
+ otherClass.sqlmeta.table)
252
+
253
+ def hasIntermediateTable(self):
254
+ return True
255
+
256
+ def performJoin(self, inst):
257
+ ids = inst._connection._SO_intermediateJoin(
258
+ self.intermediateTable,
259
+ self.otherColumn,
260
+ self.joinColumn,
261
+ inst.id)
262
+ if inst.sqlmeta._perConnection:
263
+ conn = inst._connection
264
+ else:
265
+ conn = None
266
+ return self._applyOrderBy(
267
+ [self.otherClass.get(id, conn) for (id,) in ids if id is not None],
268
+ self.otherClass)
269
+
270
+ def remove(self, inst, other):
271
+ inst._connection._SO_intermediateDelete(
272
+ self.intermediateTable,
273
+ self.joinColumn,
274
+ getID(inst),
275
+ self.otherColumn,
276
+ getID(other))
277
+
278
+ def add(self, inst, other):
279
+ inst._connection._SO_intermediateInsert(
280
+ self.intermediateTable,
281
+ self.joinColumn,
282
+ getID(inst),
283
+ self.otherColumn,
284
+ getID(other))
285
+
286
+
287
+ class RelatedJoin(MultipleJoin):
288
+ baseClass = SORelatedJoin
289
+
290
+
291
+ # helper classes to SQLRelatedJoin
292
+
293
+
294
+ class OtherTableToJoin(sqlbuilder.SQLExpression):
295
+ def __init__(self, otherTable, otherIdName, interTable, joinColumn, alias):
296
+ self.otherTable = otherTable
297
+ self.otherIdName = otherIdName
298
+ self.interTable = interTable
299
+ self.joinColumn = joinColumn
300
+ self.alias = alias
301
+
302
+ def tablesUsedImmediate(self):
303
+ return [
304
+ '%s %s' % (self.otherTable, self.alias)
305
+ if self.alias else self.otherTable,
306
+ self.interTable,
307
+ ]
308
+
309
+ def __sqlrepr__(self, db):
310
+ return '%s.%s = %s.%s' % (
311
+ self.alias if self.alias else self.otherTable,
312
+ self.otherIdName, self.interTable, self.joinColumn)
313
+
314
+
315
+ class JoinToTable(sqlbuilder.SQLExpression):
316
+ def __init__(self, table, idName, interTable, joinColumn, alias):
317
+ self.table = table
318
+ self.idName = idName
319
+ self.interTable = interTable
320
+ self.joinColumn = joinColumn
321
+ self.alias = alias
322
+
323
+ def tablesUsedImmediate(self):
324
+ return [
325
+ '%s %s' % (self.table, self.alias)
326
+ if self.alias else self.table,
327
+ self.interTable,
328
+ ]
329
+
330
+ def __sqlrepr__(self, db):
331
+ return '%s.%s = %s.%s' % (
332
+ self.interTable, self.joinColumn,
333
+ self.alias if self.alias else self.table, self.idName)
334
+
335
+
336
+ class TableToId(sqlbuilder.SQLExpression):
337
+ def __init__(self, table, idName, idValue, alias):
338
+ self.table = table
339
+ self.idName = idName
340
+ self.idValue = idValue
341
+ self.alias = alias
342
+
343
+ def tablesUsedImmediate(self):
344
+ return [
345
+ '%s %s' % (self.table, self.alias)
346
+ if self.alias else self.table,
347
+ ]
348
+
349
+ def __sqlrepr__(self, db):
350
+ return '%s.%s = %s' % (
351
+ self.alias if self.alias else self.table,
352
+ self.idName, self.idValue)
353
+
354
+
355
+ class SQLJoinSelectResults(sresults.SelectResults):
356
+ def filter(self, filter_clause):
357
+ clause_tables = filter_clause.tablesUsed(None)
358
+ if self._SOSQLRelatedJoin_realSourceClass.sqlmeta.table \
359
+ in clause_tables:
360
+ tableClass = self._SOSQLRelatedJoin_realSourceClass.__name__
361
+ raise ValueError(
362
+ "Using table '%s' in the filter expression without an alias "
363
+ "could produce wrong SQL. Most probably you need "
364
+ "Alias(%s, '_SO_SQLRelatedJoin_OtherTable') instead."
365
+ % (tableClass, tableClass)
366
+ )
367
+ return sresults.SelectResults.filter(self, filter_clause)
368
+
369
+
370
+ class SOSQLRelatedJoin(SORelatedJoin):
371
+ def performJoin(self, inst):
372
+ if inst.sqlmeta._perConnection:
373
+ conn = inst._connection
374
+ else:
375
+ conn = None
376
+ needAlias = self.soClass is self.otherClass
377
+ if needAlias:
378
+ source = sqlbuilder.Alias(
379
+ self.otherClass, '_SO_SQLRelatedJoin_OtherTable')
380
+ sresultsClass = SQLJoinSelectResults
381
+ else:
382
+ source = self.otherClass
383
+ sresultsClass = self.otherClass.SelectResultsClass
384
+ results = sresultsClass(
385
+ source,
386
+ sqlbuilder.AND(
387
+ OtherTableToJoin(
388
+ self.otherClass.sqlmeta.table,
389
+ self.otherClass.sqlmeta.idName,
390
+ self.intermediateTable, self.otherColumn,
391
+ '_SO_SQLRelatedJoin_OtherTable' if needAlias else '',
392
+ ),
393
+ JoinToTable(
394
+ self.soClass.sqlmeta.table, self.soClass.sqlmeta.idName,
395
+ self.intermediateTable, self.joinColumn,
396
+ '_SO_SQLRelatedJoin_ThisTable' if needAlias else '',
397
+ ),
398
+ TableToId(
399
+ self.soClass.sqlmeta.table, self.soClass.sqlmeta.idName,
400
+ inst.id,
401
+ '_SO_SQLRelatedJoin_ThisTable' if needAlias else '',
402
+ ),
403
+ ),
404
+ clauseTables=(
405
+ '%s _SO_SQLRelatedJoin_ThisTable' % self.soClass.sqlmeta.table
406
+ if needAlias else self.soClass.sqlmeta.table,
407
+ '%s _SO_SQLRelatedJoin_OtherTable' %
408
+ self.otherClass.sqlmeta.table
409
+ if needAlias else self.otherClass.sqlmeta.table,
410
+ self.intermediateTable,
411
+ ),
412
+ connection=conn,
413
+ orderBy=self.orderBy,
414
+ )
415
+ results._SOSQLRelatedJoin_realSourceClass = self.otherClass
416
+ return results
417
+
418
+
419
+ class SQLRelatedJoin(RelatedJoin):
420
+ baseClass = SOSQLRelatedJoin
421
+
422
+
423
+ class SOSingleJoin(SOMultipleJoin):
424
+
425
+ def __init__(self, **kw):
426
+ self.makeDefault = kw.pop('makeDefault', False)
427
+ SOMultipleJoin.__init__(self, **kw)
428
+
429
+ def performJoin(self, inst):
430
+ if inst.sqlmeta._perConnection:
431
+ conn = inst._connection
432
+ else:
433
+ conn = None
434
+ pythonColumn = self._dbNameToPythonName()
435
+ results = self.otherClass.select(
436
+ getattr(self.otherClass.q, pythonColumn) == inst.id,
437
+ connection=conn
438
+ )
439
+ if results.count() == 0:
440
+ if not self.makeDefault:
441
+ return None
442
+ else:
443
+ kw = {self.soClass.sqlmeta.style.
444
+ instanceIDAttrToAttr(pythonColumn): inst}
445
+ # instanciating the otherClass with all
446
+ return self.otherClass(**kw)
447
+ else:
448
+ return results[0]
449
+
450
+
451
+ class SingleJoin(Join):
452
+ baseClass = SOSingleJoin
453
+
454
+
455
+ class SOManyToMany(object):
456
+
457
+ def __init__(self, soClass, name, join,
458
+ intermediateTable, joinColumn, otherColumn,
459
+ createJoinTable, **attrs):
460
+ self.name = name
461
+ self.intermediateTable = intermediateTable
462
+ self.joinColumn = joinColumn
463
+ self.otherColumn = otherColumn
464
+ self.createJoinTable = createJoinTable
465
+ self.soClass = self.otherClass = None
466
+ for name, value in attrs.items():
467
+ setattr(self, name, value)
468
+ classregistry.registry(
469
+ soClass.sqlmeta.registry).addClassCallback(
470
+ join, self._setOtherClass)
471
+ classregistry.registry(
472
+ soClass.sqlmeta.registry).addClassCallback(
473
+ soClass.__name__, self._setThisClass)
474
+
475
+ def _setThisClass(self, soClass):
476
+ self.soClass = soClass
477
+ if self.soClass and self.otherClass:
478
+ self._finishSet()
479
+
480
+ def _setOtherClass(self, otherClass):
481
+ self.otherClass = otherClass
482
+ if self.soClass and self.otherClass:
483
+ self._finishSet()
484
+
485
+ def _finishSet(self):
486
+ if self.intermediateTable is None:
487
+ names = [self.soClass.sqlmeta.table,
488
+ self.otherClass.sqlmeta.table]
489
+ names.sort()
490
+ self.intermediateTable = '%s_%s' % (names[0], names[1])
491
+ if not self.otherColumn:
492
+ self.otherColumn = self.soClass.sqlmeta.style.tableReference(
493
+ self.otherClass.sqlmeta.table)
494
+ if not self.joinColumn:
495
+ self.joinColumn = styles.getStyle(
496
+ self.soClass).tableReference(self.soClass.sqlmeta.table)
497
+ events.listen(self.event_CreateTableSignal,
498
+ self.soClass, events.CreateTableSignal)
499
+ events.listen(self.event_CreateTableSignal,
500
+ self.otherClass, events.CreateTableSignal)
501
+ self.clause = (
502
+ (self.otherClass.q.id
503
+ == sqlbuilder.Field(self.intermediateTable, self.otherColumn))
504
+ & (sqlbuilder.Field(self.intermediateTable, self.joinColumn)
505
+ == self.soClass.q.id))
506
+
507
+ def __get__(self, obj, type):
508
+ if obj is None:
509
+ return self
510
+ query = (
511
+ (self.otherClass.q.id
512
+ == sqlbuilder.Field(self.intermediateTable, self.otherColumn))
513
+ & (sqlbuilder.Field(self.intermediateTable, self.joinColumn)
514
+ == obj.id))
515
+ select = self.otherClass.select(query)
516
+ return _ManyToManySelectWrapper(obj, self, select)
517
+
518
+ def event_CreateTableSignal(self, soClass, connection, extra_sql,
519
+ post_funcs):
520
+ if self.createJoinTable:
521
+ post_funcs.append(self.event_CreateTableSignalPost)
522
+
523
+ def event_CreateTableSignalPost(self, soClass, connection):
524
+ if connection.tableExists(self.intermediateTable):
525
+ return
526
+ connection._SO_createJoinTable(self)
527
+
528
+
529
+ class ManyToMany(boundattributes.BoundFactory):
530
+ factory_class = SOManyToMany
531
+ __restrict_attributes__ = (
532
+ 'join', 'intermediateTable',
533
+ 'joinColumn', 'otherColumn', 'createJoinTable')
534
+ __unpackargs__ = ('join',)
535
+
536
+ # Default values:
537
+ intermediateTable = None
538
+ joinColumn = None
539
+ otherColumn = None
540
+ createJoinTable = True
541
+
542
+
543
+ class _ManyToManySelectWrapper(object):
544
+
545
+ def __init__(self, forObject, join, select):
546
+ self.forObject = forObject
547
+ self.join = join
548
+ self.select = select
549
+
550
+ def __getattr__(self, attr):
551
+ # @@: This passes through private variable access too... should it?
552
+ # Also magic methods, like __str__
553
+ return getattr(self.select, attr)
554
+
555
+ def __repr__(self):
556
+ return '<%s for: %s>' % (self.__class__.__name__, repr(self.select))
557
+
558
+ def __str__(self):
559
+ return str(self.select)
560
+
561
+ def __iter__(self):
562
+ return iter(self.select)
563
+
564
+ def __getitem__(self, key):
565
+ return self.select[key]
566
+
567
+ def add(self, obj):
568
+ obj._connection._SO_intermediateInsert(
569
+ self.join.intermediateTable,
570
+ self.join.joinColumn,
571
+ getID(self.forObject),
572
+ self.join.otherColumn,
573
+ getID(obj))
574
+
575
+ def remove(self, obj):
576
+ obj._connection._SO_intermediateDelete(
577
+ self.join.intermediateTable,
578
+ self.join.joinColumn,
579
+ getID(self.forObject),
580
+ self.join.otherColumn,
581
+ getID(obj))
582
+
583
+ def create(self, **kw):
584
+ obj = self.join.otherClass(**kw)
585
+ self.add(obj)
586
+ return obj
587
+
588
+
589
+ class SOOneToMany(object):
590
+
591
+ def __init__(self, soClass, name, join, joinColumn, **attrs):
592
+ self.soClass = soClass
593
+ self.name = name
594
+ self.joinColumn = joinColumn
595
+ for name, value in attrs.items():
596
+ setattr(self, name, value)
597
+ classregistry.registry(
598
+ soClass.sqlmeta.registry).addClassCallback(
599
+ join, self._setOtherClass)
600
+
601
+ def _setOtherClass(self, otherClass):
602
+ self.otherClass = otherClass
603
+ if not self.joinColumn:
604
+ self.joinColumn = styles.getStyle(
605
+ self.soClass).tableReference(self.soClass.sqlmeta.table)
606
+ self.clause = (
607
+ sqlbuilder.Field(self.otherClass.sqlmeta.table, self.joinColumn)
608
+ == self.soClass.q.id)
609
+
610
+ def __get__(self, obj, type):
611
+ if obj is None:
612
+ return self
613
+ query = (
614
+ sqlbuilder.Field(self.otherClass.sqlmeta.table, self.joinColumn)
615
+ == obj.id)
616
+ select = self.otherClass.select(query)
617
+ return _OneToManySelectWrapper(obj, self, select)
618
+
619
+
620
+ class OneToMany(boundattributes.BoundFactory):
621
+ factory_class = SOOneToMany
622
+ __restrict_attributes__ = (
623
+ 'join', 'joinColumn')
624
+ __unpackargs__ = ('join',)
625
+
626
+ # Default values:
627
+ joinColumn = None
628
+
629
+
630
+ class _OneToManySelectWrapper(object):
631
+
632
+ def __init__(self, forObject, join, select):
633
+ self.forObject = forObject
634
+ self.join = join
635
+ self.select = select
636
+
637
+ def __getattr__(self, attr):
638
+ # @@: This passes through private variable access too... should it?
639
+ # Also magic methods, like __str__
640
+ return getattr(self.select, attr)
641
+
642
+ def __repr__(self):
643
+ return '<%s for: %s>' % (self.__class__.__name__, repr(self.select))
644
+
645
+ def __str__(self):
646
+ return str(self.select)
647
+
648
+ def __iter__(self):
649
+ return iter(self.select)
650
+
651
+ def __getitem__(self, key):
652
+ return self.select[key]
653
+
654
+ def create(self, **kw):
655
+ kw[self.join.joinColumn] = self.forObject.id
656
+ return self.join.otherClass(**kw)