Django

Code

Ticket #87: sqlite3.py

File sqlite3.py, 6.0 kB (added by Jason Huggins, 3 years ago)
Line 
1 """
2 SQLite3 backend for django.  Requires pysqlite2 (http://pysqlite.org/).
3 """
4
5 from django.core.db import base, typecasts
6 from django.core.db.dicthelpers import *
7 from pysqlite2 import dbapi2 as Database
8 DatabaseError = Database.DatabaseError
9
10 # Register adaptors ###########################################################
11
12 Database.register_converter("bool", lambda s: str(s) == '1')
13 Database.register_converter("time", typecasts.typecast_time)
14 Database.register_converter("date", typecasts.typecast_date)
15 Database.register_converter("datetime", typecasts.typecast_timestamp)
16
17 # Database wrapper ############################################################
18
19 def utf8rowFactory(cursor, row):
20     def utf8(s):
21         if type(s) == unicode:
22             return s.encode("utf-8")
23         else:
24             return s
25     return [utf8(r) for r in row]
26
27 class DatabaseWrapper:
28     def __init__(self):
29         self.connection = None
30         self.queries = []
31
32     def cursor(self):
33         from django.conf.settings import DATABASE_NAME, DEBUG
34         if self.connection is None:
35             self.connection = Database.connect(DATABASE_NAME, detect_types=Database.PARSE_DECLTYPES)
36             # register extract and date_trun functions
37             self.connection.create_function("django_extract", 2, _sqlite_extract)
38             self.connection.create_function("django_date_trunc", 2, _sqlite_date_trunc)
39         cursor = self.connection.cursor(factory=SQLiteCursorWrapper)
40         cursor.row_factory = utf8rowFactory
41         if DEBUG:
42             return base.CursorDebugWrapper(cursor, self)
43         else:
44             return cursor
45
46     def commit(self):
47         self.connection.commit()
48
49     def rollback(self):
50         if self.connection:
51             self.connection.rollback()
52
53     def close(self):
54         if self.connection is not None:
55             self.connection.close()
56             self.connection = None
57
58 class SQLiteCursorWrapper(Database.Cursor):
59     """
60     Django uses "format" style placeholders, but pysqlite2 uses "qmark" style.
61     This fixes it -- but note that if you want to use a literal "%s" in a query,
62     you'll need to use "%%s" (which I belive is true of other wrappers as well).
63     """
64
65     def execute(self, query, params=[]):
66         query = self.convert_query(query, len(params))
67         return Database.Cursor.execute(self, query, params)
68
69     def executemany(self, query, params=[]):
70         query = self.convert_query(query, len(params[0]))
71         return Database.Cursor.executemany(self, query, params)
72
73     def convert_query(self, query, num_params):
74         # XXX this seems too simple to be correct... is this right?
75         return query % tuple("?" * num_params)
76
77 # Helper functions ############################################################
78
79 def get_last_insert_id(cursor, table_name, pk_name):
80     return cursor.lastrowid
81
82 def get_date_extract_sql(lookup_type, table_name):
83     # lookup_type is 'year', 'month', 'day'
84     # sqlite doesn't support extract, so we fake it with the user-defined
85     # function _sqlite_extract that's registered in connect(), above.
86     return 'django_extract("%s", %s)' % (lookup_type.lower(), table_name)
87
88 def _sqlite_extract(lookup_type, dt):
89     try:
90         dt = typecasts.typecast_timestamp(dt)
91     except (ValueError, TypeError):
92         return None
93     return str(getattr(dt, lookup_type))
94
95 def get_date_trunc_sql(lookup_type, field_name):
96     # lookup_type is 'year', 'month', 'day'
97     # sqlite doesn't support DATE_TRUNC, so we fake it as above.
98     return 'django_date_trunc("%s", %s)' % (lookup_type.lower(), field_name)
99
100 def _sqlite_date_trunc(lookup_type, dt):
101     try:
102         dt = typecasts.typecast_timestamp(dt)
103     except (ValueError, TypeError):
104         return None
105     if lookup_type == 'year':
106         return "%i-01-01 00:00:00" % dt.year
107     elif lookup_type == 'month':
108         return "%i-%02i-01 00:00:00" % (dt.year, dt.month)
109     elif lookup_type == 'day':
110         return "%i-%02i-%02i 00:00:00" % (dt.year, dt.month, dt.day)
111
112 def get_table_list(cursor):
113     raise NotImplementedError
114
115 def get_relations(cursor, table_name):
116     raise NotImplementedError
117
118 # Operators and fields ########################################################
119
120 OPERATOR_MAPPING = {
121     'exact':        '=',
122     'iexact':       'LIKE',
123     'contains':     'LIKE',
124     'icontains':    'LIKE',
125     'ne':           '!=',
126     'gt':           '>',
127     'gte':          '>=',
128     'lt':           '<',
129     'lte':          '<=',
130     'startswith':   'LIKE',
131     'endswith':     'LIKE',
132     'istartswith':  'LIKE',
133     'iendswith':    'LIKE',
134 }
135
136 # SQLite doesn't actually support most of these types, but it "does the right
137 # thing" given more verbose field definitions, so leave them as is so that
138 # schema inspection is more useful.
139 DATA_TYPES = {
140     'AutoField':                    'integer',
141     'BooleanField':                 'bool',
142     'CharField':                    'varchar(%(maxlength)s)',
143     'CommaSeparatedIntegerField':   'varchar(%(maxlength)s)',
144     'DateField':                    'date',
145     'DateTimeField':                'datetime',
146     'EmailField':                   'varchar(75)',
147     'FileField':                    'varchar(100)',
148     'FloatField':                   'numeric(%(max_digits)s, %(decimal_places)s)',
149     'ImageField':                   'varchar(100)',
150     'IntegerField':                 'integer',
151     'IPAddressField':               'char(15)',
152     'ManyToManyField':              None,
153     'NullBooleanField':             'bool',
154     'OneToOneField':                'integer',
155     'PhoneNumberField':             'varchar(20)',
156     'PositiveIntegerField':         'integer unsigned',
157     'PositiveSmallIntegerField':    'smallint unsigned',
158     'SlugField':                    'varchar(50)',
159     'SmallIntegerField':            'smallint',
160     'TextField':                    'text',
161     'TimeField':                    'time',
162     'URLField':                     'varchar(200)',
163     'USStateField':                 'varchar(2)',
164     'XMLField':                     'text',
165 }
166
167 DATA_TYPES_REVERSE = {}
168
169 EMPTY_STR_EQUIV = ''