Fixed #121 -- Django now quotes all names in SQL queries. Also added unit tests to confirm. Thanks, Robin Munn and Sune.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@1224 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Adrian Holovaty 2005-11-14 01:44:35 +00:00
parent 6e40d8c29f
commit f6bf41e59a
9 changed files with 295 additions and 116 deletions

View File

@ -7,8 +7,10 @@ DOCUMENTATION_DIRECTORY = '/home/html/documentation/'
def clean_up(): def clean_up():
# Clean up old database records # Clean up old database records
cursor = db.cursor() cursor = db.cursor()
cursor.execute("DELETE FROM core_sessions WHERE expire_date < NOW()") cursor.execute("DELETE FROM %s WHERE %s < NOW()" % \
cursor.execute("DELETE FROM registration_challenges WHERE request_date < NOW() - INTERVAL '1 week'") (db.quote_name('core_sessions'), db.quote_name('expire_date')))
cursor.execute("DELETE FROM %s WHERE %s < NOW() - INTERVAL '1 week'" % \
(db.quote_name('registration_challenges'), db.quote_name('request_date')))
db.commit() db.commit()
if __name__ == "__main__": if __name__ == "__main__":

View File

@ -149,8 +149,8 @@ DATA_TYPES = {
'NullBooleanField': 'bit', 'NullBooleanField': 'bit',
'OneToOneField': 'int', 'OneToOneField': 'int',
'PhoneNumberField': 'varchar(20)', 'PhoneNumberField': 'varchar(20)',
'PositiveIntegerField': 'int CONSTRAINT [CK_int_pos_%(name)s] CHECK ([%(name)s] > 0)', 'PositiveIntegerField': 'int CONSTRAINT [CK_int_pos_%(column)s] CHECK ([%(column)s] > 0)',
'PositiveSmallIntegerField': 'smallint CONSTRAINT [CK_smallint_pos_%(name)s] CHECK ([%(name)s] > 0)', 'PositiveSmallIntegerField': 'smallint CONSTRAINT [CK_smallint_pos_%(column)s] CHECK ([%(column)s] > 0)',
'SlugField': 'varchar(50)', 'SlugField': 'varchar(50)',
'SmallIntegerField': 'smallint', 'SmallIntegerField': 'smallint',
'TextField': 'text', 'TextField': 'text',

View File

@ -170,8 +170,8 @@ DATA_TYPES = {
'NullBooleanField': 'boolean', 'NullBooleanField': 'boolean',
'OneToOneField': 'integer', 'OneToOneField': 'integer',
'PhoneNumberField': 'varchar(20)', 'PhoneNumberField': 'varchar(20)',
'PositiveIntegerField': 'integer CHECK (%(name)s >= 0)', 'PositiveIntegerField': 'integer CHECK (%(column)s >= 0)',
'PositiveSmallIntegerField': 'smallint CHECK (%(name)s >= 0)', 'PositiveSmallIntegerField': 'smallint CHECK (%(column)s >= 0)',
'SlugField': 'varchar(50)', 'SlugField': 'varchar(50)',
'SmallIntegerField': 'smallint', 'SmallIntegerField': 'smallint',
'TextField': 'text', 'TextField': 'text',

View File

@ -19,7 +19,10 @@ APP_ARGS = '[modelmodule ...]'
PROJECT_TEMPLATE_DIR = os.path.join(django.__path__[0], 'conf', '%s_template') PROJECT_TEMPLATE_DIR = os.path.join(django.__path__[0], 'conf', '%s_template')
def _get_packages_insert(app_label): def _get_packages_insert(app_label):
return "INSERT INTO packages (label, name) VALUES ('%s', '%s');" % (app_label, app_label) from django.core.db import db
return "INSERT INTO %s (%s, %s) VALUES ('%s', '%s');" % \
(db.quote_name('packages'), db.quote_name('label'), db.quote_name('name'),
app_label, app_label)
def _get_permission_codename(action, opts): def _get_permission_codename(action, opts):
return '%s_%s' % (action, opts.object_name.lower()) return '%s_%s' % (action, opts.object_name.lower())
@ -33,12 +36,16 @@ def _get_all_permissions(opts):
return perms + list(opts.permissions) return perms + list(opts.permissions)
def _get_permission_insert(name, codename, opts): def _get_permission_insert(name, codename, opts):
return "INSERT INTO auth_permissions (name, package, codename) VALUES ('%s', '%s', '%s');" % \ from django.core.db import db
(name.replace("'", "''"), opts.app_label, codename) return "INSERT INTO %s (%s, %s, %s) VALUES ('%s', '%s', '%s');" % \
(db.quote_name('auth_permissions'), db.quote_name('name'), db.quote_name('package'),
db.quote_name('codename'), name.replace("'", "''"), opts.app_label, codename)
def _get_contenttype_insert(opts): def _get_contenttype_insert(opts):
return "INSERT INTO content_types (name, package, python_module_name) VALUES ('%s', '%s', '%s');" % \ from django.core.db import db
(opts.verbose_name, opts.app_label, opts.module_name) return "INSERT INTO %s (%s, %s, %s) VALUES ('%s', '%s', '%s');" % \
(db.quote_name('content_types'), db.quote_name('name'), db.quote_name('package'),
db.quote_name('python_module_name'), opts.verbose_name, opts.app_label, opts.module_name)
def _is_valid_dir_name(s): def _is_valid_dir_name(s):
return bool(re.search(r'^\w+$', s)) return bool(re.search(r'^\w+$', s))
@ -64,7 +71,7 @@ def get_sql_create(mod):
data_type = f.get_internal_type() data_type = f.get_internal_type()
col_type = db.DATA_TYPES[data_type] col_type = db.DATA_TYPES[data_type]
if col_type is not None: if col_type is not None:
field_output = [f.column, col_type % rel_field.__dict__] field_output = [db.db.quote_name(f.column), col_type % rel_field.__dict__]
field_output.append('%sNULL' % (not f.null and 'NOT ' or '')) field_output.append('%sNULL' % (not f.null and 'NOT ' or ''))
if f.unique: if f.unique:
field_output.append('UNIQUE') field_output.append('UNIQUE')
@ -72,14 +79,16 @@ def get_sql_create(mod):
field_output.append('PRIMARY KEY') field_output.append('PRIMARY KEY')
if f.rel: if f.rel:
field_output.append('REFERENCES %s (%s)' % \ field_output.append('REFERENCES %s (%s)' % \
(f.rel.to.db_table, f.rel.to.get_field(f.rel.field_name).column)) (db.db.quote_name(f.rel.to.db_table),
db.db.quote_name(f.rel.to.get_field(f.rel.field_name).column)))
table_output.append(' '.join(field_output)) table_output.append(' '.join(field_output))
if opts.order_with_respect_to: if opts.order_with_respect_to:
table_output.append('_order %s NULL' % db.DATA_TYPES['IntegerField']) table_output.append('%s %s NULL' % (db.db.quote_name('_order'), db.DATA_TYPES['IntegerField']))
for field_constraints in opts.unique_together: for field_constraints in opts.unique_together:
table_output.append('UNIQUE (%s)' % ", ".join([opts.get_field(f).column for f in field_constraints])) table_output.append('UNIQUE (%s)' % \
", ".join([db.db.quote_name(opts.get_field(f).column) for f in field_constraints]))
full_statement = ['CREATE TABLE %s (' % opts.db_table] full_statement = ['CREATE TABLE %s (' % db.db.quote_name(opts.db_table)]
for i, line in enumerate(table_output): # Combine and add commas. for i, line in enumerate(table_output): # Combine and add commas.
full_statement.append(' %s%s' % (line, i < len(table_output)-1 and ',' or '')) full_statement.append(' %s%s' % (line, i < len(table_output)-1 and ',' or ''))
full_statement.append(');') full_statement.append(');')
@ -88,13 +97,21 @@ def get_sql_create(mod):
for klass in mod._MODELS: for klass in mod._MODELS:
opts = klass._meta opts = klass._meta
for f in opts.many_to_many: for f in opts.many_to_many:
table_output = ['CREATE TABLE %s (' % f.get_m2m_db_table(opts)] table_output = ['CREATE TABLE %s (' % db.db.quote_name(f.get_m2m_db_table(opts))]
table_output.append(' id %s NOT NULL PRIMARY KEY,' % db.DATA_TYPES['AutoField']) table_output.append(' %s %s NOT NULL PRIMARY KEY,' % (db.db.quote_name('id'), db.DATA_TYPES['AutoField']))
table_output.append(' %s_id %s NOT NULL REFERENCES %s (%s),' % \ table_output.append(' %s %s NOT NULL REFERENCES %s (%s),' % \
(opts.object_name.lower(), db.DATA_TYPES[get_rel_data_type(opts.pk)] % opts.pk.__dict__, opts.db_table, opts.pk.column)) (db.db.quote_name(opts.object_name.lower() + '_id'),
table_output.append(' %s_id %s NOT NULL REFERENCES %s (%s),' % \ db.DATA_TYPES[get_rel_data_type(opts.pk)] % opts.pk.__dict__,
(f.rel.to.object_name.lower(), db.DATA_TYPES[get_rel_data_type(f.rel.to.pk)] % f.rel.to.pk.__dict__, f.rel.to.db_table, f.rel.to.pk.column)) db.db.quote_name(opts.db_table),
table_output.append(' UNIQUE (%s_id, %s_id)' % (opts.object_name.lower(), f.rel.to.object_name.lower())) db.db.quote_name(opts.pk.column)))
table_output.append(' %s %s NOT NULL REFERENCES %s (%s),' % \
(db.db.quote_name(f.rel.to.object_name.lower() + '_id'),
db.DATA_TYPES[get_rel_data_type(f.rel.to.pk)] % f.rel.to.pk.__dict__,
db.db.quote_name(f.rel.to.db_table),
db.db.quote_name(f.rel.to.pk.column)))
table_output.append(' UNIQUE (%s, %s)' % \
(db.db.quote_name(opts.object_name.lower() + '_id'),
db.db.quote_name(f.rel.to.object_name.lower() + '_id')))
table_output.append(');') table_output.append(');')
final_output.append('\n'.join(table_output)) final_output.append('\n'.join(table_output))
return final_output return final_output
@ -114,7 +131,7 @@ def get_sql_delete(mod):
try: try:
if cursor is not None: if cursor is not None:
# Check whether the table exists. # Check whether the table exists.
cursor.execute("SELECT 1 FROM django_admin_log LIMIT 1") cursor.execute("SELECT 1 FROM %s LIMIT 1" % db.db.quote_name('django_admin_log'))
except: except:
# The table doesn't exist, so it doesn't need to be dropped. # The table doesn't exist, so it doesn't need to be dropped.
db.db.rollback() db.db.rollback()
@ -129,12 +146,12 @@ def get_sql_delete(mod):
try: try:
if cursor is not None: if cursor is not None:
# Check whether the table exists. # Check whether the table exists.
cursor.execute("SELECT 1 FROM %s LIMIT 1" % klass._meta.db_table) cursor.execute("SELECT 1 FROM %s LIMIT 1" % db.db.quote_name(klass._meta.db_table))
except: except:
# The table doesn't exist, so it doesn't need to be dropped. # The table doesn't exist, so it doesn't need to be dropped.
db.db.rollback() db.db.rollback()
else: else:
output.append("DROP TABLE %s;" % klass._meta.db_table) output.append("DROP TABLE %s;" % db.db.quote_name(klass._meta.db_table))
# Output DROP TABLE statements for many-to-many tables. # Output DROP TABLE statements for many-to-many tables.
for klass in mod._MODELS: for klass in mod._MODELS:
@ -142,25 +159,31 @@ def get_sql_delete(mod):
for f in opts.many_to_many: for f in opts.many_to_many:
try: try:
if cursor is not None: if cursor is not None:
cursor.execute("SELECT 1 FROM %s LIMIT 1" % f.get_m2m_db_table(opts)) cursor.execute("SELECT 1 FROM %s LIMIT 1" % db.db.quote_name(f.get_m2m_db_table(opts)))
except: except:
db.db.rollback() db.db.rollback()
else: else:
output.append("DROP TABLE %s;" % f.get_m2m_db_table(opts)) output.append("DROP TABLE %s;" % db.db.quote_name(f.get_m2m_db_table(opts)))
app_label = mod._MODELS[0]._meta.app_label app_label = mod._MODELS[0]._meta.app_label
# Delete from packages, auth_permissions, content_types. # Delete from packages, auth_permissions, content_types.
output.append("DELETE FROM packages WHERE label = '%s';" % app_label) output.append("DELETE FROM %s WHERE %s = '%s';" % \
output.append("DELETE FROM auth_permissions WHERE package = '%s';" % app_label) (db.db.quote_name('packages'), db.db.quote_name('label'), app_label))
output.append("DELETE FROM content_types WHERE package = '%s';" % app_label) output.append("DELETE FROM %s WHERE %s = '%s';" % \
(db.db.quote_name('auth_permissions'), db.db.quote_name('package'), app_label))
output.append("DELETE FROM %s WHERE %s = '%s';" % \
(db.db.quote_name('content_types'), db.db.quote_name('package'), app_label))
# Delete from the admin log. # Delete from the admin log.
if cursor is not None: if cursor is not None:
cursor.execute("SELECT id FROM content_types WHERE package = %s", [app_label]) cursor.execute("SELECT %s FROM %s WHERE %s = %%s" % \
(db.db.quote_name('id'), db.db.quote_name('content_types'),
db.db.quote_name('package')), [app_label])
if admin_log_exists: if admin_log_exists:
for row in cursor.fetchall(): for row in cursor.fetchall():
output.append("DELETE FROM django_admin_log WHERE content_type_id = %s;" % row[0]) output.append("DELETE FROM %s WHERE %s = %s;" % \
(db.db.quote_name('django_admin_log'), db.db.quote_name('content_type_id'), row[0]))
# Close database connection explicitly, in case this output is being piped # Close database connection explicitly, in case this output is being piped
# directly into a database client, to avoid locking issues. # directly into a database client, to avoid locking issues.
@ -206,25 +229,29 @@ get_sql_initial_data.args = APP_ARGS
def get_sql_sequence_reset(mod): def get_sql_sequence_reset(mod):
"Returns a list of the SQL statements to reset PostgreSQL sequences for the given module." "Returns a list of the SQL statements to reset PostgreSQL sequences for the given module."
from django.core import meta from django.core import db, meta
output = [] output = []
for klass in mod._MODELS: for klass in mod._MODELS:
for f in klass._meta.fields: for f in klass._meta.fields:
if isinstance(f, meta.AutoField): if isinstance(f, meta.AutoField):
output.append("SELECT setval('%s_%s_seq', (SELECT max(%s) FROM %s));" % (klass._meta.db_table, f.column, f.column, klass._meta.db_table)) output.append("SELECT setval('%s_%s_seq', (SELECT max(%s) FROM %s));" % \
(klass._meta.db_table, f.column, db.db.quote_name(f.column),
db.db.quote_name(klass._meta.db_table)))
return output return output
get_sql_sequence_reset.help_doc = "Prints the SQL statements for resetting PostgreSQL sequences for the given model module name(s)." get_sql_sequence_reset.help_doc = "Prints the SQL statements for resetting PostgreSQL sequences for the given model module name(s)."
get_sql_sequence_reset.args = APP_ARGS get_sql_sequence_reset.args = APP_ARGS
def get_sql_indexes(mod): def get_sql_indexes(mod):
"Returns a list of the CREATE INDEX SQL statements for the given module." "Returns a list of the CREATE INDEX SQL statements for the given module."
from django.core.db import db
output = [] output = []
for klass in mod._MODELS: for klass in mod._MODELS:
for f in klass._meta.fields: for f in klass._meta.fields:
if f.db_index: if f.db_index:
unique = f.unique and "UNIQUE " or "" unique = f.unique and "UNIQUE " or ""
output.append("CREATE %sINDEX %s_%s ON %s (%s);" % \ output.append("CREATE %sINDEX %s_%s ON %s (%s);" % \
(unique, klass._meta.db_table, f.column, klass._meta.db_table, f.column)) (unique, klass._meta.db_table, f.column,
db.quote_name(klass._meta.db_table), db.quote_name(f.column)))
return output return output
get_sql_indexes.help_doc = "Prints the CREATE INDEX SQL statements for the given model module name(s)." get_sql_indexes.help_doc = "Prints the CREATE INDEX SQL statements for the given model module name(s)."
get_sql_indexes.args = APP_ARGS get_sql_indexes.args = APP_ARGS
@ -242,7 +269,8 @@ def database_check(mod):
app_label = mod._MODELS[0]._meta.app_label app_label = mod._MODELS[0]._meta.app_label
# Check that the package exists in the database. # Check that the package exists in the database.
cursor.execute("SELECT 1 FROM packages WHERE label = %s", [app_label]) cursor.execute("SELECT 1 FROM %s WHERE %s = %%s" % \
(db.db.quote_name('packages'), db.db.quote_name('label')), [app_label])
if cursor.rowcount < 1: if cursor.rowcount < 1:
# sys.stderr.write("The '%s' package isn't installed.\n" % app_label) # sys.stderr.write("The '%s' package isn't installed.\n" % app_label)
print _get_packages_insert(app_label) print _get_packages_insert(app_label)
@ -256,34 +284,46 @@ def database_check(mod):
perms_seen.update(dict(perms)) perms_seen.update(dict(perms))
contenttypes_seen[opts.module_name] = 1 contenttypes_seen[opts.module_name] = 1
for codename, name in perms: for codename, name in perms:
cursor.execute("SELECT 1 FROM auth_permissions WHERE package = %s AND codename = %s", (app_label, codename)) cursor.execute("SELECT 1 FROM %s WHERE %s = %%s AND %s = %%s" % \
(db.db.quote_name('auth_permissions'), db.db.quote_name('package'),
db.db.quote_name('codename')), (app_label, codename))
if cursor.rowcount < 1: if cursor.rowcount < 1:
# sys.stderr.write("The '%s.%s' permission doesn't exist.\n" % (app_label, codename)) # sys.stderr.write("The '%s.%s' permission doesn't exist.\n" % (app_label, codename))
print _get_permission_insert(name, codename, opts) print _get_permission_insert(name, codename, opts)
cursor.execute("SELECT 1 FROM content_types WHERE package = %s AND python_module_name = %s", (app_label, opts.module_name)) cursor.execute("SELECT 1 FROM %s WHERE %s = %%s AND %s = %%s" % \
(db.db.quote_name('content_types'), db.db.quote_name('package'),
db.db.quote_name('python_module_name')), (app_label, opts.module_name))
if cursor.rowcount < 1: if cursor.rowcount < 1:
# sys.stderr.write("The '%s.%s' content type doesn't exist.\n" % (app_label, opts.module_name)) # sys.stderr.write("The '%s.%s' content type doesn't exist.\n" % (app_label, opts.module_name))
print _get_contenttype_insert(opts) print _get_contenttype_insert(opts)
# Check that there aren't any *extra* permissions in the DB that the model # Check that there aren't any *extra* permissions in the DB that the model
# doesn't know about. # doesn't know about.
cursor.execute("SELECT codename FROM auth_permissions WHERE package = %s", (app_label,)) cursor.execute("SELECT %s FROM %s WHERE %s = %%s" % \
(db.db.quote_name('codename'), db.db.quote_name('auth_permissions'),
db.db.quote_name('package')), (app_label,))
for row in cursor.fetchall(): for row in cursor.fetchall():
try: try:
perms_seen[row[0]] perms_seen[row[0]]
except KeyError: except KeyError:
# sys.stderr.write("A permission called '%s.%s' was found in the database but not in the model.\n" % (app_label, row[0])) # sys.stderr.write("A permission called '%s.%s' was found in the database but not in the model.\n" % (app_label, row[0]))
print "DELETE FROM auth_permissions WHERE package='%s' AND codename = '%s';" % (app_label, row[0]) print "DELETE FROM %s WHERE %s='%s' AND %s = '%s';" % \
(db.db.quote_name('auth_permissions'), db.db.quote_name('package'),
app_label, db.db.quote_name('codename'), row[0])
# Check that there aren't any *extra* content types in the DB that the # Check that there aren't any *extra* content types in the DB that the
# model doesn't know about. # model doesn't know about.
cursor.execute("SELECT python_module_name FROM content_types WHERE package = %s", (app_label,)) cursor.execute("SELECT %s FROM %s WHERE %s = %%s" % \
(db.db.quote_name('python_module_name'), db.db.quote_name('content_types'),
db.db.quote_name('package')), (app_label,))
for row in cursor.fetchall(): for row in cursor.fetchall():
try: try:
contenttypes_seen[row[0]] contenttypes_seen[row[0]]
except KeyError: except KeyError:
# sys.stderr.write("A content type called '%s.%s' was found in the database but not in the model.\n" % (app_label, row[0])) # sys.stderr.write("A content type called '%s.%s' was found in the database but not in the model.\n" % (app_label, row[0]))
print "DELETE FROM content_types WHERE package='%s' AND python_module_name = '%s';" % (app_label, row[0]) print "DELETE FROM %s WHERE %s='%s' AND %s = '%s';" % \
(db.db.quote_name('content_types'), db.db.quote_name('package'),
app_label, db.db.quote_name('python_module_name'), row[0])
database_check.help_doc = "Checks that everything is installed in the database for the given model module name(s) and prints SQL statements if needed." database_check.help_doc = "Checks that everything is installed in the database for the given model module name(s) and prints SQL statements if needed."
database_check.args = APP_ARGS database_check.args = APP_ARGS
@ -318,7 +358,9 @@ def init():
cursor = db.db.cursor() cursor = db.db.cursor()
for sql in get_sql_create(core) + get_sql_create(auth) + get_sql_initial_data(core) + get_sql_initial_data(auth): for sql in get_sql_create(core) + get_sql_create(auth) + get_sql_initial_data(core) + get_sql_initial_data(auth):
cursor.execute(sql) cursor.execute(sql)
cursor.execute("INSERT INTO %s (domain, name) VALUES ('example.com', 'Example site')" % core.Site._meta.db_table) cursor.execute("INSERT INTO %s (%s, %s) VALUES ('example.com', 'Example site')" % \
(db.db.quote_name(core.Site._meta.db_table), db.db.quote_name('domain'),
db.db.quote_name('name')))
except Exception, e: except Exception, e:
sys.stderr.write("Error: The database couldn't be initialized.\n%s\n" % e) sys.stderr.write("Error: The database couldn't be initialized.\n%s\n" % e)
try: try:
@ -687,7 +729,7 @@ def createcachetable(tablename):
table_output = [] table_output = []
index_output = [] index_output = []
for f in fields: for f in fields:
field_output = [f.column, db.DATA_TYPES[f.get_internal_type()] % f.__dict__] field_output = [db.db.quote_name(f.column), db.DATA_TYPES[f.get_internal_type()] % f.__dict__]
field_output.append("%sNULL" % (not f.null and "NOT " or "")) field_output.append("%sNULL" % (not f.null and "NOT " or ""))
if f.unique: if f.unique:
field_output.append("UNIQUE") field_output.append("UNIQUE")
@ -695,9 +737,11 @@ def createcachetable(tablename):
field_output.append("PRIMARY KEY") field_output.append("PRIMARY KEY")
if f.db_index: if f.db_index:
unique = f.unique and "UNIQUE " or "" unique = f.unique and "UNIQUE " or ""
index_output.append("CREATE %sINDEX %s_%s ON %s (%s);" % (unique, tablename, f.column, tablename, f.column)) index_output.append("CREATE %sINDEX %s_%s ON %s (%s);" % \
(unique, tablename, f.column, db.db.quote_name(tablename),
db.db.quote_name(f.column)))
table_output.append(" ".join(field_output)) table_output.append(" ".join(field_output))
full_statement = ["CREATE TABLE %s (" % tablename] full_statement = ["CREATE TABLE %s (" % db.db.quote_name(tablename)]
for i, line in enumerate(table_output): for i, line in enumerate(table_output):
full_statement.append(' %s%s' % (line, i < len(table_output)-1 and ',' or '')) full_statement.append(' %s%s' % (line, i < len(table_output)-1 and ',' or ''))
full_statement.append(');') full_statement.append(');')

View File

@ -57,14 +57,16 @@ def orderfield2column(f, opts):
return f return f
def orderlist2sql(order_list, opts, prefix=''): def orderlist2sql(order_list, opts, prefix=''):
if prefix.endswith('.'):
prefix = db.db.quote_name(prefix[:-1]) + '.'
output = [] output = []
for f in handle_legacy_orderlist(order_list): for f in handle_legacy_orderlist(order_list):
if f.startswith('-'): if f.startswith('-'):
output.append('%s%s DESC' % (prefix, orderfield2column(f[1:], opts))) output.append('%s%s DESC' % (prefix, db.db.quote_name(orderfield2column(f[1:], opts))))
elif f == '?': elif f == '?':
output.append(db.get_random_function_sql()) output.append(db.get_random_function_sql())
else: else:
output.append('%s%s ASC' % (prefix, orderfield2column(f, opts))) output.append('%s%s ASC' % (prefix, db.db.quote_name(orderfield2column(f, opts))))
return ', '.join(output) return ', '.join(output)
def get_module(app_label, module_name): def get_module(app_label, module_name):
@ -785,27 +787,31 @@ def method_save(opts, self):
record_exists = True record_exists = True
if pk_set: if pk_set:
# Determine whether a record with the primary key already exists. # Determine whether a record with the primary key already exists.
cursor.execute("SELECT 1 FROM %s WHERE %s=%%s LIMIT 1" % (opts.db_table, opts.pk.column), [pk_val]) cursor.execute("SELECT 1 FROM %s WHERE %s=%%s LIMIT 1" % \
(db.db.quote_name(opts.db_table), db.db.quote_name(opts.pk.column)), [pk_val])
# If it does already exist, do an UPDATE. # If it does already exist, do an UPDATE.
if cursor.fetchone(): if cursor.fetchone():
db_values = [f.get_db_prep_save(f.pre_save(getattr(self, f.attname), False)) for f in non_pks] db_values = [f.get_db_prep_save(f.pre_save(getattr(self, f.attname), False)) for f in non_pks]
cursor.execute("UPDATE %s SET %s WHERE %s=%%s" % (opts.db_table, cursor.execute("UPDATE %s SET %s WHERE %s=%%s" % \
','.join(['%s=%%s' % f.column for f in non_pks]), opts.pk.attname), (db.db.quote_name(opts.db_table),
','.join(['%s=%%s' % db.db.quote_name(f.column) for f in non_pks]),
db.db.quote_name(opts.pk.attname)),
db_values + [pk_val]) db_values + [pk_val])
else: else:
record_exists = False record_exists = False
if not pk_set or not record_exists: if not pk_set or not record_exists:
field_names = [f.column for f in opts.fields if not isinstance(f, AutoField)] field_names = [db.db.quote_name(f.column) for f in opts.fields if not isinstance(f, AutoField)]
placeholders = ['%s'] * len(field_names) placeholders = ['%s'] * len(field_names)
db_values = [f.get_db_prep_save(f.pre_save(getattr(self, f.attname), True)) for f in opts.fields if not isinstance(f, AutoField)] db_values = [f.get_db_prep_save(f.pre_save(getattr(self, f.attname), True)) for f in opts.fields if not isinstance(f, AutoField)]
if opts.order_with_respect_to: if opts.order_with_respect_to:
field_names.append('_order') field_names.append(db.db.quote_name('_order'))
# TODO: This assumes the database supports subqueries. # TODO: This assumes the database supports subqueries.
placeholders.append('(SELECT COUNT(*) FROM %s WHERE %s = %%s)' % \ placeholders.append('(SELECT COUNT(*) FROM %s WHERE %s = %%s)' % \
(opts.db_table, opts.order_with_respect_to.column)) (db.db.quote_name(opts.db_table), db.db.quote_name(opts.order_with_respect_to.column)))
db_values.append(getattr(self, opts.order_with_respect_to.attname)) db_values.append(getattr(self, opts.order_with_respect_to.attname))
cursor.execute("INSERT INTO %s (%s) VALUES (%s)" % (opts.db_table, cursor.execute("INSERT INTO %s (%s) VALUES (%s)" % \
','.join(field_names), ','.join(placeholders)), db_values) (db.db.quote_name(opts.db_table), ','.join(field_names),
','.join(placeholders)), db_values)
if opts.has_auto_field: if opts.has_auto_field:
setattr(self, opts.pk.attname, db.get_last_insert_id(cursor, opts.db_table, opts.pk.column)) setattr(self, opts.pk.attname, db.get_last_insert_id(cursor, opts.db_table, opts.pk.column))
db.db.commit() db.db.commit()
@ -832,12 +838,17 @@ def method_delete(opts, self):
for sub_obj in getattr(self, 'get_%s_list' % rel_opts_name)(): for sub_obj in getattr(self, 'get_%s_list' % rel_opts_name)():
sub_obj.delete() sub_obj.delete()
for rel_opts, rel_field in opts.get_all_related_many_to_many_objects(): for rel_opts, rel_field in opts.get_all_related_many_to_many_objects():
cursor.execute("DELETE FROM %s WHERE %s_id=%%s" % (rel_field.get_m2m_db_table(rel_opts), cursor.execute("DELETE FROM %s WHERE %s=%%s" % \
self._meta.object_name.lower()), [getattr(self, opts.pk.attname)]) (db.db.quote_name(rel_field.get_m2m_db_table(rel_opts)),
db.db.quote_name(self._meta.object_name.lower() + '_id')), [getattr(self, opts.pk.attname)])
for f in opts.many_to_many: for f in opts.many_to_many:
cursor.execute("DELETE FROM %s WHERE %s_id=%%s" % (f.get_m2m_db_table(opts), self._meta.object_name.lower()), cursor.execute("DELETE FROM %s WHERE %s=%%s" % \
(db.db.quote_name(f.get_m2m_db_table(opts)),
db.db.quote_name(self._meta.object_name.lower() + '_id')),
[getattr(self, opts.pk.attname)])
cursor.execute("DELETE FROM %s WHERE %s=%%s" % \
(db.db.quote_name(opts.db_table), db.db.quote_name(opts.pk.column)),
[getattr(self, opts.pk.attname)]) [getattr(self, opts.pk.attname)])
cursor.execute("DELETE FROM %s WHERE %s=%%s" % (opts.db_table, opts.pk.column), [getattr(self, opts.pk.attname)])
db.db.commit() db.db.commit()
setattr(self, opts.pk.attname, None) setattr(self, opts.pk.attname, None)
for f in opts.fields: for f in opts.fields:
@ -854,16 +865,20 @@ def method_delete(opts, self):
def method_get_next_in_order(opts, order_field, self): def method_get_next_in_order(opts, order_field, self):
if not hasattr(self, '_next_in_order_cache'): if not hasattr(self, '_next_in_order_cache'):
self._next_in_order_cache = opts.get_model_module().get_object(order_by=('_order',), self._next_in_order_cache = opts.get_model_module().get_object(order_by=('_order',),
where=['_order > (SELECT _order FROM %s WHERE %s=%%s)' % (opts.db_table, opts.pk.column), where=['%s > (SELECT %s FROM %s WHERE %s=%%s)' % \
'%s=%%s' % order_field.column], limit=1, (db.db.quote_name('_order'), db.db.quote_name('_order'),
db.db.quote_name(opts.db_table), db.db.quote_name(opts.pk.column)),
'%s=%%s' % db.db.quote_name(order_field.column)], limit=1,
params=[getattr(self, opts.pk.attname), getattr(self, order_field.attname)]) params=[getattr(self, opts.pk.attname), getattr(self, order_field.attname)])
return self._next_in_order_cache return self._next_in_order_cache
def method_get_previous_in_order(opts, order_field, self): def method_get_previous_in_order(opts, order_field, self):
if not hasattr(self, '_previous_in_order_cache'): if not hasattr(self, '_previous_in_order_cache'):
self._previous_in_order_cache = opts.get_model_module().get_object(order_by=('-_order',), self._previous_in_order_cache = opts.get_model_module().get_object(order_by=('-_order',),
where=['_order < (SELECT _order FROM %s WHERE %s=%%s)' % (opts.db_table, opts.pk.column), where=['%s < (SELECT %s FROM %s WHERE %s=%%s)' % \
'%s=%%s' % order_field.column], limit=1, (db.db.quote_name('_order'), db.db.quote_name('_order'),
db.db.quote_name(opts.db_table), db.db.quote_name(opts.pk.column)),
'%s=%%s' % db.db.quote_name(order_field.column)], limit=1,
params=[getattr(self, opts.pk.attname), getattr(self, order_field.attname)]) params=[getattr(self, opts.pk.attname), getattr(self, order_field.attname)])
return self._previous_in_order_cache return self._previous_in_order_cache
@ -888,10 +903,13 @@ def method_get_many_to_many(field_with_rel, self):
cache_var = '_%s_cache' % field_with_rel.name cache_var = '_%s_cache' % field_with_rel.name
if not hasattr(self, cache_var): if not hasattr(self, cache_var):
mod = rel.get_model_module() mod = rel.get_model_module()
sql = "SELECT %s FROM %s a, %s b WHERE a.%s = b.%s_id AND b.%s_id = %%s %s" % \ sql = "SELECT %s FROM %s a, %s b WHERE a.%s = b.%s AND b.%s = %%s %s" % \
(','.join(['a.%s' % f.column for f in rel.fields]), rel.db_table, (','.join(['a.%s' % db.db.quote_name(f.column) for f in rel.fields]),
field_with_rel.get_m2m_db_table(self._meta), rel.pk.column, db.db.quote_name(rel.db_table),
rel.object_name.lower(), self._meta.object_name.lower(), rel.get_order_sql('a')) db.db.quote_name(field_with_rel.get_m2m_db_table(self._meta)),
db.db.quote_name(rel.pk.column),
db.db.quote_name(rel.object_name.lower() + '_id'),
db.db.quote_name(self._meta.object_name.lower() + '_id'), rel.get_order_sql('a'))
cursor = db.db.cursor() cursor = db.db.cursor()
cursor.execute(sql, [getattr(self, self._meta.pk.attname)]) cursor.execute(sql, [getattr(self, self._meta.pk.attname)])
setattr(self, cache_var, [getattr(mod, rel.object_name)(*row) for row in cursor.fetchall()]) setattr(self, cache_var, [getattr(mod, rel.object_name)(*row) for row in cursor.fetchall()])
@ -916,10 +934,16 @@ def method_set_many_to_many(rel_field, self, id_list):
cursor = db.db.cursor() cursor = db.db.cursor()
this_id = getattr(self, self._meta.pk.attname) this_id = getattr(self, self._meta.pk.attname)
if ids_to_delete: if ids_to_delete:
sql = "DELETE FROM %s WHERE %s_id = %%s AND %s_id IN (%s)" % (m2m_table, self._meta.object_name.lower(), rel.object_name.lower(), ','.join(map(str, ids_to_delete))) sql = "DELETE FROM %s WHERE %s = %%s AND %s IN (%s)" % \
(db.db.quote_name(m2m_table),
db.db.quote_name(self._meta.object_name.lower() + '_id'),
db.db.quote_name(rel.object_name.lower() + '_id'), ','.join(map(str, ids_to_delete)))
cursor.execute(sql, [this_id]) cursor.execute(sql, [this_id])
if ids_to_add: if ids_to_add:
sql = "INSERT INTO %s (%s_id, %s_id) VALUES (%%s, %%s)" % (m2m_table, self._meta.object_name.lower(), rel.object_name.lower()) sql = "INSERT INTO %s (%s, %s) VALUES (%%s, %%s)" % \
(db.db.quote_name(m2m_table),
db.db.quote_name(self._meta.object_name.lower() + '_id'),
db.db.quote_name(rel.object_name.lower() + '_id'))
cursor.executemany(sql, [(this_id, i) for i in ids_to_add]) cursor.executemany(sql, [(this_id, i) for i in ids_to_add])
db.db.commit() db.db.commit()
try: try:
@ -965,8 +989,13 @@ def method_set_related_many_to_many(rel_opts, rel_field, self, id_list):
m2m_table = rel_field.get_m2m_db_table(rel_opts) m2m_table = rel_field.get_m2m_db_table(rel_opts)
this_id = getattr(self, self._meta.pk.attname) this_id = getattr(self, self._meta.pk.attname)
cursor = db.db.cursor() cursor = db.db.cursor()
cursor.execute("DELETE FROM %s WHERE %s_id = %%s" % (m2m_table, rel.object_name.lower()), [this_id]) cursor.execute("DELETE FROM %s WHERE %s = %%s" % \
sql = "INSERT INTO %s (%s_id, %s_id) VALUES (%%s, %%s)" % (m2m_table, rel.object_name.lower(), rel_opts.object_name.lower()) (db.db.quote_name(m2m_table),
db.db.quote_name(rel.object_name.lower() + '_id')), [this_id])
sql = "INSERT INTO %s (%s, %s) VALUES (%%s, %%s)" % \
(db.db.quote_name(m2m_table),
db.db.quote_name(rel.object_name.lower() + '_id'),
db.db.quote_name(rel_opts.object_name.lower() + '_id'))
cursor.executemany(sql, [(this_id, i) for i in id_list]) cursor.executemany(sql, [(this_id, i) for i in id_list])
db.db.commit() db.db.commit()
@ -975,7 +1004,10 @@ def method_set_related_many_to_many(rel_opts, rel_field, self, id_list):
def method_set_order(ordered_obj, self, id_list): def method_set_order(ordered_obj, self, id_list):
cursor = db.db.cursor() cursor = db.db.cursor()
# Example: "UPDATE poll_choices SET _order = %s WHERE poll_id = %s AND id = %s" # Example: "UPDATE poll_choices SET _order = %s WHERE poll_id = %s AND id = %s"
sql = "UPDATE %s SET _order = %%s WHERE %s = %%s AND %s = %%s" % (ordered_obj.db_table, ordered_obj.order_with_respect_to.column, ordered_obj.pk.column) sql = "UPDATE %s SET %s = %%s WHERE %s = %%s AND %s = %%s" % \
(db.db.quote_name(ordered_obj.db_table), db.db.quote_name('_order'),
db.db.quote_name(ordered_obj.order_with_respect_to.column),
db.db.quote_name(ordered_obj.pk.column))
rel_val = getattr(self, ordered_obj.order_with_respect_to.rel.field_name) rel_val = getattr(self, ordered_obj.order_with_respect_to.rel.field_name)
cursor.executemany(sql, [(i, rel_val, j) for i, j in enumerate(id_list)]) cursor.executemany(sql, [(i, rel_val, j) for i, j in enumerate(id_list)])
db.db.commit() db.db.commit()
@ -983,7 +1015,11 @@ def method_set_order(ordered_obj, self, id_list):
def method_get_order(ordered_obj, self): def method_get_order(ordered_obj, self):
cursor = db.db.cursor() cursor = db.db.cursor()
# Example: "SELECT id FROM poll_choices WHERE poll_id = %s ORDER BY _order" # Example: "SELECT id FROM poll_choices WHERE poll_id = %s ORDER BY _order"
sql = "SELECT %s FROM %s WHERE %s = %%s ORDER BY _order" % (ordered_obj.pk.column, ordered_obj.db_table, ordered_obj.order_with_respect_to.column) sql = "SELECT %s FROM %s WHERE %s = %%s ORDER BY %s" % \
(db.db.quote_name(ordered_obj.pk.column),
db.db.quote_name(ordered_obj.db_table),
db.db.quote_name(ordered_obj.order_with_respect_to.column),
db.db.quote_name('_order'))
rel_val = getattr(self, ordered_obj.order_with_respect_to.rel.field_name) rel_val = getattr(self, ordered_obj.order_with_respect_to.rel.field_name)
cursor.execute(sql, [rel_val]) cursor.execute(sql, [rel_val])
return [r[0] for r in cursor.fetchall()] return [r[0] for r in cursor.fetchall()]
@ -993,7 +1029,8 @@ def method_get_order(ordered_obj, self):
def method_get_next_or_previous(get_object_func, opts, field, is_next, self, **kwargs): def method_get_next_or_previous(get_object_func, opts, field, is_next, self, **kwargs):
op = is_next and '>' or '<' op = is_next and '>' or '<'
kwargs.setdefault('where', []).append('(%s %s %%s OR (%s = %%s AND %s %s %%s))' % \ kwargs.setdefault('where', []).append('(%s %s %%s OR (%s = %%s AND %s %s %%s))' % \
(field.column, op, field.column, opts.pk.column, op)) (db.db.quote_name(field.column), op, db.db.quote_name(field.column),
db.db.quote_name(opts.pk.column), op))
param = str(getattr(self, field.attname)) param = str(getattr(self, field.attname))
kwargs.setdefault('params', []).extend([param, param, getattr(self, opts.pk.attname)]) kwargs.setdefault('params', []).extend([param, param, getattr(self, opts.pk.attname)])
kwargs['order_by'] = [(not is_next and '-' or '') + field.name, (not is_next and '-' or '') + opts.pk.name] kwargs['order_by'] = [(not is_next and '-' or '') + field.name, (not is_next and '-' or '') + opts.pk.name]
@ -1080,6 +1117,9 @@ def get_absolute_url(opts, func, self):
return settings.ABSOLUTE_URL_OVERRIDES.get('%s.%s' % (opts.app_label, opts.module_name), func)(self) return settings.ABSOLUTE_URL_OVERRIDES.get('%s.%s' % (opts.app_label, opts.module_name), func)(self)
def _get_where_clause(lookup_type, table_prefix, field_name, value): def _get_where_clause(lookup_type, table_prefix, field_name, value):
if table_prefix.endswith('.'):
table_prefix = db.db.quote_name(table_prefix[:-1])+'.'
field_name = db.db.quote_name(field_name)
try: try:
return '%s%s %s %%s' % (table_prefix, field_name, db.OPERATOR_MAPPING[lookup_type]) return '%s%s %s %%s' % (table_prefix, field_name, db.OPERATOR_MAPPING[lookup_type])
except KeyError: except KeyError:
@ -1160,7 +1200,7 @@ def function_get_values_iterator(opts, klass, **kwargs):
cursor = db.db.cursor() cursor = db.db.cursor()
_, sql, params = function_get_sql_clause(opts, **kwargs) _, sql, params = function_get_sql_clause(opts, **kwargs)
select = ['%s.%s' % (opts.db_table, f) for f in fields] select = ['%s.%s' % (db.db.quote_name(opts.db_table), db.db.quote_name(f)) for f in fields]
cursor.execute("SELECT " + (kwargs.get('distinct') and "DISTINCT " or "") + ",".join(select) + sql, params) cursor.execute("SELECT " + (kwargs.get('distinct') and "DISTINCT " or "") + ",".join(select) + sql, params)
while 1: while 1:
rows = cursor.fetchmany(GET_ITERATOR_CHUNK_SIZE) rows = cursor.fetchmany(GET_ITERATOR_CHUNK_SIZE)
@ -1181,14 +1221,16 @@ def _fill_table_cache(opts, select, tables, where, old_prefix, cache_tables_seen
if f.rel and not f.null: if f.rel and not f.null:
db_table = f.rel.to.db_table db_table = f.rel.to.db_table
if db_table not in cache_tables_seen: if db_table not in cache_tables_seen:
tables.append(db_table) tables.append(db.db.quote_name(db_table))
else: # The table was already seen, so give it a table alias. else: # The table was already seen, so give it a table alias.
new_prefix = '%s%s' % (db_table, len(cache_tables_seen)) new_prefix = '%s%s' % (db_table, len(cache_tables_seen))
tables.append('%s %s' % (db_table, new_prefix)) tables.append('%s %s' % (db.db.quote_name(db_table), db.db.quote_name(new_prefix)))
db_table = new_prefix db_table = new_prefix
cache_tables_seen.append(db_table) cache_tables_seen.append(db_table)
where.append('%s.%s = %s.%s' % (old_prefix, f.column, db_table, f.rel.get_related_field().column)) where.append('%s.%s = %s.%s' % \
select.extend(['%s.%s' % (db_table, f2.column) for f2 in f.rel.to.fields]) (db.db.quote_name(old_prefix), db.db.quote_name(f.column),
db.db.quote_name(db_table), db.db.quote_name(f.rel.get_related_field().column)))
select.extend(['%s.%s' % (db.db.quote_name(db_table), db.db.quote_name(f2.column)) for f2 in f.rel.to.fields])
_fill_table_cache(f.rel.to, select, tables, where, db_table, cache_tables_seen) _fill_table_cache(f.rel.to, select, tables, where, db_table, cache_tables_seen)
def _throw_bad_kwarg_error(kwarg): def _throw_bad_kwarg_error(kwarg):
@ -1247,11 +1289,15 @@ def _parse_lookup(kwarg_items, opts, table_count=0):
# Try many-to-many relationships first... # Try many-to-many relationships first...
for f in current_opts.many_to_many: for f in current_opts.many_to_many:
if f.name == current: if f.name == current:
rel_table_alias = 't%s' % table_count rel_table_alias = db.db.quote_name('t%s' % table_count)
table_count += 1 table_count += 1
tables.append('%s %s' % (f.get_m2m_db_table(current_opts), rel_table_alias)) tables.append('%s %s' % \
join_where.append('%s.%s = %s.%s_id' % (current_table_alias, current_opts.pk.column, (db.db.quote_name(f.get_m2m_db_table(current_opts)), rel_table_alias))
rel_table_alias, current_opts.object_name.lower())) join_where.append('%s.%s = %s.%s' % \
(db.db.quote_name(current_table_alias),
db.db.quote_name(current_opts.pk.column),
rel_table_alias,
db.db.quote_name(current_opts.object_name.lower() + '_id')))
# Optimization: In the case of primary-key lookups, we # Optimization: In the case of primary-key lookups, we
# don't have to do an extra join. # don't have to do an extra join.
if lookup_list and lookup_list[0] == f.rel.to.pk.name and lookup_type == 'exact': if lookup_list and lookup_list[0] == f.rel.to.pk.name and lookup_type == 'exact':
@ -1262,9 +1308,13 @@ def _parse_lookup(kwarg_items, opts, table_count=0):
param_required = False param_required = False
else: else:
new_table_alias = 't%s' % table_count new_table_alias = 't%s' % table_count
tables.append('%s %s' % (f.rel.to.db_table, new_table_alias)) tables.append('%s %s' % (db.db.quote_name(f.rel.to.db_table),
join_where.append('%s.%s_id = %s.%s' % (rel_table_alias, f.rel.to.object_name.lower(), db.db.quote_name(new_table_alias)))
new_table_alias, f.rel.to.pk.column)) join_where.append('%s.%s = %s.%s' % \
(db.db.quote_name(rel_table_alias),
db.db.quote_name(f.rel.to.object_name.lower() + '_id'),
db.db.quote_name(new_table_alias),
db.db.quote_name(f.rel.to.pk.column)))
current_table_alias = new_table_alias current_table_alias = new_table_alias
param_required = True param_required = True
current_opts = f.rel.to current_opts = f.rel.to
@ -1287,9 +1337,11 @@ def _parse_lookup(kwarg_items, opts, table_count=0):
params.extend(f.get_db_prep_lookup(lookup_type, kwarg_value)) params.extend(f.get_db_prep_lookup(lookup_type, kwarg_value))
else: else:
new_table_alias = 't%s' % table_count new_table_alias = 't%s' % table_count
tables.append('%s %s' % (f.rel.to.db_table, new_table_alias)) tables.append('%s %s' % \
join_where.append('%s.%s = %s.%s' % (current_table_alias, f.column, \ (db.db.quote_name(f.rel.to.db_table), db.db.quote_name(new_table_alias)))
new_table_alias, f.rel.to.pk.column)) join_where.append('%s.%s = %s.%s' % \
(db.db.quote_name(current_table_alias), db.db.quote_name(f.column),
db.db.quote_name(new_table_alias), db.db.quote_name(f.rel.to.pk.column)))
current_table_alias = new_table_alias current_table_alias = new_table_alias
param_required = True param_required = True
current_opts = f.rel.to current_opts = f.rel.to
@ -1308,8 +1360,9 @@ def _parse_lookup(kwarg_items, opts, table_count=0):
return tables, join_where, where, params, table_count return tables, join_where, where, params, table_count
def function_get_sql_clause(opts, **kwargs): def function_get_sql_clause(opts, **kwargs):
select = ["%s.%s" % (opts.db_table, f.column) for f in opts.fields] select = ["%s.%s" % (db.db.quote_name(opts.db_table), db.db.quote_name(f.column)) for f in opts.fields]
tables = [opts.db_table] + (kwargs.get('tables') and kwargs['tables'][:] or []) tables = [opts.db_table] + (kwargs.get('tables') and kwargs['tables'][:] or [])
tables = [db.db.quote_name(t) for t in tables]
where = kwargs.get('where') and kwargs['where'][:] or [] where = kwargs.get('where') and kwargs['where'][:] or []
params = kwargs.get('params') and kwargs['params'][:] or [] params = kwargs.get('params') and kwargs['params'][:] or []
@ -1328,7 +1381,7 @@ def function_get_sql_clause(opts, **kwargs):
# Add any additional SELECTs passed in via kwargs. # Add any additional SELECTs passed in via kwargs.
if kwargs.get('select'): if kwargs.get('select'):
select.extend(['(%s) AS %s' % (s[1], s[0]) for s in kwargs['select']]) select.extend(['(%s) AS %s' % (db.db.quote_name(s[1]), db.db.quote_name(s[0])) for s in kwargs['select']])
# ORDER BY clause # ORDER BY clause
order_by = [] order_by = []
@ -1342,13 +1395,17 @@ def function_get_sql_clause(opts, **kwargs):
else: else:
col_name = f col_name = f
order = "ASC" order = "ASC"
if "." in col_name:
table_prefix, col_name = col_name.split('.', 1)
table_prefix = db.db.quote_name(table_prefix) + '.'
else:
# Use the database table as a column prefix if it wasn't given, # Use the database table as a column prefix if it wasn't given,
# and if the requested column isn't a custom SELECT. # and if the requested column isn't a custom SELECT.
if "." not in col_name and col_name not in [k[0] for k in kwargs.get('select', [])]: if "." not in col_name and col_name not in [k[0] for k in kwargs.get('select', [])]:
table_prefix = opts.db_table + '.' table_prefix = db.db.quote_name(opts.db_table) + '.'
else: else:
table_prefix = '' table_prefix = ''
order_by.append('%s%s %s' % (table_prefix, orderfield2column(col_name, opts), order)) order_by.append('%s%s %s' % (table_prefix, db.db.quote_name(orderfield2column(col_name, opts)), order))
order_by = ", ".join(order_by) order_by = ", ".join(order_by)
# LIMIT and OFFSET clauses # LIMIT and OFFSET clauses
@ -1363,7 +1420,7 @@ def function_get_sql_clause(opts, **kwargs):
def function_get_in_bulk(opts, klass, *args, **kwargs): def function_get_in_bulk(opts, klass, *args, **kwargs):
id_list = args and args[0] or kwargs['id_list'] id_list = args and args[0] or kwargs['id_list']
assert id_list != [], "get_in_bulk() cannot be passed an empty list." assert id_list != [], "get_in_bulk() cannot be passed an empty list."
kwargs['where'] = ["%s.%s IN (%s)" % (opts.db_table, opts.pk.column, ",".join(['%s'] * len(id_list)))] kwargs['where'] = ["%s.%s IN (%s)" % (db.db.quote_name(opts.db_table), db.db.quote_name(opts.pk.column), ",".join(['%s'] * len(id_list)))]
kwargs['params'] = id_list kwargs['params'] = id_list
obj_list = function_get_list(opts, klass, **kwargs) obj_list = function_get_list(opts, klass, **kwargs)
return dict([(getattr(o, opts.pk.attname), o) for o in obj_list]) return dict([(getattr(o, opts.pk.attname), o) for o in obj_list])
@ -1384,9 +1441,10 @@ def function_get_date_list(opts, field, *args, **kwargs):
assert order in ('ASC', 'DESC'), "'order' must be either 'ASC' or 'DESC'" assert order in ('ASC', 'DESC'), "'order' must be either 'ASC' or 'DESC'"
kwargs['order_by'] = [] # Clear this because it'll mess things up otherwise. kwargs['order_by'] = [] # Clear this because it'll mess things up otherwise.
if field.null: if field.null:
kwargs.setdefault('where', []).append('%s.%s IS NOT NULL' % (opts.db_table, field.column)) kwargs.setdefault('where', []).append('%s.%s IS NOT NULL' % \
(db.db.quote_name(opts.db_table), db.db.quote_name(field.column)))
select, sql, params = function_get_sql_clause(opts, **kwargs) select, sql, params = function_get_sql_clause(opts, **kwargs)
sql = 'SELECT %s %s GROUP BY 1 ORDER BY 1' % (db.get_date_trunc_sql(kind, '%s.%s' % (opts.db_table, field.column)), sql) sql = 'SELECT %s %s GROUP BY 1 ORDER BY 1' % (db.get_date_trunc_sql(kind, '%s.%s' % (db.db.quote_name(opts.db_table), db.db.quote_name(field.column))), sql)
cursor = db.db.cursor() cursor = db.db.cursor()
cursor.execute(sql, params) cursor.execute(sql, params)
# We have to manually run typecast_timestamp(str()) on the results, because # We have to manually run typecast_timestamp(str()) on the results, because

View File

@ -91,12 +91,25 @@ class User(meta.Model):
if not hasattr(self, '_group_perm_cache'): if not hasattr(self, '_group_perm_cache'):
import sets import sets
cursor = db.cursor() cursor = db.cursor()
cursor.execute(""" # The SQL below works out to the following, after DB quoting:
SELECT p.package, p.codename # cursor.execute("""
FROM auth_permissions p, auth_groups_permissions gp, auth_users_groups ug # SELECT p.package, p.codename
WHERE p.id = gp.permission_id # FROM auth_permissions p, auth_groups_permissions gp, auth_users_groups ug
AND gp.group_id = ug.group_id # WHERE p.id = gp.permission_id
AND ug.user_id = %s""", [self.id]) # AND gp.group_id = ug.group_id
# AND ug.user_id = %s""", [self.id])
sql = """
SELECT p.%s, p.%s
FROM %s p, %s gp, %s ug
WHERE p.%s = gp.%s
AND gp.%s = ug.%s
AND ug.%s = %%s""" % (
db.quote_name('package'), db.quote_name('codename'),
db.quote_name('auth_permissions'), db.quote_name('auth_groups_permissions'),
db.quote_name('auth_users_groups'), db.quote_name('id'),
db.quote_name('permission_id'), db.quote_name('group_id'),
db.quote_name('group_id'), db.quote_name('user_id'))
cursor.execute(sql, [self.id])
self._group_perm_cache = sets.Set(["%s.%s" % (row[0], row[1]) for row in cursor.fetchall()]) self._group_perm_cache = sets.Set(["%s.%s" % (row[0], row[1]) for row in cursor.fetchall()])
return self._group_perm_cache return self._group_perm_cache

View File

@ -34,15 +34,21 @@ Django will use ``first_name`` and ``last_name`` as the database column names.
Each field type, except for ``ForeignKey``, ``ManyToManyField`` and Each field type, except for ``ForeignKey``, ``ManyToManyField`` and
``OneToOneField``, takes an optional first positional argument -- a ``OneToOneField``, takes an optional first positional argument -- a
human-readable name. If the human-readable name isn't given, Django will use human-readable name. If the human-readable name isn't given, Django will
the machine-readable name, converting underscores to spaces. automatically create the human-readable name by using the machine-readable
name, converting underscores to spaces.
Example:: In this example, the human-readable name is ``"Person's first name"``::
first_name = meta.CharField("Person's first name", maxlength=30) first_name = meta.CharField("Person's first name", maxlength=30)
For ``ForeignKey``, ``ManyToManyField`` and ``OneToOneField``, use the In this example, the human-readable name is ``"first name"``::
``verbose_name`` keyword argument::
first_name = meta.CharField(maxlength=30)
``ForeignKey``, ``ManyToManyField`` and ``OneToOneField`` require the first
argument to be a model class, so use the ``verbose_name`` keyword argument to
specify the human-readable name::
poll = meta.ForeignKey(Poll, verbose_name="the related poll") poll = meta.ForeignKey(Poll, verbose_name="the related poll")
sites = meta.ManyToManyField(Site, verbose_name="list of sites") sites = meta.ManyToManyField(Site, verbose_name="list of sites")
@ -111,6 +117,11 @@ The following arguments are available to all field types. All are optional.
The name of the database column to use for this field. If this isn't given, The name of the database column to use for this field. If this isn't given,
Django will use the field's name. Django will use the field's name.
If your database column name is an SQL reserved word, or contains
characters that aren't allowed in Python variable names -- notably, the
hyphen -- that's OK. Django quotes column and table names behind the
scenes.
``db_index`` ``db_index``
If ``True``, ``django-admin.py sqlindexes`` will output a ``CREATE INDEX`` If ``True``, ``django-admin.py sqlindexes`` will output a ``CREATE INDEX``
statement for this field. statement for this field.
@ -700,6 +711,10 @@ Here's a list of all possible ``META`` options. No options are required. Adding
If this isn't given, Django will use ``app_label + '_' + module_name``. If this isn't given, Django will use ``app_label + '_' + module_name``.
If your database table name is an SQL reserved word, or contains characters
that aren't allowed in Python variable names -- notably, the hyphen --
that's OK. Django quotes column and table names behind the scenes.
``exceptions`` ``exceptions``
Names of extra exception subclasses to include in the generated module. Names of extra exception subclasses to include in the generated module.
These exceptions are available from instance methods and from module-level These exceptions are available from instance methods and from module-level
@ -732,8 +747,8 @@ Here's a list of all possible ``META`` options. No options are required. Adding
module_name = "pizza_orders" module_name = "pizza_orders"
If this isn't given, Django will use a lowercased version of the class If this isn't given, Django will use a lowercased version of the class
name, plus "s". This "poor man's pluralization" is intentional: Any other name, plus ``"s"``. This "poor man's pluralization" is intentional: Any
level of magic pluralization would get confusing. other level of magic pluralization would get confusing.
``order_with_respect_to`` ``order_with_respect_to``
Marks this object as "orderable" with respect to the given field. This is Marks this object as "orderable" with respect to the given field. This is

View File

@ -1,4 +1,4 @@
__all__ = ['basic', 'repr', 'custom_methods', 'many_to_one', 'many_to_many', __all__ = ['basic', 'repr', 'custom_methods', 'many_to_one', 'many_to_many',
'ordering', 'lookup', 'get_latest', 'm2m_intermediary', 'one_to_one', 'ordering', 'lookup', 'get_latest', 'm2m_intermediary', 'one_to_one',
'm2o_recursive', 'm2o_recursive2', 'save_delete_hooks', 'custom_pk', 'm2o_recursive', 'm2o_recursive2', 'save_delete_hooks', 'custom_pk',
'subclassing', 'many_to_one_null', 'custom_columns'] 'subclassing', 'many_to_one_null', 'custom_columns', 'reserved_names']

View File

@ -0,0 +1,47 @@
"""
18. Using SQL reserved names
Need to use a reserved SQL name as a column name or table name? Need to include
a hyphen in a column or table name? No problem. Django quotes names
appropriately behind the scenes, so your database won't complain about
reserved-name usage.
"""
from django.core import meta
class Thing(meta.Model):
when = meta.CharField(maxlength=1, primary_key=True)
join = meta.CharField(maxlength=1)
like = meta.CharField(maxlength=1)
drop = meta.CharField(maxlength=1)
alter = meta.CharField(maxlength=1)
having = meta.CharField(maxlength=1)
where = meta.CharField(maxlength=1)
has_hyphen = meta.CharField(maxlength=1, db_column='has-hyphen')
class META:
db_table = 'select'
def __repr__(self):
return self.when
API_TESTS = """
>>> t = things.Thing(when='a', join='b', like='c', drop='d', alter='e', having='f', where='g', has_hyphen='h')
>>> t.save()
>>> print t.when
a
>>> u = things.Thing(when='h', join='i', like='j', drop='k', alter='l', having='m', where='n')
>>> u.save()
>>> print u.when
h
>>> things.get_list(order_by=['when'])
[a, h]
>>> v = things.get_object(pk='a')
>>> print v.join
b
>>> print v.where
g
>>> things.get_list(order_by=['select.when'])
[a, h]
"""