Ticket #10096: added linter backend.diff
File added linter backend.diff, 26.9 KB (added by , 16 years ago) |
---|
-
django/db/backends/linter/base.py
1 """ 2 LINTER database backend for Django. 3 """ 4 from django.db.backends import * 5 6 from django.db.backends.linter import query 7 from django.db.backends.linter.client import DatabaseClient 8 from django.db.backends.linter.creation import DatabaseCreation 9 from django.db.backends.linter.introspection import DatabaseIntrospection 10 from django.utils.encoding import smart_str, force_unicode 11 12 try: 13 import LinPy as Database 14 except ImportError, e: 15 from django.core.exceptions import ImproperlyConfigured 16 raise ImproperlyConfigured, "Error loading LinPy module: %s" % e 17 18 DatabaseError = Database.Error 19 IntegrityError = Database.IntegrityError 20 21 class DatabaseFeatures(BaseDatabaseFeatures): 22 uses_custom_query_class = True 23 24 class DatabaseOperations(BaseDatabaseOperations): 25 26 _lookup_types_dict = {'day' : 'D', 'month' : 'M', 'year' : 'Y'} 27 28 def date_extract_sql(self, lookup_type, table_name): 29 return "DATESPLIT (%s, '%s')" % (table_name, self._lookup_types_dict[lookup_type]) 30 31 def date_trunc_sql(self, lookup_type, field_name): 32 return "TRUNC(%s, '%s')" % (field_name, self._lookup_types_dict[lookup_type]) 33 34 def drop_foreignkey_sql(self): 35 return "DROP FOREIGN KEY" 36 37 def fulltext_search_sql(self, field_name): 38 return "%s CONTAINS '%%s'" % field_name 39 40 def last_executed_query(self, cursor, sql, params): 41 """ 42 Returns a string of the query last executed by the given cursor, with 43 placeholders replaced with actual values. 44 """ 45 from django.utils.encoding import smart_unicode, force_unicode 46 47 if not params: 48 return smart_unicode(sql) 49 50 # Convert params to contain Unicode values. 51 to_unicode = lambda s: force_unicode(s, strings_only=True) 52 if isinstance(params, (list, tuple)): 53 u_params = tuple([to_unicode(val) for val in params]) 54 else: 55 u_params = dict([(to_unicode(k), to_unicode(v)) for k, v in params.items()]) 56 57 return smart_unicode(sql) % u_params 58 59 def last_insert_id(self, cursor, table_name, pk_name): 60 query = "SELECT %s FROM %s WHERE ROWID = LAST_ROWID" % (self.quote_name(pk_name), self.quote_name(table_name)) # 23.09. 61 cursor.execute(query) 62 return cursor.fetchone()[0] 63 64 def lookup_cast(self, lookup_type): 65 if lookup_type in ('iexact', 'icontains', 'iregex', 'istartswith', 'iendswith'): 66 return "UPPER(%s)" 67 return "%s" 68 69 def no_limit_value(self): 70 return -1 71 72 def query_class(self, DefaultQueryClass): 73 return query.query_class(DefaultQueryClass, Database) 74 75 def quote_name(self, name): 76 if name.startswith('"') and name.endswith('"'): 77 return name # Quoting once is enough. 78 return '"%s"' % name.upper() 79 80 def random_function_sql(self): 81 return "RAND()" 82 83 def sql_flush(self, style, tables, sequences): 84 # Return a list of 'TRUNCATE x;', 'TRUNCATE y;', 85 # 'TRUNCATE z;'... style SQL statements 86 87 sql = ['%s %s %s;' % \ 88 (style.SQL_KEYWORD('TRUNCATE'), 89 style.SQL_KEYWORD('TABLE'), 90 style.SQL_FIELD(self.quote_name(table)) 91 ) for table in tables] 92 return sql 93 94 class DatabaseWrapper(BaseDatabaseWrapper): 95 96 operators = { 97 'exact': '= %s', 98 'iexact': '= UPPER(%s)', 99 'contains': 'LIKE %s', 100 'icontains': 'LIKE UPPER(%s)', 101 'regex': 'SIMILAR TO %s', 102 'iregex': 'SIMILAR TO UPPER(%s)', 103 'gt': '> %s', 104 'gte': '>= %s', 105 'lt': '< %s', 106 'lte': '<= %s', 107 'startswith': 'LIKE %s', 108 'endswith': 'LIKE %s', 109 'istartswith': 'LIKE UPPER(%s)', 110 'iendswith': 'LIKE UPPER(%s)', 111 } 112 113 def __init__(self, **kwargs): 114 super(DatabaseWrapper, self).__init__(**kwargs) 115 116 self.features = DatabaseFeatures() 117 self.ops = DatabaseOperations() 118 self.client = DatabaseClient() 119 self.creation = DatabaseCreation(self) 120 self.introspection = DatabaseIntrospection(self) 121 self.validation = BaseDatabaseValidation() 122 123 def _valid_connection(self): 124 return self.connection is not None 125 126 def _cursor(self, settings): 127 # SetUnicodeData(1) - set output data mode in unicode 128 Database.SetUnicodeData(1) 129 from django.conf import settings 130 131 if not self._valid_connection(): 132 self.connection = Database.connect(settings.DATABASE_USER, settings.DATABASE_PASSWORD, settings.DATABASE_NAME, **self.options) 133 cursor = self.connection.cursor(mode=Database.M_EXCLUSIVE) 134 cursor = LinterCursorWrapper(cursor) 135 return cursor 136 137 def _commit(self): 138 if self.connection is not None: 139 self.connection.commit() 140 141 def _rollback(self): 142 if self.connection is not None: 143 try: 144 self.connection.rollback() 145 except Database.NotSupportedError: 146 pass 147 148 def set_date_emulation(self, value=0): 149 # Set mode conversion values datetime type to date type, 150 # if data contain values only for year, month and day. 151 # By default always returned data in datetime type. 152 Database.SetDateEmulation(value) 153 154 class LinterCursorWrapper(object): 155 """ 156 Django uses "format" style placeholders, but LinPy uses "qmark" style. 157 This fixes it -- but note that if you want to use a literal "%s" in a query, 158 you'll need to use "%%s". 159 """ 160 def __init__(self, cursor): 161 self.cursor = cursor 162 163 def __getattr__(self, attr): 164 if self.__dict__.has_key(attr): 165 return self.__dict__[attr] 166 else: 167 return getattr(self.cursor, attr) 168 169 def execute(self, query, params=()): 170 query = self.convert_query(query, len(params)) 171 172 try: 173 return self.cursor.execute(query, params) 174 except DatabaseError, e: 175 if type(e) != IntegrityError: 176 e = IntegrityError(e) 177 raise e 178 179 def executemany(self, query, param_list): 180 try: 181 query = self.convert_query(query, len(param_list[0])) 182 return self.cursor.executemany(query, param_list) 183 except (IndexError,TypeError): 184 # No parameter list provided 185 return None 186 187 def convert_query(self, query, num_params): 188 if num_params: 189 return smart_str(query % tuple("?" * num_params)) 190 else: 191 return smart_str(query) -
django/db/backends/linter/client.py
1 from django.db.backends import BaseDatabaseClient 2 from django.conf import settings 3 import os 4 5 class DatabaseClient(BaseDatabaseClient): 6 def runshell(self): 7 args = [''] 8 if settings.DATABASE_PASSWORD: 9 args = ["-u %s/%s" % (settings.DATABASE_USER, settings.DATABASE_PASSWORD)] 10 else: 11 args = ["-u %s/" % settings.DATABASE_USER] 12 args += [" -n %s" % settings.DATABASE_NAME] 13 os.execvp('inl', args) 14 15 -
django/db/backends/linter/introspection.py
1 from django.db.backends import BaseDatabaseIntrospection 2 import string 3 4 class DatabaseIntrospection(BaseDatabaseIntrospection): 5 # Maps type codes to Django Field types. 6 data_types_reverse = { 7 'CHAR': 'CharField', 8 'NCHAR': 'CharField', 9 'VARCHAR': 'CharField', 10 'NACHAR VARYING': 'CharField', 11 'SMALLINT': 'SmallIntegerField', 12 'INTEGER': 'IntegerField', 13 'BIGINT': 'IntegerField', 14 'REAL': 'FloatField', 15 'DOUBLE': 'FloatField', 16 'NUMERIC': 'FloatField', 17 'BOOLEAN': 'BooleanField', 18 'DATE': 'DateField', 19 'DATE': 'DateFieldWrapper', 20 'BLOB': 'TextField', 21 'EXTFILE': 'CharField', 22 } 23 24 def get_table_list(self, cursor): 25 "Returns a list of table names in the current database." 26 cursor.execute(""" 27 SELECT TABLE_NAME 28 FROM LINTER_SYSTEM_USER.TABLES 29 WHERE TABLE_SCHEM = USER AND TABLE_TYPE ='TABLE' 30 AND TABLE_NAME NOT LIKE '$$$%' AND TABLE_NAME NOT LIKE 'L\_%' 31 AND TABLE_NAME NOT IN ('SERVERS','PRIV_TYPES','TYPEINFO','COUNTER6', 32 'COUNTER8','COUNTER31','PROVIDER_TYPES','ERRORS')""") 33 return ([string.lower(row[0]) for row in cursor.fetchall()]) 34 35 def get_table_description(self, cursor, table_name): 36 qn = self.connection.ops.quote_name 37 cursor.execute("SELECT * FROM %s LIMIT 1" % qn(table_name)) 38 return cursor.description 39 40 def _name_to_index(self, cursor, table_name): 41 """ 42 Returns a dictionary of {field_name: field_index} for the given table. 43 Indexes are 0-based. 44 """ 45 return dict([(d[0], i) for i, d in enumerate(self.get_table_description(cursor, table_name))]) 46 47 def get_relations(self, cursor, table_name): 48 """ 49 Returns a dictionary of {field_index: (field_index_other_table, other_table)} 50 representing all relationships to the given table. Indexes are 0-based. 51 """ 52 my_field_dict = self._name_to_index(cursor, table_name) 53 relations = {} 54 55 cursor.execute(""" 56 SELECT FKCOLUMN_NAME, PKTABLE_NAME, PKCOLUMN_NAME 57 FROM LINTER_SYSTEM_USER.FOREIGN_KEYS 58 WHERE FKTABLE_NAME = %s""" % dbo.quote_name(table_name)) 59 for row in cursor.fetchall(): 60 other_field_index = self._name_to_index(cursor, row[1])[row[2]] 61 my_field_index = my_field_dict[row[0]] 62 relations[my_field_index] = (other_field_index, row[1]) 63 64 return relations 65 66 def get_indexes(self, cursor, table_name): 67 """ 68 Returns a dictionary of fieldname -> infodict for the given table, 69 where each infodict is in the format: 70 {'primary_key': boolean representing whether it's the primary key, 71 'unique': boolean representing whether it's a unique index} 72 """ 73 indexes = {} 74 75 cursor.execute(""" 76 SELECT NON_UNIQUE, COLUMN_NAME 77 FROM LINTER_SYSTEM_USER.TABLESTATISTICS 78 WHERE TABLE_NAME = %s""" % dbo.quote_name(table_name)) 79 for row in cursor.fetchall(): 80 if row[0] != None: 81 indexes[row[1]] = {'primary_key': bool(row[1]), 'unique': not bool(row[0])} 82 83 return indexes 84 -
django/db/backends/linter/creation.py
1 import sys 2 from django.conf import settings 3 from django.db.backends.creation import BaseDatabaseCreation 4 5 TEST_USER_PREFIX = 'TEST_' 6 7 class DatabaseCreation(BaseDatabaseCreation): 8 # This dictionary maps Field objects to their associated LINTER column 9 # types, as strings. Column-type strings can contain format strings; they'll 10 # be interpolated against the values of Field.__dict__ before being output. 11 # If a column type is set to None, it won't be included in the output. 12 data_types = { 13 'AutoField': 'integer AUTOINC', 14 'BooleanField': 'boolean', 15 'CharField': 'nvarchar(%(max_length)s)', 16 'CommaSeparatedIntegerField': 'varchar(%(max_length)s)', 17 'DateField': 'date', 18 'DateTimeField': 'date', 19 'DecimalField': 'number(%(max_digits)s, %(decimal_places)s)', 20 'FileField': 'varchar(%(max_length)s)', 21 'FilePathField': 'varchar(%(max_length)s)', 22 'FloatField': 'double', 23 'IntegerField': 'integer', 24 'IPAddressField': 'char(15)', 25 'NullBooleanField': 'integer', 26 'OneToOneField': 'integer', 27 'PositiveIntegerField': 'integer', 28 'PositiveSmallIntegerField': 'smallint', 29 'SlugField': 'varchar(%(max_length)s)', 30 'SmallIntegerField': 'smallint', 31 'TextField': 'varchar(1000)', 32 'TimeField': 'date', 33 'URLField': 'varchar(%(max_length)s)', 34 } 35 36 remember = {} 37 38 def _create_test_db(self, verbosity, autoclobber): 39 """ 40 We don't created test database, we only create test user 41 """ 42 43 TEST_DATABASE_USER = TEST_USER_PREFIX + settings.DATABASE_USER 44 TEST_DATABASE_PASSWD = settings.DATABASE_PASSWORD 45 46 parameters = { 47 'user' : TEST_DATABASE_USER, 48 'password' : TEST_DATABASE_PASSWD, 49 } 50 51 self.remember['user'] = settings.DATABASE_USER 52 self.remember['passwd'] = settings.DATABASE_PASSWORD 53 54 cursor = self.connection.cursor() 55 56 if verbosity >= 1: 57 print "Creating test user..." 58 try: 59 self._create_test_user(cursor, parameters, verbosity) 60 except Exception, e: 61 sys.stderr.write("Got an error creating the test user: %s\n" % e) 62 if not autoclobber: 63 confirm = raw_input("It appears the test user, %s, already exists. Type 'yes' to delete it? or 'no' to cancel: " % TEST_DATABASE_USER) 64 if autoclobber or confirm == 'yes': 65 try: 66 if verbosity >= 1: 67 print "Destroying old test user..." 68 self._destroy_test_user(cursor, parameters, verbosity) 69 if verbosity >= 1: 70 print "Creating test user..." 71 self._create_test_user(cursor, parameters, verbosity) 72 except Exception, e: 73 sys.stderr.write("Got an error recreating the test user: %s\n" % e) 74 sys.exit(2) 75 else: 76 print "Tests cancelled." 77 sys.exit(1) 78 79 settings.DATABASE_USER = TEST_DATABASE_USER 80 81 return settings.DATABASE_NAME 82 83 def _destroy_test_db(self, test_database_name, verbosity): 84 """ 85 Don't destroy database. Desrtoy test user. 86 """ 87 TEST_DATABASE_USER = settings.DATABASE_USER 88 TEST_DATABASE_PASSWD = settings.DATABASE_PASSWORD 89 90 parameters = { 91 'user' : TEST_DATABASE_USER, 92 'password' : TEST_DATABASE_PASSWD 93 } 94 95 settings.DATABASE_USER = self.remember['user'] 96 settings.DATABASE_PASSWORD = self.remember['passwd'] 97 98 cursor = self.connection.cursor() 99 100 if verbosity >= 1: 101 print "Destroying test user..." 102 self._destroy_test_user(cursor, parameters, verbosity) 103 104 self.connection.close() 105 106 def _create_test_user(self, cursor, parameters, verbosity): 107 if verbosity >= 2: 108 print "_crete_test_user(): username = %s" % parameters['user'] 109 statements = [ 110 "CREATE USER %(user)s IDENTIFIED BY '%(password)s'", 111 "GRANT RESOURCE TO %(user)s" 112 ] 113 self._execute_statements(cursor, statements, parameters, verbosity) 114 115 def _destroy_test_user(self, cursor, parameters, verbosity): 116 if verbosity >= 2: 117 print "_destroy_test_user(): user = %s" % parameters['user'] 118 statements = ["DROP USER %(user)s CASCADE"] 119 120 self._execute_statements(cursor, statements, parameters, verbosity) 121 122 def _execute_statements(self, cursor, statements, parameters, verbosity): 123 for template in statements: 124 stmt = template % parameters 125 if verbosity >= 2: 126 print stmt 127 try: 128 cursor.execute(stmt) 129 except Exception, err: 130 sys.stderr.write("Failed (%s)\n" % (err)) 131 raise 132 133 def sql_for_pending_references(self, model, style, pending_references): 134 """ 135 Returns any ALTER TABLE statements to add constraints after the fact. 136 """ 137 qn = self.connection.ops.quote_name 138 final_output = [] 139 opts = model._meta 140 if model in pending_references: 141 for rel_class, f in pending_references[model]: 142 rel_opts = rel_class._meta 143 r_table = rel_opts.db_table 144 r_col = f.column 145 table = opts.db_table 146 col = opts.get_field(f.rel.field_name).column 147 final_output.append(style.SQL_KEYWORD('ALTER TABLE') + ' %s ADD FOREIGN KEY (%s) REFERENCES %s (%s)%s;' % \ 148 (qn(r_table), 149 qn(r_col), qn(table), qn(col), 150 self.connection.ops.deferrable_sql())) 151 del pending_references[model] 152 return final_output 153 154 def sql_indexes_for_field(self, model, f, style): 155 """ 156 Return the CREATE INDEX SQL statements for a single model field 157 """ 158 from django.db.models.fields.related import ForeignKey 159 # LINTER create index for primary keys, unique and foreign keys 160 # automatically 161 if f.db_index and not f.unique and (not isinstance(f, ForeignKey)): 162 qn = self.connection.ops.quote_name 163 tablespace = f.db_tablespace or model._meta.db_tablespace 164 if tablespace: 165 sql = self.connection.ops.tablespace_sql(tablespace) 166 if sql: 167 tablespace_sql = ' ' + sql 168 else: 169 tablespace_sql = '' 170 else: 171 tablespace_sql = '' 172 output = [style.SQL_KEYWORD('CREATE INDEX') + ' ' + 173 style.SQL_TABLE(qn('%s_%s' % (model._meta.db_table, f.column))) + ' ' + 174 style.SQL_KEYWORD('ON') + ' ' + 175 style.SQL_TABLE(qn(model._meta.db_table)) + ' ' + 176 "(%s)" % style.SQL_FIELD(qn(f.column)) + 177 "%s;" % tablespace_sql] 178 else: 179 output = [] 180 return output 181 182 def sql_remove_table_constraints(self, model, references_to_delete, style): 183 from django.db.backends.util import truncate_name 184 185 output = [] 186 qn = self.connection.ops.quote_name 187 for rel_class, f in references_to_delete[model]: 188 table = rel_class._meta.db_table 189 col = f.column 190 output.append('%s %s %s (%s);' % \ 191 (style.SQL_KEYWORD('ALTER TABLE'), 192 style.SQL_TABLE(qn(table)), 193 style.SQL_KEYWORD(self.connection.ops.drop_foreignkey_sql()), 194 style.SQL_FIELD(col))) 195 del references_to_delete[model] 196 return output -
django/db/backends/linter/query.py
1 """ 2 Custom Query class for Linter. 3 Derives from: django.db.models.sql.query.Query 4 """ 5 6 import datetime 7 8 from django.db.backends import util 9 10 # Cache. Maps default query class to new Linter query class. 11 _classes = {} 12 13 def query_class(QueryClass, Database): 14 """ 15 Returns a custom django.db.models.sql.query.Query subclass that is 16 appropriate for Linter. 17 18 """ 19 global _classes 20 try: 21 return _classes[QueryClass] 22 except KeyError: 23 pass 24 25 class LinterQuery(QueryClass): 26 27 def as_sql(self, with_limits=True, with_col_aliases=False): 28 """ 29 Creates the SQL for this query. Returns the SQL string and list 30 of parameters. This is overriden from the original Query class 31 to accommodate Linter's limit/offset SQL. 32 33 If 'with_limits' is False, any limit/offset information is not 34 included in the query. 35 """ 36 37 # The `do_offset` flag indicates whether we need to construct 38 # the SQL needed to use limit/offset. 39 do_offset = with_limits and (self.high_mark is not None 40 or self.low_mark) 41 42 # If no offsets, just return the result of the base class 43 # `as_sql`. 44 if not do_offset: 45 return super(LinterQuery, self).as_sql(with_limits=False, 46 with_col_aliases=with_col_aliases) 47 48 # `get_columns` needs to be called before `get_ordering` to 49 # populate `_select_alias`. 50 self.pre_sql_setup() 51 out_cols = self.get_columns() 52 ordering = self.get_ordering() 53 54 sql, params = super(LinterQuery, self).as_sql(with_limits=False, 55 with_col_aliases=True) 56 57 # Constructing the result SQL, using the initial select SQL 58 # obtained above. 59 result =['%s' % sql] 60 61 if self.low_mark: 62 if self.high_mark == self.low_mark: 63 return None 64 elif self.high_mark is not None: 65 result.append('LIMIT %d, %d' % (self.low_mark, (self.high_mark - self.low_mark))) 66 else: 67 val = self.connection.ops.no_limit_value() 68 if val: 69 result.append('LIMIT %d, %d' % (self.low_mark, val)) 70 else: 71 result.append('LIMIT %d' % self.high_mark) 72 73 # Returning the SQL w/params. 74 return ' '.join(result), params 75 76 _classes[QueryClass] = LinterQuery 77 return LinterQuery -
docs/faq/install.txt
30 30 31 31 If you want to use Django with a database, which is probably the case, you'll 32 32 also need a database engine. PostgreSQL_ is recommended, because we're 33 PostgreSQL fans, and MySQL_, `SQLite 3`_, and Oracle_ are also supported.33 PostgreSQL fans, and MySQL_, `SQLite 3`_, Oracle_, and LINTER_ are also supported. 34 34 35 35 .. _Python: http://www.python.org/ 36 36 .. _Apache 2: http://httpd.apache.org/ … … 40 40 .. _MySQL: http://www.mysql.com/ 41 41 .. _`SQLite 3`: http://www.sqlite.org/ 42 42 .. _Oracle: http://www.oracle.com/ 43 .. _LINTER: http://www.lintersql.com/ 43 44 44 45 Do I lose anything by using Python 2.3 versus newer Python versions, such as Python 2.5? 45 46 ---------------------------------------------------------------------------------------- -
docs/ref/databases.txt
460 460 Oracle. A workaround to this is to keep ``TextField`` columns out of any 461 461 models that you foresee performing ``distinct()`` queries on, and to 462 462 include the ``TextField`` in a related model instead. 463 464 .. _linter-notes: 465 466 .. versionadded:: 1.0 467 468 LINTER notes 469 ============ 470 471 Django supports `DBMS LINTER`_ versions 5.9 and higher. For working Django 472 with LINTER you will need the `LinPy.dll`. It's library based on 473 `Python Database API Specification v2.0`_ included in distributive `LINTER` 474 and supports Python versions 2.1-2.6. 475 476 .. _`DBMS LINTER`: http://www.lintersql.com/ 477 .. _`Python Database API Specification v2.0`: http://www.python.org/dev/peps/pep-0249/ 478 479 In order for the ``python manage.py syncdb`` command to work, your LINTER 480 database user must have access category ``resource`` or ``dba``. 481 To run Django's test suite, the user needs access category ``dba``. 482 483 Connecting to the database 484 -------------------------- 485 486 Your Django settings.py file should look something like this for LINTER:: 487 488 DATABASE_ENGINE = 'linter' 489 DATABASE_NAME = 'DEMO' 490 DATABASE_USER = 'a_user' 491 DATABASE_PASSWORD = 'a_password' 492 DATABASE_HOST = '' 493 DATABASE_PORT = '' 494 495 ``DateField`` and ``DateTimeField`` data 496 ---------------------------------------- 497 498 All date and time data LINTER storage in one type `DATE`_. LinPy by default 499 returned all data in ``datetime.datetime`` format, if need get data in 500 ``datetime.date`` format, then you will use LINTER backend function 501 ``set_date_emulation``. This function set mode conversion values ``datetime`` 502 type to ``date`` type, if data contain values only for year, month and day. 503 This mode conversion values used in tests ``serializers``, ``model_forms`` 504 from Django's test suite. This tests is necessary edit in the following way. 505 At the beggining of tests need to add strings:: 506 507 >>> from django.db import connection 508 >>> connection.set_date_emulation(1) 509 510 In this case tests will execute completely. 511 512 .. _`DATE`: http://www.lintersql.com/en/documentation/pdf/sql.pdf -
docs/ref/settings.txt
152 152 Default: ``''`` (Empty string) 153 153 154 154 The database backend to use. The built-in database backends are 155 ``'postgresql_psycopg2'``, ``'postgresql'``, ``'mysql'``, ``'sqlite3'``, and156 ``'oracle'`` .155 ``'postgresql_psycopg2'``, ``'postgresql'``, ``'mysql'``, ``'sqlite3'``, 156 ``'oracle'`` and ``'linter'``. 157 157 158 158 You can use a database backend that doesn't ship with Django by setting 159 159 ``DATABASE_ENGINE`` to a fully-qualified path (i.e. -
docs/topics/install.txt
61 61 62 62 If you plan to use Django's database API functionality, you'll need to 63 63 make sure a database server is running. Django works with PostgreSQL_, 64 MySQL_, Oracle_ and SQLite_ (although SQLite doesn't require a separate server64 MySQL_, Oracle_, LINTER_ and SQLite_ (although SQLite doesn't require a separate server 65 65 to be running). 66 66 67 67 Additionally, you'll need to make sure your Python database bindings are … … 86 86 4.3.1 or higher. You will also want to read the database-specific notes for 87 87 the :ref:`Oracle backend <oracle-notes>`. 88 88 89 * If you're using LINTER, you'll need LinPy.dll. You will also want to read 90 the database-specific notes for the :ref:`LINTER backend <ref-databases>`. 91 89 92 If you plan to use Django's ``manage.py syncdb`` command to 90 93 automatically create database tables for your models, you'll need to 91 94 ensure that Django has permission to create and alter tables in the … … 108 111 .. _pysqlite: http://initd.org/pub/software/pysqlite/ 109 112 .. _cx_Oracle: http://cx-oracle.sourceforge.net/ 110 113 .. _Oracle: http://www.oracle.com/ 114 .. _LINTER: http://www.lintersql.com/ 111 115 112 116 .. _removing-old-versions-of-django: 113 117