Skip to content
Browse files

Added sqlite3 database backend -- somewhat tested, but probably not 1…

…00% perfect.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@288 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information...
1 parent dbfb35b commit e320a0936e4c846e7332bcacc533dda19dad6783 @jacobian jacobian committed
Showing with 205 additions and 44 deletions.
  1. +5 −5 django/conf/project_template/settings/main.py
  2. +148 −0 django/core/db/backends/sqlite3.py
  3. +3 −2 docs/faq.txt
  4. +49 −37 docs/tutorial01.txt
View
10 django/conf/project_template/settings/main.py
@@ -10,11 +10,11 @@
LANGUAGE_CODE = 'en-us'
-DATABASE_ENGINE = 'postgresql' # 'postgresql' or 'mysql'
-DATABASE_NAME = ''
-DATABASE_USER = ''
-DATABASE_PASSWORD = ''
-DATABASE_HOST = '' # Set to empty string for localhost
+DATABASE_ENGINE = 'postgresql' # 'postgresql', 'mysql', or 'sqlite'
+DATABASE_NAME = '' # or path to database file if using sqlite
+DATABASE_USER = '' # not used with sqlite
+DATABASE_PASSWORD = '' # not used with sqlite
+DATABASE_HOST = '' # Set to empty string for localhost; not used with sqlite
SITE_ID = 1
View
148 django/core/db/backends/sqlite3.py
@@ -0,0 +1,148 @@
+"""
+SQLite3 backend for django. Requires pysqlite2 (http://pysqlite.org/).
+"""
+
+from django.core.db import base, typecasts
+from django.core.db.dicthelpers import *
+from pysqlite2 import dbapi2 as Database
+DatabaseError = Database.DatabaseError
+
+# Register adaptors ###########################################################
+
+Database.register_converter("bool", lambda s: str(s) == '1')
+Database.register_converter("time", typecasts.typecast_time)
+Database.register_converter("date", typecasts.typecast_date)
+Database.register_converter("datetime", typecasts.typecast_timestamp)
+
+# Database wrapper ############################################################
+
+class DatabaseWrapper:
+ def __init__(self):
+ self.connection = None
+ self.queries = []
+
+ def cursor(self):
+ from django.conf.settings import DATABASE_NAME, DEBUG
+ if self.connection is None:
+ self.connection = Database.connect(DATABASE_NAME, detect_types=Database.PARSE_DECLTYPES)
+ # register extract and date_trun functions
+ self.connection.create_function("django_extract", 2, _sqlite_extract)
+ self.connection.create_function("django_date_trunc", 2, _sqlite_date_trunc)
+ if DEBUG:
+ return base.CursorDebugWrapper(FormatStylePlaceholderCursor(self.connection), self)
+ return FormatStylePlaceholderCursor(self.connection)
+
+ def commit(self):
+ self.connection.commit()
+
+ def rollback(self):
+ if self.connection:
+ self.connection.rollback()
+
+ def close(self):
+ if self.connection is not None:
+ self.connection.close()
+ self.connection = None
+
+class FormatStylePlaceholderCursor(Database.Cursor):
+ """
+ Django uses "format" style placeholders, but pysqlite2 uses "qmark" style.
+ This fixes it -- but note that if you want to use a literal "%s" in a query,
+ you'll need to use "%%s" (which I belive is true of other wrappers as well).
+ """
+
+ def execute(self, query, params=[]):
+ query = self.convert_query(query, len(params))
+ return Database.Cursor.execute(self, query, params)
+
+ def executemany(self, query, params=[]):
+ query = self.convert_query(query, len(params))
+ return Database.Cursor.executemany(self, query, params)
+
+ def convert_query(self, query, num_params):
+ # XXX this seems too simple to be correct... is this right?
+ return query % tuple("?" * num_params)
+
+# Helper functions ############################################################
+
+def get_last_insert_id(cursor, table_name, pk_name):
+ return cursor.lastrowid
+
+def get_date_extract_sql(lookup_type, table_name):
+ # lookup_type is 'year', 'month', 'day'
+ # sqlite doesn't support extract, so we fake it with the user-defined
+ # function _sqlite_extract that's registered in connect(), above.
+ return 'django_extract("%s", %s)' % (lookup_type.lower(), table_name)
+
+def _sqlite_extract(lookup_type, dt):
+ try:
+ dt = typecasts.typecast_timestamp(dt)
+ except (ValueError, TypeError):
+ return None
+ return str(getattr(dt, lookup_type))
+
+def get_date_trunc_sql(lookup_type, field_name):
+ # lookup_type is 'year', 'month', 'day'
+ # sqlite doesn't support DATE_TRUNC, so we fake it as above.
+ return 'django_date_trunc("%s", %s)' % (lookup_type.lower(), field_name)
+
+def _sqlite_date_trunc(lookup_type, dt):
+ try:
+ dt = typecasts.typecast_timestamp(dt)
+ except (ValueError, TypeError):
+ return None
+ if lookup_type == 'year':
+ return "%i-01-01 00:00:00" % dt.year
+ elif lookup_type == 'month':
+ return "%i-%02i-01 00:00:00" % (dt.year, dt.month)
+ elif lookup_type == 'day':
+ return "%i-%02i-%02i 00:00:00" % (dt.year, dt.month, dt.day)
+
+# Operators and fields ########################################################
+
+OPERATOR_MAPPING = {
+ 'exact': '=',
+ 'iexact': 'LIKE',
+ 'contains': 'LIKE',
+ 'icontains': 'LIKE',
+ 'ne': '!=',
+ 'gt': '>',
+ 'gte': '>=',
+ 'lt': '<',
+ 'lte': '<=',
+ 'startswith': 'LIKE',
+ 'endswith': 'LIKE',
+ 'istartswith': 'LIKE',
+ 'iendswith': 'LIKE',
+}
+
+# SQLite doesn't actually support most of these types, but it "does the right
+# thing" given more verbose field definitions, so leave them as is so that
+# schema inspection is more useful.
+DATA_TYPES = {
+ 'AutoField': 'integer',
+ 'BooleanField': 'bool',
+ 'CharField': 'varchar(%(maxlength)s)',
+ 'CommaSeparatedIntegerField': 'varchar(%(maxlength)s)',
+ 'DateField': 'date',
+ 'DateTimeField': 'datetime',
+ 'EmailField': 'varchar(75)',
+ 'FileField': 'varchar(100)',
+ 'FloatField': 'numeric(%(max_digits)s, %(decimal_places)s)',
+ 'ImageField': 'varchar(100)',
+ 'IntegerField': 'integer',
+ 'IPAddressField': 'char(15)',
+ 'ManyToManyField': None,
+ 'NullBooleanField': 'bool',
+ 'OneToOneField': 'integer',
+ 'PhoneNumberField': 'varchar(20)',
+ 'PositiveIntegerField': 'integer unsigned',
+ 'PositiveSmallIntegerField': 'smallint unsigned',
+ 'SlugField': 'varchar(50)',
+ 'SmallIntegerField': 'smallint',
+ 'TextField': 'text',
+ 'TimeField': 'time',
+ 'URLField': 'varchar(200)',
+ 'USStateField': 'varchar(2)',
+ 'XMLField': 'text',
+}
View
5 docs/faq.txt
@@ -144,8 +144,8 @@ own lightweight development server. For a production environment, we recommend
`Apache 2`_ and mod_python_, although Django follows the WSGI_ spec, which
means it can run on a variety of server platforms.
-You'll also need a database engine. PostgreSQL_ is recommended, and MySQL_ is
-supported.
+You'll also need a database engine. PostgreSQL_ is recommended, and MySQL_
+and `SQLite 3`_ are supported.
.. _Python: http://www.python.org/
.. _Apache 2: http://httpd.apache.org/
@@ -153,6 +153,7 @@ supported.
.. _WSGI: http://www.python.org/peps/pep-0333.html
.. _PostgreSQL: http://www.postgresql.org/
.. _MySQL: http://www.mysql.com/
+.. _`SQLite 3`: http://www.sqlite.org/
Do I have to use mod_python?
----------------------------
View
86 docs/tutorial01.txt
@@ -49,23 +49,27 @@ settings. Let's look at what ``startproject`` created::
First, edit ``myproject/settings/main.py``. It's a normal Python module with
module-level variables representing Django settings. Edit the file and change
these settings to match your database's connection parameters:
-
-* ``DATABASE_ENGINE`` -- Either 'postgresql' or 'mysql'. More coming soon.
-* ``DATABASE_NAME`` -- The name of your database.
-* ``DATABASE_USER`` -- Your database username.
-* ``DATABASE_PASSWORD`` -- Your database password.
-* ``DATABASE_HOST`` -- The host your database is on. Leave this as an
- empty string if your database server is on the same physical machine
- (localhost).
-
-(Make sure you've created a database within PostgreSQL or MySQL by this point.
-Do that with "``CREATE DATABASE database_name;``" within your database's
-interactive prompt.)
-
-Also, note that MySQL support is a recent development, and Django hasn't been
-comprehensively tested with that database. If you find any bugs in Django's
-MySQL bindings, please file them in `Django's ticket system`_ so we can fix them
-immediately.
+
+ * ``DATABASE_ENGINE`` -- Either 'postgresql', 'mysql' or 'sqlite3'.
+ More coming soon.
+ * ``DATABASE_NAME`` -- The name of your database, or the full path to
+ the database file if using sqlite.
+ * ``DATABASE_USER`` -- Your database username (not used for sqlite).
+ * ``DATABASE_PASSWORD`` -- Your database password (not used for sqlite).
+ * ``DATABASE_HOST`` -- The host your database is on. Leave this as an
+ empty string if your database server is on the same physical machine
+ (not used for sqlite).
+
+.. admonition:: Note
+
+ Make sure you've created a database within PostgreSQL or MySQL by this
+ point. Do that with "``CREATE DATABASE database_name;``" within your
+ database's interactive prompt.
+
+ Also, note that MySQL and sqlite support is a recent development, and Django
+ hasn't been comprehensively tested with either database. If you find any
+ bugs in those bindings, please file them in `Django's ticket system`_ so we
+ can fix them immediately.
Now, take a second to make sure ``myproject`` is on your Python path. You
can do this by copying ``myproject`` to Python's ``site-packages`` directory,
@@ -90,8 +94,9 @@ On Windows, you'd use ``set`` instead::
If you don't see any errors after running ``django-admin.py init``, you know it
worked. That command initialized your database with Django's core database
-tables. If you're interested, run the PostgreSQL or MySQL command-line client
-and type "\\dt" (PostgreSQL) or "SHOW TABLES;" (MySQL) to display the tables.
+tables. If you're interested, run the command-line client for your database and
+type ``\\dt`` (PostgreSQL), ``SHOW TABLES;`` (MySQL), or ``.schema`` (SQLite) to
+display the tables.
Now you're set to start doing work. You won't have to take care of this boring
administrative stuff again.
@@ -235,27 +240,34 @@ You should see the following (the CREATE TABLE SQL statements for the polls app)
Note the following:
-* Table names are automatically generated by combining the name of the app
- (polls) with a plural version of the object name (polls and choices). (You
- can override this behavior.)
-* Primary keys (IDs) are added automatically. (You can override this, too.)
-* The foreign key relationship is made explicit by a ``REFERENCES`` statement.
-* It's tailored to the database you're using, so database-specific field types
- such as ``auto_increment`` (MySQL) vs. ``serial`` (PostgreSQL) are handled
- for you automatically. The author of this tutorial runs PostgreSQL, so the
- example output is in PostgreSQL syntax.
+ * Table names are automatically generated by combining the name of the app
+ (polls) with a plural version of the object name (polls and choices). (You
+ can override this behavior.)
+
+ * Primary keys (IDs) are added automatically. (You can override this, too.)
+
+ * The foreign key relationship is made explicit by a ``REFERENCES`` statement.
+
+ * It's tailored to the database you're using, so database-specific field types
+ such as ``auto_increment`` (MySQL), ``serial`` (PostgreSQL), or ``integer
+ primary key`` (SQLite) are handled for you automatically. The author of
+ this tutorial runs PostgreSQL, so the example output is in PostgreSQL
+ syntax.
If you're interested, also run the following commands:
-* ``django-admin.py sqlinitialdata polls`` -- Outputs the initial-data inserts
- required for Django's admin framework.
-* ``django-admin.py sqlclear polls`` -- Outputs the necessary ``DROP TABLE``
- statements for this app, according to which tables already exist in your
- database (if any).
-* ``django-admin.py sqlindexes polls`` -- Outputs the ``CREATE INDEX``
- statements for this app.
-* ``django-admin.py sqlall polls`` -- A combination of 'sql' and
- 'sqlinitialdata'.
+ * ``django-admin.py sqlinitialdata polls`` -- Outputs the initial-data
+ inserts required for Django's admin framework.
+
+ * ``django-admin.py sqlclear polls`` -- Outputs the necessary ``DROP
+ TABLE`` statements for this app, according to which tables already exist
+ in your database (if any).
+
+ * ``django-admin.py sqlindexes polls`` -- Outputs the ``CREATE INDEX``
+ statements for this app.
+
+ * ``django-admin.py sqlall polls`` -- A combination of 'sql' and
+ 'sqlinitialdata'.
Looking at the output of those commands can help you understand what's actually
happening under the hood.

0 comments on commit e320a09

Please sign in to comment.
Something went wrong with that request. Please try again.