Added sqlite3 database backend -- somewhat tested, but probably not 100% perfect.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@288 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Jacob Kaplan-Moss 2005-07-22 03:15:43 +00:00
parent dbfb35b542
commit e320a0936e
4 changed files with 202 additions and 41 deletions

View File

@ -10,11 +10,11 @@ MANAGERS = ADMINS
LANGUAGE_CODE = 'en-us' LANGUAGE_CODE = 'en-us'
DATABASE_ENGINE = 'postgresql' # 'postgresql' or 'mysql' DATABASE_ENGINE = 'postgresql' # 'postgresql', 'mysql', or 'sqlite'
DATABASE_NAME = '' DATABASE_NAME = '' # or path to database file if using sqlite
DATABASE_USER = '' DATABASE_USER = '' # not used with sqlite
DATABASE_PASSWORD = '' DATABASE_PASSWORD = '' # not used with sqlite
DATABASE_HOST = '' # Set to empty string for localhost DATABASE_HOST = '' # Set to empty string for localhost; not used with sqlite
SITE_ID = 1 SITE_ID = 1

View File

@ -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 File

@ -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 `Apache 2`_ and mod_python_, although Django follows the WSGI_ spec, which
means it can run on a variety of server platforms. means it can run on a variety of server platforms.
You'll also need a database engine. PostgreSQL_ is recommended, and MySQL_ is You'll also need a database engine. PostgreSQL_ is recommended, and MySQL_
supported. and `SQLite 3`_ are supported.
.. _Python: http://www.python.org/ .. _Python: http://www.python.org/
.. _Apache 2: http://httpd.apache.org/ .. _Apache 2: http://httpd.apache.org/
@ -153,6 +153,7 @@ supported.
.. _WSGI: http://www.python.org/peps/pep-0333.html .. _WSGI: http://www.python.org/peps/pep-0333.html
.. _PostgreSQL: http://www.postgresql.org/ .. _PostgreSQL: http://www.postgresql.org/
.. _MySQL: http://www.mysql.com/ .. _MySQL: http://www.mysql.com/
.. _`SQLite 3`: http://www.sqlite.org/
Do I have to use mod_python? Do I have to use mod_python?
---------------------------- ----------------------------

View File

@ -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 First, edit ``myproject/settings/main.py``. It's a normal Python module with
module-level variables representing Django settings. Edit the file and change module-level variables representing Django settings. Edit the file and change
these settings to match your database's connection parameters: these settings to match your database's connection parameters:
* ``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).
* ``DATABASE_ENGINE`` -- Either 'postgresql' or 'mysql'. More coming soon. .. admonition:: Note
* ``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. Make sure you've created a database within PostgreSQL or MySQL by this
Do that with "``CREATE DATABASE database_name;``" within your database's point. Do that with "``CREATE DATABASE database_name;``" within your
interactive prompt.) database's interactive prompt.
Also, note that MySQL support is a recent development, and Django hasn't been Also, note that MySQL and sqlite support is a recent development, and Django
comprehensively tested with that database. If you find any bugs in Django's hasn't been comprehensively tested with either database. If you find any
MySQL bindings, please file them in `Django's ticket system`_ so we can fix them bugs in those bindings, please file them in `Django's ticket system`_ so we
immediately. can fix them immediately.
Now, take a second to make sure ``myproject`` is on your Python path. You 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, 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 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 worked. That command initialized your database with Django's core database
tables. If you're interested, run the PostgreSQL or MySQL command-line client tables. If you're interested, run the command-line client for your database and
and type "\\dt" (PostgreSQL) or "SHOW TABLES;" (MySQL) to display the tables. 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 Now you're set to start doing work. You won't have to take care of this boring
administrative stuff again. administrative stuff again.
@ -235,27 +240,34 @@ You should see the following (the CREATE TABLE SQL statements for the polls app)
Note the following: Note the following:
* Table names are automatically generated by combining the name of the app * 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 (polls) with a plural version of the object name (polls and choices). (You
can override this behavior.) 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. * Primary keys (IDs) are added automatically. (You can override this, too.)
* It's tailored to the database you're using, so database-specific field types
such as ``auto_increment`` (MySQL) vs. ``serial`` (PostgreSQL) are handled * The foreign key relationship is made explicit by a ``REFERENCES`` statement.
for you automatically. The author of this tutorial runs PostgreSQL, so the
example output is in PostgreSQL syntax. * 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: If you're interested, also run the following commands:
* ``django-admin.py sqlinitialdata polls`` -- Outputs the initial-data inserts * ``django-admin.py sqlinitialdata polls`` -- Outputs the initial-data
required for Django's admin framework. 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 * ``django-admin.py sqlclear polls`` -- Outputs the necessary ``DROP
database (if any). TABLE`` statements for this app, according to which tables already exist
* ``django-admin.py sqlindexes polls`` -- Outputs the ``CREATE INDEX`` in your database (if any).
statements for this app.
* ``django-admin.py sqlall polls`` -- A combination of 'sql' and * ``django-admin.py sqlindexes polls`` -- Outputs the ``CREATE INDEX``
'sqlinitialdata'. 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 Looking at the output of those commands can help you understand what's actually
happening under the hood. happening under the hood.