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
@@ -0,0 +1,183 @@
1
+ from sqlobject.dbconnection import DBAPI
2
+ from sqlobject import col
3
+
4
+
5
+ class SybaseConnection(DBAPI):
6
+
7
+ supportTransactions = False
8
+ dbName = 'sybase'
9
+ schemes = [dbName]
10
+ NumericType = None
11
+
12
+ def __init__(self, db, user, password='', host='localhost', port=None,
13
+ locking=1, **kw):
14
+ db = db.strip('/')
15
+ import Sybase
16
+ Sybase._ctx.debug = 0
17
+ if SybaseConnection.NumericType is None:
18
+ from Sybase import NumericType
19
+ SybaseConnection.NumericType = NumericType
20
+ from sqlobject.converters import registerConverter, IntConverter
21
+ registerConverter(NumericType, IntConverter)
22
+ self.module = Sybase
23
+ self.locking = int(locking)
24
+ self.host = host
25
+ self.port = port
26
+ self.db = db
27
+ self.user = user
28
+ self.password = password
29
+ autoCommit = kw.get('autoCommit')
30
+ if autoCommit:
31
+ autoCommit = int(autoCommit)
32
+ else:
33
+ autoCommit = None
34
+ kw['autoCommit'] = autoCommit
35
+ DBAPI.__init__(self, **kw)
36
+
37
+ @classmethod
38
+ def _connectionFromParams(cls, user, password, host, port, path, args):
39
+ return cls(user=user, password=password,
40
+ host=host or 'localhost', port=port, db=path, **args)
41
+
42
+ def insert_id(self, conn):
43
+ """
44
+ Sybase adapter/cursor does not support the
45
+ insert_id method.
46
+ """
47
+ c = conn.cursor()
48
+ c.execute('SELECT @@IDENTITY')
49
+ result = c.fetchone()[0]
50
+ c.close()
51
+ return result
52
+
53
+ def makeConnection(self):
54
+ return self.module.connect(self.host, self.user, self.password,
55
+ database=self.db,
56
+ auto_commit=self.autoCommit,
57
+ locking=self.locking)
58
+
59
+ HAS_IDENTITY = """
60
+ SELECT col.name, col.status, obj.name
61
+ FROM syscolumns col
62
+ JOIN sysobjects obj
63
+ ON obj.id = col.id
64
+ WHERE obj.name = '%s'
65
+ AND (col.status & 0x80) = 0x80
66
+ """
67
+
68
+ def _hasIdentity(self, conn, table):
69
+ query = self.HAS_IDENTITY % table
70
+ c = conn.cursor()
71
+ c.execute(query)
72
+ r = c.fetchone()
73
+ c.close()
74
+ return r is not None
75
+
76
+ def _queryInsertID(self, conn, soInstance, id, names, values):
77
+ table = soInstance.sqlmeta.table
78
+ idName = soInstance.sqlmeta.idName
79
+ c = conn.cursor()
80
+ if id is not None:
81
+ names = [idName] + names
82
+ values = [id] + values
83
+
84
+ has_identity = self._hasIdentity(conn, table)
85
+ identity_insert_on = False
86
+ if has_identity and (id is not None):
87
+ identity_insert_on = True
88
+ c.execute('SET IDENTITY_INSERT %s ON' % table)
89
+
90
+ if names and values:
91
+ q = self._insertSQL(table, names, values)
92
+ else:
93
+ q = "INSERT INTO %s DEFAULT VALUES" % table
94
+ if self.debug:
95
+ self.printDebug(conn, q, 'QueryIns')
96
+ c.execute(q)
97
+ if has_identity and identity_insert_on:
98
+ c.execute('SET IDENTITY_INSERT %s OFF' % table)
99
+ c.close()
100
+ if id is None:
101
+ id = self.insert_id(conn)
102
+ if self.debugOutput:
103
+ self.printDebug(conn, id, 'QueryIns', 'result')
104
+ return id
105
+
106
+ @classmethod
107
+ def _queryAddLimitOffset(cls, query, start, end):
108
+ # XXX Sybase doesn't support OFFSET
109
+ if end:
110
+ return "SET ROWCOUNT %i %s SET ROWCOUNT 0" % (end, query)
111
+ return query
112
+
113
+ def createReferenceConstraint(self, soClass, col):
114
+ return None
115
+
116
+ def createColumn(self, soClass, col):
117
+ return col.sybaseCreateSQL()
118
+
119
+ def createIDColumn(self, soClass):
120
+ key_type = {int: "NUMERIC(18,0)", str: "TEXT"}[soClass.sqlmeta.idType]
121
+ return '%s %s IDENTITY UNIQUE' % (soClass.sqlmeta.idName, key_type)
122
+
123
+ def createIndexSQL(self, soClass, index):
124
+ return index.sybaseCreateIndexSQL(soClass)
125
+
126
+ def joinSQLType(self, join):
127
+ return 'NUMERIC(18,0) NOT NULL'
128
+
129
+ SHOW_TABLES = "SELECT name FROM sysobjects WHERE type='U'"
130
+
131
+ def tableExists(self, tableName):
132
+ for (table,) in self.queryAll(self.SHOW_TABLES):
133
+ if table.lower() == tableName.lower():
134
+ return True
135
+ return False
136
+
137
+ def addColumn(self, tableName, column):
138
+ self.query('ALTER TABLE %s ADD COLUMN %s' %
139
+ (tableName,
140
+ column.sybaseCreateSQL()))
141
+
142
+ def delColumn(self, sqlmeta, column):
143
+ self.query(
144
+ 'ALTER TABLE %s DROP COLUMN %s' % (sqlmeta.table, column.dbName))
145
+
146
+ SHOW_COLUMNS = ('SELECT '
147
+ 'COLUMN_NAME, DATA_TYPE, IS_NULLABLE, COLUMN_DEFAULT '
148
+ 'FROM INFORMATION_SCHEMA.COLUMNS '
149
+ 'WHERE TABLE_NAME = \'%s\'')
150
+
151
+ def columnsFromSchema(self, tableName, soClass):
152
+ colData = self.queryAll(self.SHOW_COLUMNS
153
+ % tableName)
154
+ results = []
155
+ for field, t, nullAllowed, default in colData:
156
+ if field == soClass.sqlmeta.idName:
157
+ continue
158
+ colClass, kw = self.guessClass(t)
159
+ kw['name'] = soClass.sqlmeta.style.dbColumnToPythonAttr(field)
160
+ kw['dbName'] = field
161
+ kw['notNone'] = not nullAllowed
162
+ kw['default'] = default
163
+ # @@ skip key...
164
+ # @@ skip extra...
165
+ kw['forceDBName'] = True
166
+ results.append(colClass(**kw))
167
+ return results
168
+
169
+ def _setAutoCommit(self, conn, auto):
170
+ conn.auto_commit = auto
171
+
172
+ def guessClass(self, t):
173
+ if t.startswith('int'):
174
+ return col.IntCol, {}
175
+ elif t.startswith('varchar'):
176
+ return col.StringCol, {'length': int(t[8:-1])}
177
+ elif t.startswith('char'):
178
+ return col.StringCol, {'length': int(t[5:-1]),
179
+ 'varchar': False}
180
+ elif t.startswith('datetime'):
181
+ return col.DateTimeCol, {}
182
+ else:
183
+ return col.Col, {}
@@ -0,0 +1 @@
1
+ #
@@ -0,0 +1,363 @@
1
+ """
2
+ The framework for making database tests.
3
+ """
4
+ from __future__ import print_function
5
+
6
+ import logging
7
+ import os
8
+ import sys
9
+ from pytest import raises, skip
10
+ import sqlobject
11
+ from sqlobject.col import use_microseconds
12
+ import sqlobject.conftest as conftest
13
+
14
+ if sys.platform[:3] == "win":
15
+ def getcwd():
16
+ return os.getcwd().replace('\\', '/')
17
+ else:
18
+ getcwd = os.getcwd
19
+
20
+ """
21
+ supportsMatrix defines what database backends support what features.
22
+ Each feature has a name, if you see a key like '+featureName' then
23
+ only the databases listed support the feature. Conversely,
24
+ '-featureName' means all databases *except* the ones listed support
25
+ the feature. The databases are given by their SQLObject string name,
26
+ separated by spaces.
27
+
28
+ The function supports(featureName) returns True or False based on this,
29
+ and you can use it like::
30
+
31
+ def test_featureX():
32
+ if not supports('featureX'):
33
+ pytest.skip("Doesn't support featureX")
34
+ """
35
+ supportsMatrix = {
36
+ '-blobData': 'mssql',
37
+ '-decimalColumn': 'mssql',
38
+ '-dropTableCascade': 'sybase mssql mysql',
39
+ '-emptyTable': 'mssql',
40
+ '+exceptions': 'mysql postgres sqlite',
41
+ '-expressionIndex': 'mysql sqlite firebird mssql',
42
+ '-limitSelect': 'mssql',
43
+ '+memorydb': 'sqlite',
44
+ '+rlike': 'mysql postgres sqlite',
45
+ '+schema': 'postgres',
46
+ '-transactions': ' ',
47
+ }
48
+
49
+
50
+ def setupClass(soClasses, force=False):
51
+ """
52
+ Makes sure the classes have a corresponding and correct table.
53
+ This won't recreate the table if it already exists. It will check
54
+ that the table is properly defined (in case you change your table
55
+ definition).
56
+
57
+ You can provide a single class or a list of classes; if a list
58
+ then classes will be created in the order you provide, and
59
+ destroyed in the opposite order. So if class A depends on class
60
+ B, then do setupClass([B, A]) and B won't be destroyed or cleared
61
+ until after A is destroyed or cleared.
62
+
63
+ If force is true, then the database will be recreated no matter
64
+ what.
65
+ """
66
+ # global hub
67
+ if not isinstance(soClasses, (list, tuple)):
68
+ soClasses = [soClasses]
69
+ connection = getConnection()
70
+ for soClass in soClasses:
71
+ # This would be an alternate way to register connections:
72
+ # try:
73
+ # hub
74
+ # except NameError:
75
+ # hub = sqlobject.dbconnection.ConnectionHub()
76
+ # soClass._connection = hub
77
+ # hub.threadConnection = connection
78
+ # hub.processConnection = connection
79
+ soClass._connection = connection
80
+ installOrClear(soClasses, force=force)
81
+ return soClasses
82
+
83
+
84
+ def speedupSQLiteConnection(connection):
85
+ connection.query("PRAGMA synchronous=OFF")
86
+ connection.query("PRAGMA count_changes=OFF")
87
+ connection.query("PRAGMA journal_mode=MEMORY")
88
+ connection.query("PRAGMA temp_store=MEMORY")
89
+
90
+ installedDBFilename = os.path.join(getcwd(), 'dbs_data.tmp')
91
+
92
+ installedDBTracker = sqlobject.connectionForURI(
93
+ 'sqlite:///' + installedDBFilename)
94
+ speedupSQLiteConnection(installedDBTracker)
95
+
96
+
97
+ def getConnection(**kw):
98
+ name = getConnectionURI()
99
+ conn = sqlobject.connectionForURI(name, **kw)
100
+ if conftest.option.show_sql:
101
+ conn.debug = True
102
+ if conftest.option.show_sql_output:
103
+ conn.debugOutput = True
104
+ if (conn.dbName == 'postgres') and (conn.driver == 'pg8000') \
105
+ and conn._pool is not None:
106
+ conn._pool = None
107
+ if (conn.dbName == 'sqlite') and not conn._memory:
108
+ speedupSQLiteConnection(conn)
109
+ return conn
110
+
111
+
112
+ def getConnectionURI():
113
+ name = conftest.option.Database
114
+ if name in conftest.connectionShortcuts:
115
+ name = conftest.connectionShortcuts[name]
116
+ return name
117
+
118
+ try:
119
+ connection = getConnection()
120
+ except Exception as e:
121
+ # At least this module should be importable...
122
+ # The module was imported during documentation building
123
+ if 'sphinx' not in sys.modules:
124
+ print("Could not open database: %s" % e, file=sys.stderr)
125
+ else:
126
+ if (connection.dbName == 'firebird'):
127
+ use_microseconds(False)
128
+
129
+
130
+ class InstalledTestDatabase(sqlobject.SQLObject):
131
+ """
132
+ This table is set up in SQLite (always, regardless of --Database) and
133
+ tracks what tables have been set up in the 'real' database. This
134
+ way we don't keep recreating the tables over and over when there
135
+ are multiple tests that use a table.
136
+ """
137
+
138
+ _connection = installedDBTracker
139
+ table_name = sqlobject.StringCol(notNull=True)
140
+ createSQL = sqlobject.StringCol(notNull=True)
141
+ connectionURI = sqlobject.StringCol(notNull=True)
142
+
143
+ @classmethod
144
+ def installOrClear(cls, soClasses, force=False):
145
+ cls.setup()
146
+ reversed = list(soClasses)[:]
147
+ reversed.reverse()
148
+ # If anything needs to be dropped, they all must be dropped
149
+ # But if we're forcing it, then we'll always drop
150
+ if force:
151
+ any_drops = True
152
+ else:
153
+ any_drops = False
154
+ for soClass in reversed:
155
+ table = soClass.sqlmeta.table
156
+ if not soClass._connection.tableExists(table):
157
+ continue
158
+ items = list(cls.selectBy(
159
+ table_name=table,
160
+ connectionURI=soClass._connection.uri()))
161
+ if items:
162
+ instance = items[0]
163
+ sql = instance.createSQL
164
+ else:
165
+ sql = None
166
+ newSQL, constraints = soClass.createTableSQL()
167
+ if sql != newSQL:
168
+ if sql is not None:
169
+ instance.destroySelf()
170
+ any_drops = True
171
+ break
172
+ for soClass in reversed:
173
+ if soClass._connection.tableExists(soClass.sqlmeta.table):
174
+ if any_drops:
175
+ cls.drop(soClass)
176
+ else:
177
+ cls.clear(soClass)
178
+ for soClass in soClasses:
179
+ table = soClass.sqlmeta.table
180
+ if not soClass._connection.tableExists(table):
181
+ cls.install(soClass)
182
+
183
+ @classmethod
184
+ def install(cls, soClass):
185
+ """
186
+ Creates the given table in its database.
187
+ """
188
+ sql = getattr(soClass, soClass._connection.dbName + 'Create',
189
+ None)
190
+ all_extra = []
191
+ if sql:
192
+ soClass._connection.query(sql)
193
+ else:
194
+ sql, extra_sql = soClass.createTableSQL()
195
+ soClass.createTable(applyConstraints=False)
196
+ all_extra.extend(extra_sql)
197
+ cls(table_name=soClass.sqlmeta.table,
198
+ createSQL=sql,
199
+ connectionURI=soClass._connection.uri())
200
+ for extra_sql in all_extra:
201
+ soClass._connection.query(extra_sql)
202
+
203
+ @classmethod
204
+ def drop(cls, soClass):
205
+ """
206
+ Drops a the given table from its database
207
+ """
208
+ sql = getattr(soClass, soClass._connection.dbName + 'Drop', None)
209
+ if sql:
210
+ soClass._connection.query(sql)
211
+ else:
212
+ soClass.dropTable()
213
+
214
+ @classmethod
215
+ def clear(cls, soClass):
216
+ """
217
+ Removes all the rows from a table.
218
+ """
219
+ soClass.clearTable()
220
+
221
+ @classmethod
222
+ def setup(cls):
223
+ """
224
+ This sets up *this* table.
225
+ """
226
+ if not cls._connection.tableExists(cls.sqlmeta.table):
227
+ cls.createTable()
228
+
229
+ installOrClear = InstalledTestDatabase.installOrClear
230
+
231
+
232
+ class Dummy(object):
233
+
234
+ """
235
+ Used for creating fake objects; a really poor 'mock object'.
236
+ """
237
+
238
+ def __init__(self, **kw):
239
+ for name, value in kw.items():
240
+ setattr(self, name, value)
241
+
242
+
243
+ def inserts(cls, data, schema=None):
244
+ """
245
+ Creates a bunch of rows.
246
+
247
+ You can use it like::
248
+
249
+ inserts(Person, [{'fname': 'blah', 'lname': 'doe'}, ...])
250
+
251
+ Or::
252
+
253
+ inserts(Person, [('blah', 'doe')], schema=
254
+ ['fname', 'lname'])
255
+
256
+ If you give a single string for the `schema` then it'll split
257
+ that string to get the list of column names.
258
+ """
259
+ if schema:
260
+ if isinstance(schema, str):
261
+ schema = schema.split()
262
+ keywordData = []
263
+ for item in data:
264
+ itemDict = {}
265
+ for name, value in zip(schema, item):
266
+ itemDict[name] = value
267
+ keywordData.append(itemDict)
268
+ data = keywordData
269
+ results = []
270
+ for args in data:
271
+ results.append(cls(**args))
272
+ return results
273
+
274
+
275
+ def supports(feature):
276
+ dbName = connection.dbName
277
+ support = supportsMatrix.get('+' + feature, None)
278
+ notSupport = supportsMatrix.get('-' + feature, None)
279
+ if support is not None and dbName in support.split():
280
+ return True
281
+ elif support:
282
+ return False
283
+ if notSupport is not None and dbName in notSupport.split():
284
+ return False
285
+ elif notSupport:
286
+ return True
287
+ assert notSupport is not None or support is not None, (
288
+ "The supportMatrix does not list this feature: %r"
289
+ % feature)
290
+
291
+
292
+ # To avoid name clashes:
293
+ _inserts = inserts
294
+
295
+
296
+ def setSQLiteConnectionFactory(TableClass, factory):
297
+ from sqlobject.sqlite.sqliteconnection import SQLiteConnection
298
+ conn = TableClass._connection
299
+ TableClass._connection = SQLiteConnection(
300
+ filename=conn.filename,
301
+ name=conn.name, debug=conn.debug, debugOutput=conn.debugOutput,
302
+ cache=conn.cache, style=conn.style, autoCommit=conn.autoCommit,
303
+ debugThreading=conn.debugThreading, registry=conn.registry,
304
+ factory=factory
305
+ )
306
+ speedupSQLiteConnection(TableClass._connection)
307
+ installOrClear([TableClass])
308
+
309
+
310
+ def deprecated_module():
311
+ sqlobject.main.warnings_level = None
312
+ sqlobject.main.exception_level = None
313
+
314
+
315
+ def setup_module(mod):
316
+ # modules with '_old' test backward compatible methods, so they
317
+ # don't get warnings or errors.
318
+ mod_name = str(mod.__name__)
319
+ if mod_name.endswith('/py'):
320
+ mod_name = mod_name[:-3]
321
+ if mod_name.endswith('_old'):
322
+ sqlobject.main.warnings_level = None
323
+ sqlobject.main.exception_level = None
324
+ else:
325
+ sqlobject.main.warnings_level = None
326
+ sqlobject.main.exception_level = 0
327
+
328
+
329
+ def teardown_module(mod=None):
330
+ sqlobject.main.warnings_level = None
331
+ sqlobject.main.exception_level = 0
332
+
333
+
334
+ def setupLogging():
335
+ fmt = '[%(asctime)s] %(name)s %(levelname)s: %(message)s'
336
+ formatter = logging.Formatter(fmt)
337
+ hdlr = logging.StreamHandler(sys.stderr)
338
+ hdlr.setFormatter(formatter)
339
+ hdlr.setLevel(logging.NOTSET)
340
+ logger = logging.getLogger()
341
+ logger.addHandler(hdlr)
342
+
343
+
344
+ def setupCyclicClasses(*classes):
345
+ if not supports('dropTableCascade'):
346
+ skip("dropTableCascade isn't supported")
347
+ conn = getConnection()
348
+ for soClass in classes:
349
+ soClass.setConnection(conn)
350
+ soClass.dropTable(ifExists=True, cascade=True)
351
+
352
+ constraints = []
353
+ for soClass in classes:
354
+ constraints += soClass.createTable(ifNotExists=True,
355
+ applyConstraints=False)
356
+ for constraint in constraints:
357
+ conn.query(constraint)
358
+
359
+
360
+ __all__ = ['Dummy', 'deprecated_module', 'getConnection', 'getConnectionURI',
361
+ 'inserts', 'raises', 'setupClass', 'setupCyclicClasses',
362
+ 'setupLogging', 'setup_module', 'supports', 'teardown_module',
363
+ ]
@@ -0,0 +1,133 @@
1
+ from formencode import validators
2
+ from sqlobject import ForeignKey, IntCol, SQLObject, StringCol
3
+ from sqlobject.tests.dbtest import getConnection, InstalledTestDatabase, \
4
+ raises, setupClass, setupCyclicClasses
5
+
6
+
7
+ class SOTestComposerKey(SQLObject):
8
+ name = StringCol()
9
+ id2 = IntCol(default=None, unique=True)
10
+
11
+
12
+ class SOTestWorkKey(SQLObject):
13
+ class sqlmeta:
14
+ idName = "work_id"
15
+
16
+ composer = ForeignKey('SOTestComposerKey', cascade=True)
17
+ title = StringCol()
18
+
19
+
20
+ class SOTestWorkKey2(SQLObject):
21
+ title = StringCol()
22
+
23
+
24
+ class SOTestOtherColumn(SQLObject):
25
+ key1 = ForeignKey('SOTestComposerKey', default=None)
26
+ key2 = ForeignKey('SOTestComposerKey', refColumn='id2', default=None)
27
+
28
+
29
+ def test1():
30
+ setupClass([SOTestComposerKey, SOTestWorkKey])
31
+
32
+ c = SOTestComposerKey(name='Mahler, Gustav')
33
+ w1 = SOTestWorkKey(composer=c, title='Symphony No. 9')
34
+ w2 = SOTestWorkKey(composer=None, title=None)
35
+
36
+ # Select by usual way
37
+ s = SOTestWorkKey.selectBy(composerID=c.id, title='Symphony No. 9')
38
+ assert s.count() == 1
39
+ assert s[0] == w1
40
+ # selectBy object.id
41
+ s = SOTestWorkKey.selectBy(composer=c.id, title='Symphony No. 9')
42
+ assert s.count() == 1
43
+ assert s[0] == w1
44
+ # selectBy object
45
+ s = SOTestWorkKey.selectBy(composer=c, title='Symphony No. 9')
46
+ assert s.count() == 1
47
+ assert s[0] == w1
48
+ # selectBy id
49
+ s = SOTestWorkKey.selectBy(id=w1.id)
50
+ assert s.count() == 1
51
+ assert s[0] == w1
52
+ # is None handled correctly?
53
+ s = SOTestWorkKey.selectBy(composer=None, title=None)
54
+ assert s.count() == 1
55
+ assert s[0] == w2
56
+
57
+ s = SOTestWorkKey.selectBy()
58
+ assert s.count() == 2
59
+
60
+ # select with objects
61
+ s = SOTestWorkKey.select(SOTestWorkKey.q.composerID == c.id)
62
+ assert s.count() == 1
63
+ assert s[0] == w1
64
+ s = SOTestWorkKey.select(SOTestWorkKey.q.composer == c.id)
65
+ assert s.count() == 1
66
+ assert s[0] == w1
67
+ s = SOTestWorkKey.select(SOTestWorkKey.q.composerID == c)
68
+ assert s.count() == 1
69
+ assert s[0] == w1
70
+ s = SOTestWorkKey.select(SOTestWorkKey.q.composer == c)
71
+ assert s.count() == 1
72
+ assert s[0] == w1
73
+ s = SOTestWorkKey.select(
74
+ (SOTestWorkKey.q.composer == c)
75
+ & (SOTestWorkKey.q.title == 'Symphony No. 9'))
76
+ assert s.count() == 1
77
+ assert s[0] == w1
78
+
79
+
80
+ def test2():
81
+ SOTestWorkKey._connection = getConnection()
82
+ InstalledTestDatabase.drop(SOTestWorkKey)
83
+ setupClass([SOTestComposerKey, SOTestWorkKey2], force=True)
84
+ SOTestWorkKey2.sqlmeta.addColumn(ForeignKey('SOTestComposerKey'),
85
+ changeSchema=True)
86
+
87
+
88
+ def test_otherColumn():
89
+ setupClass([SOTestComposerKey, SOTestOtherColumn])
90
+ test_composer1 = SOTestComposerKey(name='Test1')
91
+ test_composer2 = SOTestComposerKey(name='Test2', id2=2)
92
+ test_fkey = SOTestOtherColumn(key1=test_composer1)
93
+ test_other = SOTestOtherColumn(key2=test_composer2.id2)
94
+ getConnection().cache.clear()
95
+ assert test_fkey.key1 == test_composer1
96
+ assert test_other.key2 == test_composer2
97
+
98
+
99
+ class SOTestFKValidationA(SQLObject):
100
+ name = StringCol()
101
+ bfk = ForeignKey("SOTestFKValidationB")
102
+ cfk = ForeignKey("SOTestFKValidationC", default=None)
103
+
104
+
105
+ class SOTestFKValidationB(SQLObject):
106
+ name = StringCol()
107
+ afk = ForeignKey("SOTestFKValidationA")
108
+
109
+
110
+ class SOTestFKValidationC(SQLObject):
111
+ class sqlmeta:
112
+ idType = str
113
+ name = StringCol()
114
+
115
+
116
+ def test_foreignkey_validation():
117
+ setupCyclicClasses(SOTestFKValidationA, SOTestFKValidationB,
118
+ SOTestFKValidationC)
119
+ a = SOTestFKValidationA(name="testa", bfk=None)
120
+ b = SOTestFKValidationB(name="testb", afk=a)
121
+ c = SOTestFKValidationC(id='testc', name="testc")
122
+ a.bfk = b
123
+ a.cfk = c
124
+ assert a.bfk == b
125
+ assert a.cfk == c
126
+ assert b.afk == a
127
+
128
+ raises(validators.Invalid,
129
+ SOTestFKValidationA, name="testa", bfk='testb', cfk='testc')
130
+
131
+ a = SOTestFKValidationA(name="testa", bfk=1, cfk='testc')
132
+ assert a.bfkID == 1
133
+ assert a.cfkID == 'testc'