1 | --- trunk/django/django/models/auth.py (revision 656)
|
---|
2 | +++ trunk/django/django/models/auth.py (working copy)
|
---|
3 | @@ -181,7 +181,7 @@
|
---|
4 | action_time = meta.DateTimeField(auto_now=True)
|
---|
5 | user = meta.ForeignKey(User)
|
---|
6 | content_type = meta.ForeignKey(core.ContentType, blank=True, null=True) # TODO: content_type_id name?
|
---|
7 | - object_id = meta.TextField(blank=True, null=True)
|
---|
8 | + object_id = meta.CharField(maxlength=100, blank=True, null=True)
|
---|
9 | object_repr = meta.CharField(maxlength=200)
|
---|
10 | action_flag = meta.PositiveSmallIntegerField()
|
---|
11 | change_message = meta.TextField(blank=True)
|
---|
12 | --- trunk/django/django/core/db/__init__.py (revision 656)
|
---|
13 | +++ trunk/django/django/core/db/__init__.py (working copy)
|
---|
14 | @@ -44,3 +44,4 @@
|
---|
15 | OPERATOR_MAPPING = dbmod.OPERATOR_MAPPING
|
---|
16 | DATA_TYPES = dbmod.DATA_TYPES
|
---|
17 | DATA_TYPES_REVERSE = dbmod.DATA_TYPES_REVERSE
|
---|
18 | +EMPTY_STR_EQUIV = dbmod.EMPTY_STR_EQUIV
|
---|
19 | --- trunk/django/django/core/db/backends/postgresql.py (revision 656)
|
---|
20 | +++ trunk/django/django/core/db/backends/postgresql.py (working copy)
|
---|
21 | @@ -188,3 +188,5 @@
|
---|
22 | 1266: 'TimeField',
|
---|
23 | 1700: 'FloatField',
|
---|
24 | }
|
---|
25 | +
|
---|
26 | +EMPTY_STR_EQUIV = ''
|
---|
27 | \ No newline at end of file
|
---|
28 | --- trunk/django/django/core/db/backends/sqlite3.py (revision 656)
|
---|
29 | +++ trunk/django/django/core/db/backends/sqlite3.py (working copy)
|
---|
30 | @@ -174,3 +174,5 @@
|
---|
31 | }
|
---|
32 |
|
---|
33 | DATA_TYPES_REVERSE = {}
|
---|
34 | +
|
---|
35 | +EMPTY_STR_EQUIV = ''
|
---|
36 | \ No newline at end of file
|
---|
37 | --- trunk/django/django/core/db/backends/mysql.py (revision 656)
|
---|
38 | +++ trunk/django/django/core/db/backends/mysql.py (working copy)
|
---|
39 | @@ -155,3 +155,5 @@
|
---|
40 | FIELD_TYPE.LONG_BLOB: 'TextField',
|
---|
41 | FIELD_TYPE.VAR_STRING: 'CharField',
|
---|
42 | }
|
---|
43 | +
|
---|
44 | +EMPTY_STR_EQUIV = ''
|
---|
45 | \ No newline at end of file
|
---|
46 | --- None (revision 656)
|
---|
47 | +++ trunk/django/django/core/db/backends/oracle.py (working copy)
|
---|
48 | @@ -0,0 +1,162 @@
|
---|
49 | +"""
|
---|
50 | +Oracle database backend for Django.
|
---|
51 | +
|
---|
52 | +Requires cx_Oracle: http://www.computronix.com/utilities.shtml
|
---|
53 | +"""
|
---|
54 | +
|
---|
55 | +from django.core.db import base, typecasts
|
---|
56 | +import cx_Oracle as Database
|
---|
57 | +
|
---|
58 | +#needed for fetchone, fetchmany, fetchall support
|
---|
59 | +from django.core.db.dicthelpers import *
|
---|
60 | +
|
---|
61 | +
|
---|
62 | +DatabaseError = Database.DatabaseError
|
---|
63 | +
|
---|
64 | +class DatabaseWrapper:
|
---|
65 | + def __init__(self):
|
---|
66 | + self.connection = None
|
---|
67 | + self.queries = []
|
---|
68 | +
|
---|
69 | + def cursor(self):
|
---|
70 | + from django.conf.settings import DATABASE_USER, DATABASE_NAME, DATABASE_HOST, DATABASE_PASSWORD, DEBUG
|
---|
71 | + if self.connection is None:
|
---|
72 | + if DATABASE_NAME == '' or DATABASE_USER == '' or DATABASE_PASSWORD == '':
|
---|
73 | + from django.core.exceptions import ImproperlyConfigured
|
---|
74 | + raise ImproperlyConfigured, "You need to specify DATABASE_NAME, DATABASE_USER, and DATABASE_PASSWORD in your Django settings file."
|
---|
75 | + conn_string = "%s/%s@%s" % (DATABASE_USER, DATABASE_PASSWORD, DATABASE_NAME)
|
---|
76 | + self.connection = Database.connect(conn_string)
|
---|
77 | + return FormatStylePlaceholderCursor(self.connection)
|
---|
78 | +
|
---|
79 | + def commit(self):
|
---|
80 | + self.connection.commit()
|
---|
81 | +
|
---|
82 | + def rollback(self):
|
---|
83 | + if self.connection:
|
---|
84 | + self.connection.rollback()
|
---|
85 | +
|
---|
86 | + def close(self):
|
---|
87 | + if self.connection is not None:
|
---|
88 | + self.connection.close()
|
---|
89 | + self.connection = None
|
---|
90 | +
|
---|
91 | +class FormatStylePlaceholderCursor(Database.Cursor):
|
---|
92 | + """
|
---|
93 | + Django uses "format" (e.g. '%s') style placeholders, but Oracle uses ":var" style.
|
---|
94 | + This fixes it -- but note that if you want to use a literal "%s" in a query,
|
---|
95 | + you'll need to use "%%s" (which I belive is true of other wrappers as well).
|
---|
96 | + """
|
---|
97 | +
|
---|
98 | + def execute(self, query, params=[]):
|
---|
99 | + query = self.convert_arguments(query, len(params))
|
---|
100 | + return Database.Cursor.execute(self, query, params)
|
---|
101 | +
|
---|
102 | + def executemany(self, query, params=[]):
|
---|
103 | + query = self.convert_arguments(query, len(params[0]))
|
---|
104 | + return Database.Cursor.executemany(self, query, params)
|
---|
105 | +
|
---|
106 | + def convert_arguments(self, query, num_params):
|
---|
107 | + # replace occurances of "%s" with ":arg" - Oracle requires colons for parameter placeholders.
|
---|
108 | + args = [':arg' for i in range(num_params)]
|
---|
109 | + return query % tuple(args)
|
---|
110 | +
|
---|
111 | +def get_last_insert_id(cursor, table_name, pk_name):
|
---|
112 | + query = "SELECT %s_sq.currval from dual" % table_name
|
---|
113 | + cursor.execute(query)
|
---|
114 | + return cursor.fetchone()[0]
|
---|
115 | +
|
---|
116 | +def get_date_extract_sql(lookup_type, table_name):
|
---|
117 | + # lookup_type is 'year', 'month', 'day'
|
---|
118 | + # http://www.psoug.org/reference/date_func.html
|
---|
119 | + return "EXTRACT(%s FROM %s)" % (lookup_type, table_name)
|
---|
120 | +
|
---|
121 | +def get_date_trunc_sql(lookup_type, field_name):
|
---|
122 | + return "EXTRACT(%s FROM TRUNC(%s))" % (lookup_type, table_name)
|
---|
123 | +
|
---|
124 | +def get_limit_offset_sql(limit, offset=None):
|
---|
125 | + # Limits and offset are too complicated to be handled here.
|
---|
126 | + #Instead, they are handled in django.core.meta.__init__.py
|
---|
127 | + pass
|
---|
128 | +
|
---|
129 | +def get_random_function_sql():
|
---|
130 | + return "DBMS_RANDOM.RANDOM"
|
---|
131 | +
|
---|
132 | +def get_table_list(cursor):
|
---|
133 | + "Returns a list of table names in the current database."
|
---|
134 | + cursor.execute("SELECT TABLE_NAME FROM USER_TABLES")
|
---|
135 | + return [row[0] for row in cursor.fetchall()]
|
---|
136 | +
|
---|
137 | +def get_relations(cursor, table_name):
|
---|
138 | + """
|
---|
139 | + Returns a dictionary of {field_index: (field_index_other_table, other_table)}
|
---|
140 | + representing all relationships to the given table. Indexes are 0-based.
|
---|
141 | + """
|
---|
142 | + raise NotImplementedError
|
---|
143 | +
|
---|
144 | +
|
---|
145 | +OPERATOR_MAPPING = {
|
---|
146 | + 'exact': '=',
|
---|
147 | + 'iexact': 'LIKE',
|
---|
148 | + 'contains': 'LIKE',
|
---|
149 | + 'icontains': 'LIKE',
|
---|
150 | + 'ne': '!=',
|
---|
151 | + 'gt': '>',
|
---|
152 | + 'gte': '>=',
|
---|
153 | + 'lt': '<',
|
---|
154 | + 'lte': '<=',
|
---|
155 | + 'startswith': 'LIKE',
|
---|
156 | + 'endswith': 'LIKE',
|
---|
157 | + 'istartswith': 'LIKE',
|
---|
158 | + 'iendswith': 'LIKE',
|
---|
159 | +}
|
---|
160 | +
|
---|
161 | +# This dictionary maps Field objects to their associated MySQL column
|
---|
162 | +# types, as strings. Column-type strings can contain format strings; they'll
|
---|
163 | +# be interpolated against the values of Field.__dict__ before being output.
|
---|
164 | +# If a column type is set to None, it won't be included in the output.
|
---|
165 | +DATA_TYPES = {
|
---|
166 | + 'AutoField': 'number(38)',
|
---|
167 | + 'BooleanField': 'number(1)',
|
---|
168 | + 'CharField': 'varchar2(%(maxlength)s)',
|
---|
169 | + 'CommaSeparatedIntegerField': 'varchar2(%(maxlength)s)',
|
---|
170 | + 'DateField': 'date',
|
---|
171 | + 'DateTimeField': 'date',
|
---|
172 | + 'EmailField': 'varchar2(75)',
|
---|
173 | + 'FileField': 'varchar2(100)',
|
---|
174 | + 'FloatField': 'number(%(max_digits)s, %(decimal_places)s)',
|
---|
175 | + 'ImageField': 'varchar2(100)',
|
---|
176 | + 'IntegerField': 'integer',
|
---|
177 | + 'IPAddressField': 'char(15)',
|
---|
178 | + 'ManyToManyField': None,
|
---|
179 | + 'NullBooleanField': 'integer',
|
---|
180 | + 'OneToOneField': 'integer',
|
---|
181 | + 'PhoneNumberField': 'varchar(20)',
|
---|
182 | + 'PositiveIntegerField': 'integer',
|
---|
183 | + 'PositiveSmallIntegerField': 'smallint',
|
---|
184 | + 'SlugField': 'varchar(50)',
|
---|
185 | + 'SmallIntegerField': 'smallint',
|
---|
186 | + 'SmallTextField': 'varchar2(4000)',
|
---|
187 | + 'TextField': 'long',
|
---|
188 | + 'TimeField': 'timestamp',
|
---|
189 | + 'URLField': 'varchar(200)',
|
---|
190 | + 'USStateField': 'varchar(2)',
|
---|
191 | + 'XMLField': 'long',
|
---|
192 | +}
|
---|
193 | +
|
---|
194 | +# Maps type codes to Django Field types.
|
---|
195 | +DATA_TYPES_REVERSE = {
|
---|
196 | + 16: 'BooleanField',
|
---|
197 | + 21: 'SmallIntegerField',
|
---|
198 | + 23: 'IntegerField',
|
---|
199 | + 25: 'TextField',
|
---|
200 | + 869: 'IPAddressField',
|
---|
201 | + 1043: 'CharField',
|
---|
202 | + 1082: 'DateField',
|
---|
203 | + 1083: 'TimeField',
|
---|
204 | + 1114: 'DateTimeField',
|
---|
205 | + 1184: 'DateTimeField',
|
---|
206 | + 1266: 'TimeField',
|
---|
207 | + 1700: 'FloatField',
|
---|
208 | +}
|
---|
209 | +
|
---|
210 | +EMPTY_STR_EQUIV = ' '
|
---|
211 | \ No newline at end of file
|
---|
212 | --- trunk/django/django/core/meta/__init__.py (revision 656)
|
---|
213 | +++ trunk/django/django/core/meta/__init__.py (working copy)
|
---|
214 | @@ -777,8 +777,8 @@
|
---|
215 | pk_set = bool(pk_val)
|
---|
216 | record_exists = True
|
---|
217 | if pk_set:
|
---|
218 | - # Determine whether a record with the primary key already exists.
|
---|
219 | - cursor.execute("SELECT 1 FROM %s WHERE %s=%%s LIMIT 1" % (opts.db_table, opts.pk.column), [pk_val])
|
---|
220 | + # Determine whether a record with the primary key already exists.
|
---|
221 | + cursor.execute("SELECT 1 FROM %s WHERE %s=%%s" % (opts.db_table, opts.pk.column), [pk_val])
|
---|
222 | # If it does already exist, do an UPDATE.
|
---|
223 | if cursor.fetchone():
|
---|
224 | db_values = [f.get_db_prep_save(f.pre_save(getattr(self, f.column), False)) for f in non_pks]
|
---|
225 | @@ -1102,10 +1102,11 @@
|
---|
226 | kwargs['select'] = kwargs.get('select', {}).items()
|
---|
227 |
|
---|
228 | cursor = db.db.cursor()
|
---|
229 | - select, sql, params = function_get_sql_clause(opts, **kwargs)
|
---|
230 | - cursor.execute("SELECT " + (kwargs.get('distinct') and "DISTINCT " or "") + ",".join(select) + sql, params)
|
---|
231 | + select, sql, params, full_query = function_get_sql_clause(opts, **kwargs)
|
---|
232 | + cursor.execute(full_query, params)
|
---|
233 | fill_cache = kwargs.get('select_related')
|
---|
234 | index_end = len(opts.fields)
|
---|
235 | +
|
---|
236 | while 1:
|
---|
237 | rows = cursor.fetchmany(GET_ITERATOR_CHUNK_SIZE)
|
---|
238 | if not rows:
|
---|
239 | @@ -1127,7 +1128,7 @@
|
---|
240 | kwargs['offset'] = None
|
---|
241 | kwargs['limit'] = None
|
---|
242 | kwargs['select_related'] = False
|
---|
243 | - _, sql, params = function_get_sql_clause(opts, **kwargs)
|
---|
244 | + _, sql, params, full_query = function_get_sql_clause(opts, **kwargs)
|
---|
245 | cursor = db.db.cursor()
|
---|
246 | cursor.execute("SELECT COUNT(*)" + sql, params)
|
---|
247 | return cursor.fetchone()[0]
|
---|
248 | @@ -1144,7 +1145,7 @@
|
---|
249 | fields = [f.column for f in opts.fields]
|
---|
250 |
|
---|
251 | cursor = db.db.cursor()
|
---|
252 | - _, sql, params = function_get_sql_clause(opts, **kwargs)
|
---|
253 | + _, sql, params, full_query = function_get_sql_clause(opts, **kwargs)
|
---|
254 | select = ['%s.%s' % (opts.db_table, f) for f in fields]
|
---|
255 | cursor.execute("SELECT " + (kwargs.get('distinct') and "DISTINCT " or "") + ",".join(select) + sql, params)
|
---|
256 | while 1:
|
---|
257 | @@ -1171,9 +1172,9 @@
|
---|
258 | new_prefix = '%s%s' % (db_table, len(cache_tables_seen))
|
---|
259 | tables.append('%s %s' % (db_table, new_prefix))
|
---|
260 | db_table = new_prefix
|
---|
261 | - cache_tables_seen.append(db_table)
|
---|
262 | + cache_tables_seen.append(db_table)
|
---|
263 | where.append('%s.%s = %s.%s' % (old_prefix, f.column, db_table, f.rel.get_related_field().column))
|
---|
264 | - select.extend(['%s.%s' % (db_table, f2.column) for f2 in f.rel.to.fields])
|
---|
265 | + select.extend(['%s.%s as "%s.%s"' % (db_table, f2.column, db_table, f2.column) for f2 in f.rel.to.fields])
|
---|
266 | _fill_table_cache(f.rel.to, select, tables, where, db_table, cache_tables_seen)
|
---|
267 |
|
---|
268 | def _throw_bad_kwarg_error(kwarg):
|
---|
269 | @@ -1188,7 +1189,7 @@
|
---|
270 | # is specifically a list of where clauses to use for JOINs. This
|
---|
271 | # distinction is necessary because of support for "_or".
|
---|
272 |
|
---|
273 | - # table_count is used to ensure table aliases are unique.
|
---|
274 | + # table_count is used to ensure table aliases are unique.
|
---|
275 | tables, join_where, where, params = [], [], [], []
|
---|
276 | for kwarg, kwarg_value in kwarg_items:
|
---|
277 | if kwarg in ('order_by', 'limit', 'offset', 'select_related', 'distinct', 'select', 'tables', 'where', 'params'):
|
---|
278 | @@ -1333,14 +1334,58 @@
|
---|
279 | order_by.append('%s%s ASC' % (table_prefix, orderfield2column(f, opts)))
|
---|
280 | order_by = ", ".join(order_by)
|
---|
281 |
|
---|
282 | - # LIMIT and OFFSET clauses
|
---|
283 | - if kwargs.get('limit') is not None:
|
---|
284 | - limit_sql = " %s " % db.get_limit_offset_sql(kwargs['limit'], kwargs.get('offset'))
|
---|
285 | - else:
|
---|
286 | - assert kwargs.get('offset') is None, "'offset' is not allowed without 'limit'"
|
---|
287 | - limit_sql = ""
|
---|
288 |
|
---|
289 | - return select, " FROM " + ",".join(tables) + (where and " WHERE " + " AND ".join(where) or "") + (order_by and " ORDER BY " + order_by or "") + limit_sql, params
|
---|
290 | + sql = " FROM " + ",".join(tables) + (where and " WHERE " + " AND ".join(where) or "") + (order_by and " ORDER BY " + order_by or "")
|
---|
291 | +
|
---|
292 | + if (db.DATABASE_ENGINE != 'oracle'):
|
---|
293 | + # LIMIT and OFFSET clauses
|
---|
294 | + if kwargs.get('limit') is not None:
|
---|
295 | + limit_sql = " %s " % db.get_limit_offset_sql(kwargs['limit'], kwargs.get('offset'))
|
---|
296 | + else:
|
---|
297 | + assert kwargs.get('offset') is None, "'offset' is not allowed without 'limit'"
|
---|
298 | + limit_sql = ""
|
---|
299 | +
|
---|
300 | +
|
---|
301 | + full_query = "SELECT " + (kwargs.get('distinct') and "DISTINCT " or "") + ",".join(select) + sql + limit_sql
|
---|
302 | + return select, sql + limit_sql, params, full_query
|
---|
303 | + else:
|
---|
304 | + # To support limits and offsets, Oracle requires some funky rewriting of an otherwise normal looking query.
|
---|
305 | +
|
---|
306 | + select_clause = ",".join(select)
|
---|
307 | + distinct = (kwargs.get('distinct') and "DISTINCT " or "")
|
---|
308 | + from_clause = ",".join(tables)
|
---|
309 | + where_clause = (where and " WHERE " + " AND ".join(where) or "")
|
---|
310 | +
|
---|
311 | + if order_by:
|
---|
312 | + order_by_clause = " OVER (ORDER BY %s )" % (order_by)
|
---|
313 | + else:
|
---|
314 | + #Oracle's row_number() function always requires an order-by clause.
|
---|
315 | + #So we need to define a default order-by, since none was provided.
|
---|
316 | + order_by_clause = " OVER (ORDER BY %s.%s)" % (opts.db_table, opts.fields[0].name)
|
---|
317 | +
|
---|
318 | + # limit_and_offset_clause
|
---|
319 | + limit = kwargs.get('limit',0)
|
---|
320 | + offset = kwargs.get('offset',0)
|
---|
321 | +
|
---|
322 | + limit_and_offset_clause = ''
|
---|
323 | + if limit:
|
---|
324 | + limit = int(limit)
|
---|
325 | + offset = int(offset)
|
---|
326 | + limit_and_offset_clause = "WHERE rn > %s AND rn <= %s" % (offset, limit+offset)
|
---|
327 | + else:
|
---|
328 | + limit_and_offset_clause = "WHERE rn > %s" % (offset)
|
---|
329 | +
|
---|
330 | + full_query = """SELECT * FROM
|
---|
331 | + (SELECT %s
|
---|
332 | + %s,
|
---|
333 | + ROW_NUMBER() %s AS rn
|
---|
334 | + FROM %s
|
---|
335 | + %s
|
---|
336 | + )
|
---|
337 | + %s
|
---|
338 | + """ % (distinct, select_clause, order_by_clause, from_clause, where_clause, limit_and_offset_clause)
|
---|
339 | + return select, sql, params, full_query
|
---|
340 | +
|
---|
341 |
|
---|
342 | def function_get_in_bulk(opts, klass, *args, **kwargs):
|
---|
343 | id_list = args and args[0] or kwargs['id_list']
|
---|
344 | @@ -1366,7 +1411,7 @@
|
---|
345 | kwargs['order_by'] = [] # Clear this because it'll mess things up otherwise.
|
---|
346 | if field.null:
|
---|
347 | kwargs.setdefault('where', []).append('%s.%s IS NOT NULL' % (opts.db_table, field.column))
|
---|
348 | - select, sql, params = function_get_sql_clause(opts, **kwargs)
|
---|
349 | + select, sql, params, full_query = function_get_sql_clause(opts, **kwargs)
|
---|
350 | sql = 'SELECT %s %s GROUP BY 1 ORDER BY 1' % (db.get_date_trunc_sql(kind, '%s.%s' % (opts.db_table, field.column)), sql)
|
---|
351 | cursor = db.db.cursor()
|
---|
352 | cursor.execute(sql, params)
|
---|
353 | --- trunk/django/django/core/meta/fields.py (revision 656)
|
---|
354 | +++ trunk/django/django/core/meta/fields.py (working copy)
|
---|
355 | @@ -1,4 +1,5 @@
|
---|
356 | from django.conf import settings
|
---|
357 | +from django.core import db
|
---|
358 | from django.core import formfields, validators
|
---|
359 | from django.core.exceptions import ObjectDoesNotExist
|
---|
360 | from django.utils.functional import curry
|
---|
361 | @@ -120,6 +121,12 @@
|
---|
362 |
|
---|
363 | def get_db_prep_save(self, value):
|
---|
364 | "Returns field's value prepared for saving into a database."
|
---|
365 | +
|
---|
366 | + # Oracle treats empty strings ('') the same as NULLs.
|
---|
367 | + # To get around this wart, we need to change it to something else...
|
---|
368 | + if value == '':
|
---|
369 | + string_replacement = getattr(db,'EMPTY_STR_EQUIV','')
|
---|
370 | + value = string_replacement
|
---|
371 | return value
|
---|
372 |
|
---|
373 | def get_db_prep_lookup(self, lookup_type, value):
|
---|
374 | @@ -129,7 +136,12 @@
|
---|
375 | elif lookup_type in ('range', 'in'):
|
---|
376 | return value
|
---|
377 | elif lookup_type == 'year':
|
---|
378 | - return ['%s-01-01' % value, '%s-12-31' % value]
|
---|
379 | + if settings.DATABASE_ENGINE == 'oracle':
|
---|
380 | + from_dt = datetime.date(int(value), 01, 01)
|
---|
381 | + to_dt = datetime.date(int(value), 12, 31)
|
---|
382 | + return [from_dt, to_dt]
|
---|
383 | + else:
|
---|
384 | + return ['%s-01-01' % value, '%s-12-31' % value]
|
---|
385 | elif lookup_type in ('contains', 'icontains'):
|
---|
386 | return ["%%%s%%" % prep_for_like_query(value)]
|
---|
387 | elif lookup_type == 'iexact':
|
---|
388 | @@ -287,6 +299,14 @@
|
---|
389 | kwargs['blank'] = True
|
---|
390 | Field.__init__(self, *args, **kwargs)
|
---|
391 |
|
---|
392 | + def get_db_prep_lookup(self, lookup_type, value):
|
---|
393 | + if settings.DATABASE_ENGINE == 'oracle':
|
---|
394 | + if value == 'True':
|
---|
395 | + value = 1
|
---|
396 | + elif value == 'False':
|
---|
397 | + value = 0
|
---|
398 | + return Field.get_db_prep_lookup(self, lookup_type, value)
|
---|
399 | +
|
---|
400 | def get_manipulator_field_objs(self):
|
---|
401 | return [formfields.CheckboxField]
|
---|
402 |
|
---|
403 | @@ -305,12 +325,30 @@
|
---|
404 | if auto_now or auto_now_add:
|
---|
405 | kwargs['editable'] = False
|
---|
406 | Field.__init__(self, verbose_name, name, **kwargs)
|
---|
407 | -
|
---|
408 | - def get_db_prep_lookup(self, lookup_type, value):
|
---|
409 | - if lookup_type == 'range':
|
---|
410 | - value = [str(v) for v in value]
|
---|
411 | - else:
|
---|
412 | - value = str(value)
|
---|
413 | +
|
---|
414 | + def get_db_prep_lookup(self, lookup_type, value):
|
---|
415 | + # Oracle driver can deal with datetime.time objects natively
|
---|
416 | + # and doesn't need to convert to string
|
---|
417 | + if settings.DATABASE_ENGINE == 'oracle':
|
---|
418 | + if lookup_type == 'range':
|
---|
419 | + value = [v for v in value]
|
---|
420 | + #elif lookup_type == 'day':
|
---|
421 | + # value = int(value)
|
---|
422 | + elif lookup_type in ('exact', 'gt', 'gte', 'lt', 'lte', 'ne'):
|
---|
423 | + if type(value) == str:
|
---|
424 | + pattern = r'^\d\d\d\d-\d\d-\d\d$' # "YYYY-MM-DD"
|
---|
425 | + from re import search
|
---|
426 | + if search(pattern, value) != None:
|
---|
427 | + year, month, day = map(int, value.split('-'))
|
---|
428 | + value = datetime.date(year, month, day)
|
---|
429 | + else:
|
---|
430 | + value = value
|
---|
431 | + else:
|
---|
432 | + if lookup_type == 'range':
|
---|
433 | + value = [str(v) for v in value]
|
---|
434 | + else:
|
---|
435 | + value = str(value)
|
---|
436 | +
|
---|
437 | return Field.get_db_prep_lookup(self, lookup_type, value)
|
---|
438 |
|
---|
439 | def pre_save(self, value, add):
|
---|
440 | @@ -321,22 +359,30 @@
|
---|
441 | def get_db_prep_save(self, value):
|
---|
442 | # Casts dates into string format for entry into database.
|
---|
443 | if value is not None:
|
---|
444 | - value = value.strftime('%Y-%m-%d')
|
---|
445 | - return Field.get_db_prep_save(self, value)
|
---|
446 | + if settings.DATABASE_ENGINE != 'oracle':
|
---|
447 | + #Oracle does not need a string conversion
|
---|
448 | + value = value.strftime('%Y-%m-%d')
|
---|
449 | + return Field.get_db_prep_save(self, value)
|
---|
450 |
|
---|
451 | def get_manipulator_field_objs(self):
|
---|
452 | return [formfields.DateField]
|
---|
453 |
|
---|
454 | class DateTimeField(DateField):
|
---|
455 | def get_db_prep_save(self, value):
|
---|
456 | - # Casts dates into string format for entry into database.
|
---|
457 | - if value is not None:
|
---|
458 | - # MySQL will throw a warning if microseconds are given, because it
|
---|
459 | - # doesn't support microseconds.
|
---|
460 | - if settings.DATABASE_ENGINE == 'mysql':
|
---|
461 | - value = value.replace(microsecond=0)
|
---|
462 | - value = str(value)
|
---|
463 | - return Field.get_db_prep_save(self, value)
|
---|
464 | + # Casts dates into string format for entry into database.
|
---|
465 | + if value is not None:
|
---|
466 | + # MySQL will throw a warning if microseconds are given, because it
|
---|
467 | + # doesn't support microseconds.
|
---|
468 | + if settings.DATABASE_ENGINE == 'mysql':
|
---|
469 | + value = value.replace(microsecond=0)
|
---|
470 | + value = str(value)
|
---|
471 | + # Oracle will choke if microseconds are given...
|
---|
472 | + elif settings.DATABASE_ENGINE == 'oracle':
|
---|
473 | + value = value.replace(microsecond=0)
|
---|
474 | + else:
|
---|
475 | + value = str(value)
|
---|
476 | +
|
---|
477 | + return Field.get_db_prep_save(self, value)
|
---|
478 |
|
---|
479 | def get_manipulator_field_objs(self):
|
---|
480 | return [formfields.DateField, formfields.TimeField]
|
---|
481 | @@ -513,12 +559,21 @@
|
---|
482 | if auto_now or auto_now_add:
|
---|
483 | kwargs['editable'] = False
|
---|
484 | Field.__init__(self, verbose_name, name, **kwargs)
|
---|
485 | -
|
---|
486 | +
|
---|
487 | def get_db_prep_lookup(self, lookup_type, value):
|
---|
488 | - if lookup_type == 'range':
|
---|
489 | - value = [str(v) for v in value]
|
---|
490 | - else:
|
---|
491 | - value = str(value)
|
---|
492 | + # Oracle driver can deal with datetime.time objects natively
|
---|
493 | + # and doesn't need to convert to string
|
---|
494 | + if settings.DATABASE_ENGINE == 'oracle':
|
---|
495 | + if lookup_type == 'range':
|
---|
496 | + value = [v for v in value]
|
---|
497 | + else:
|
---|
498 | + value = value
|
---|
499 | + else:
|
---|
500 | + if lookup_type == 'range':
|
---|
501 | + value = [str(v) for v in value]
|
---|
502 | + else:
|
---|
503 | + value = str(value)
|
---|
504 | +
|
---|
505 | return Field.get_db_prep_lookup(self, lookup_type, value)
|
---|
506 |
|
---|
507 | def pre_save(self, value, add):
|
---|
508 | @@ -529,11 +584,16 @@
|
---|
509 | def get_db_prep_save(self, value):
|
---|
510 | # Casts dates into string format for entry into database.
|
---|
511 | if value is not None:
|
---|
512 | - # MySQL will throw a warning if microseconds are given, because it
|
---|
513 | - # doesn't support microseconds.
|
---|
514 | - if settings.DATABASE_ENGINE == 'mysql':
|
---|
515 | - value = value.replace(microsecond=0)
|
---|
516 | - value = str(value)
|
---|
517 | + # MySQL will throw a warning if microseconds are given, because it
|
---|
518 | + # doesn't support microseconds.
|
---|
519 | + if settings.DATABASE_ENGINE == 'mysql':
|
---|
520 | + value = value.replace(microsecond=0)
|
---|
521 | + value = str(value)
|
---|
522 | + # Oracle will choke if microseconds are given...
|
---|
523 | + elif settings.DATABASE_ENGINE == 'oracle':
|
---|
524 | + value = value.replace(microsecond=0)
|
---|
525 | + else:
|
---|
526 | + value = str(value)
|
---|
527 | return Field.get_db_prep_save(self, value)
|
---|
528 |
|
---|
529 | def get_manipulator_field_objs(self):
|
---|
530 | --- trunk/django/django/core/management.py (revision 656)
|
---|
531 | +++ trunk/django/django/core/management.py (working copy)
|
---|
532 | @@ -20,7 +20,7 @@
|
---|
533 | ADMIN_TEMPLATE_DIR = os.path.join(django.__path__[0], 'conf/admin_templates')
|
---|
534 |
|
---|
535 | def _get_packages_insert(app_label):
|
---|
536 | - return "INSERT INTO packages (label, name) VALUES ('%s', '%s');" % (app_label, app_label)
|
---|
537 | + return "INSERT INTO packages (label, name) VALUES ('%s', '%s')" % (app_label, app_label)
|
---|
538 |
|
---|
539 | def _get_permission_codename(action, opts):
|
---|
540 | return '%s_%s' % (action, opts.object_name.lower())
|
---|
541 | @@ -34,11 +34,11 @@
|
---|
542 | return perms + list(opts.permissions)
|
---|
543 |
|
---|
544 | def _get_permission_insert(name, codename, opts):
|
---|
545 | - return "INSERT INTO auth_permissions (name, package, codename) VALUES ('%s', '%s', '%s');" % \
|
---|
546 | + return "INSERT INTO auth_permissions (name, package, codename) VALUES ('%s', '%s', '%s')" % \
|
---|
547 | (name.replace("'", "''"), opts.app_label, codename)
|
---|
548 |
|
---|
549 | def _get_contenttype_insert(opts):
|
---|
550 | - return "INSERT INTO content_types (name, package, python_module_name) VALUES ('%s', '%s', '%s');" % \
|
---|
551 | + return "INSERT INTO content_types (name, package, python_module_name) VALUES ('%s', '%s', '%s')" % \
|
---|
552 | (opts.verbose_name, opts.app_label, opts.module_name)
|
---|
553 |
|
---|
554 | def _is_valid_dir_name(s):
|
---|
555 | @@ -85,9 +85,26 @@
|
---|
556 | full_statement = ['CREATE TABLE %s (' % opts.db_table]
|
---|
557 | for i, line in enumerate(table_output): # Combine and add commas.
|
---|
558 | full_statement.append(' %s%s' % (line, i < len(table_output)-1 and ',' or ''))
|
---|
559 | - full_statement.append(');')
|
---|
560 | + full_statement.append(')')
|
---|
561 | final_output.append('\n'.join(full_statement))
|
---|
562 | +
|
---|
563 | + if (db.DATABASE_ENGINE == 'oracle') & (opts.has_auto_field):
|
---|
564 | + # To simulate auto-incrementing primary keys in Oracle
|
---|
565 | +
|
---|
566 | + sequence_statement = 'CREATE SEQUENCE %s_sq\n' % opts.db_table
|
---|
567 | + final_output.append(sequence_statement)
|
---|
568 | +
|
---|
569 | + trigger_statement = "" + \
|
---|
570 | + "CREATE OR REPLACE trigger %s_tr\n" % opts.db_table + \
|
---|
571 | + " before insert on %s\n" % opts.db_table + \
|
---|
572 | + " for each row\n" + \
|
---|
573 | + " begin\n" + \
|
---|
574 | + " select %s_sq.NEXTVAL into :new.id from DUAL;\n" % opts.db_table + \
|
---|
575 | + " end;\n"
|
---|
576 | + final_output.append(trigger_statement)
|
---|
577 | +
|
---|
578 |
|
---|
579 | +
|
---|
580 | for klass in mod._MODELS:
|
---|
581 | opts = klass._meta
|
---|
582 | for f in opts.many_to_many:
|
---|
583 | @@ -98,10 +115,24 @@
|
---|
584 | table_output.append(' %s_id %s NOT NULL REFERENCES %s (%s),' % \
|
---|
585 | (f.rel.to.object_name.lower(), db.DATA_TYPES['IntegerField'], f.rel.to.db_table, f.rel.to.pk.column))
|
---|
586 | table_output.append(' UNIQUE (%s_id, %s_id)' % (opts.object_name.lower(), f.rel.to.object_name.lower()))
|
---|
587 | - table_output.append(');')
|
---|
588 | + table_output.append(')')
|
---|
589 | final_output.append('\n'.join(table_output))
|
---|
590 | +
|
---|
591 | + if (db.DATABASE_ENGINE == 'oracle') & (opts.has_auto_field):
|
---|
592 | + sequence_statement = 'CREATE SEQUENCE %s_sq\n' % f.get_m2m_db_table(opts)
|
---|
593 | + final_output.append(sequence_statement)
|
---|
594 | +
|
---|
595 | + trigger_statement = "" + \
|
---|
596 | + "CREATE OR REPLACE trigger %s_tr\n" % f.get_m2m_db_table(opts) + \
|
---|
597 | + " before insert on %s\n" % f.get_m2m_db_table(opts) + \
|
---|
598 | + " for each row\n" + \
|
---|
599 | + " begin\n" + \
|
---|
600 | + " select %s_sq.NEXTVAL into :new.id from DUAL;\n" % f.get_m2m_db_table(opts) + \
|
---|
601 | + " end;\n"
|
---|
602 | + final_output.append(trigger_statement)
|
---|
603 | +
|
---|
604 | return final_output
|
---|
605 | -get_sql_create.help_doc = "Prints the CREATE TABLE SQL statements for the given model module name(s)."
|
---|
606 | +get_sql_create.help_doc = "Prints the CREATE TABLE SQL statements for the given model module name(s)."
|
---|
607 | get_sql_create.args = APP_ARGS
|
---|
608 |
|
---|
609 | def get_sql_delete(mod):
|
---|
610 | @@ -116,23 +147,27 @@
|
---|
611 | try:
|
---|
612 | if cursor is not None:
|
---|
613 | # Check whether the table exists.
|
---|
614 | - cursor.execute("SELECT 1 FROM %s LIMIT 1" % klass._meta.db_table)
|
---|
615 | + cursor.execute("SELECT COUNT(1) FROM %s" % klass._meta.db_table)
|
---|
616 | except:
|
---|
617 | # The table doesn't exist, so it doesn't need to be dropped.
|
---|
618 | db.db.rollback()
|
---|
619 | else:
|
---|
620 | output.append("DROP TABLE %s;" % klass._meta.db_table)
|
---|
621 | + if db.DATABASE_ENGINE == 'oracle':
|
---|
622 | + output.append("DROP SEQUENCE %s_sq" % klass._meta.db_table)
|
---|
623 | +
|
---|
624 | for klass in mod._MODELS:
|
---|
625 | opts = klass._meta
|
---|
626 | for f in opts.many_to_many:
|
---|
627 | try:
|
---|
628 | if cursor is not None:
|
---|
629 | - cursor.execute("SELECT 1 FROM %s LIMIT 1" % f.get_m2m_db_table(opts))
|
---|
630 | + cursor.execute("SELECT COUNT(1) FROM %s" % f.get_m2m_db_table(opts))
|
---|
631 | except:
|
---|
632 | db.db.rollback()
|
---|
633 | else:
|
---|
634 | output.append("DROP TABLE %s;" % f.get_m2m_db_table(opts))
|
---|
635 | -
|
---|
636 | + if db.DATABASE_ENGINE == 'oracle':
|
---|
637 | + output.append("DROP SEQUENCE %s_sq" % f.get_m2m_db_table(opts))
|
---|
638 | app_label = mod._MODELS[0]._meta.app_label
|
---|
639 |
|
---|
640 | # Delete from packages, auth_permissions, content_types.
|
---|
641 | @@ -181,12 +216,15 @@
|
---|
642 |
|
---|
643 | def get_sql_sequence_reset(mod):
|
---|
644 | "Returns a list of the SQL statements to reset PostgreSQL sequences for the given module."
|
---|
645 | - from django.core import meta
|
---|
646 | + from django.core import db, meta
|
---|
647 | output = []
|
---|
648 | for klass in mod._MODELS:
|
---|
649 | for f in klass._meta.fields:
|
---|
650 | - if isinstance(f, meta.AutoField):
|
---|
651 | - output.append("SELECT setval('%s_%s_seq', (SELECT max(%s) FROM %s));" % (klass._meta.db_table, f.column, f.column, klass._meta.db_table))
|
---|
652 | + if isinstance(f, meta.AutoField):
|
---|
653 | + if db.DATABASE_ENGINE == 'postgresql':
|
---|
654 | + output.append("SELECT setval('%s_%s_seq', (SELECT max(%s) FROM %s));" % (klass._meta.db_table, f.column, f.column, klass._meta.db_table))
|
---|
655 | + else:
|
---|
656 | + raise "Not implemented yet for %s!" % (db.DATABASE_ENGINE)
|
---|
657 | return output
|
---|
658 | get_sql_sequence_reset.help_doc = "Prints the SQL statements for resetting PostgreSQL sequences for the given model module name(s)."
|
---|
659 | get_sql_sequence_reset.args = APP_ARGS
|
---|
660 | --- trunk/django/django/middleware/sessions.py (revision 656)
|
---|
661 | +++ trunk/django/django/middleware/sessions.py (working copy)
|
---|
662 | @@ -1,4 +1,4 @@
|
---|
663 | -from django.conf.settings import SESSION_COOKIE_NAME, SESSION_COOKIE_AGE, SESSION_COOKIE_DOMAIN
|
---|
664 | +from django.conf.settings import SESSION_COOKIE_NAME, SESSION_COOKIE_AGE, SESSION_COOKIE_DOMAIN, DATABASE_ENGINE
|
---|
665 | from django.models.core import sessions
|
---|
666 | import datetime
|
---|
667 |
|
---|
668 | @@ -64,8 +64,12 @@
|
---|
669 | modified = False
|
---|
670 | if modified:
|
---|
671 | session_key = request.session.session_key or sessions.get_new_session_key()
|
---|
672 | + expire_date = datetime.datetime.now()
|
---|
673 | + #Oracle and Mysql can't deal with microseconds
|
---|
674 | + if DATABASE_ENGINE in ['oracle','mysql']:
|
---|
675 | + expire_date = expire_date.replace(microsecond=0)
|
---|
676 | new_session = sessions.save(session_key, request.session._session,
|
---|
677 | - datetime.datetime.now() + datetime.timedelta(seconds=SESSION_COOKIE_AGE))
|
---|
678 | + expire_date + datetime.timedelta(seconds=SESSION_COOKIE_AGE))
|
---|
679 | # TODO: Accept variable session length and domain.
|
---|
680 | response.set_cookie(SESSION_COOKIE_NAME, session_key,
|
---|
681 | max_age=SESSION_COOKIE_AGE, domain=SESSION_COOKIE_DOMAIN)
|
---|