Permalink
Browse files

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
  • Loading branch information...
1 parent 6e40d8c commit f6bf41e59ac032c253e3b4e1b267010c6d456a26 @adrianholovaty adrianholovaty committed Nov 14, 2005
@@ -7,8 +7,10 @@
def clean_up():
# Clean up old database records
cursor = db.cursor()
- cursor.execute("DELETE FROM core_sessions WHERE expire_date < NOW()")
- cursor.execute("DELETE FROM registration_challenges WHERE request_date < NOW() - INTERVAL '1 week'")
+ cursor.execute("DELETE FROM %s WHERE %s < NOW()" % \
+ (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()
if __name__ == "__main__":
@@ -149,8 +149,8 @@ def get_relations(cursor, table_name):
'NullBooleanField': 'bit',
'OneToOneField': 'int',
'PhoneNumberField': 'varchar(20)',
- 'PositiveIntegerField': 'int CONSTRAINT [CK_int_pos_%(name)s] CHECK ([%(name)s] > 0)',
- 'PositiveSmallIntegerField': 'smallint CONSTRAINT [CK_smallint_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_%(column)s] CHECK ([%(column)s] > 0)',
'SlugField': 'varchar(50)',
'SmallIntegerField': 'smallint',
'TextField': 'text',
@@ -170,8 +170,8 @@ def get_relations(cursor, table_name):
'NullBooleanField': 'boolean',
'OneToOneField': 'integer',
'PhoneNumberField': 'varchar(20)',
- 'PositiveIntegerField': 'integer CHECK (%(name)s >= 0)',
- 'PositiveSmallIntegerField': 'smallint CHECK (%(name)s >= 0)',
+ 'PositiveIntegerField': 'integer CHECK (%(column)s >= 0)',
+ 'PositiveSmallIntegerField': 'smallint CHECK (%(column)s >= 0)',
'SlugField': 'varchar(50)',
'SmallIntegerField': 'smallint',
'TextField': 'text',
View

Large diffs are not rendered by default.

Oops, something went wrong.

Large diffs are not rendered by default.

Oops, something went wrong.
View
@@ -91,12 +91,25 @@ def get_group_permissions(self):
if not hasattr(self, '_group_perm_cache'):
import sets
cursor = db.cursor()
- cursor.execute("""
- SELECT p.package, p.codename
- FROM auth_permissions p, auth_groups_permissions gp, auth_users_groups ug
- WHERE p.id = gp.permission_id
- AND gp.group_id = ug.group_id
- AND ug.user_id = %s""", [self.id])
+ # The SQL below works out to the following, after DB quoting:
+ # cursor.execute("""
+ # SELECT p.package, p.codename
+ # FROM auth_permissions p, auth_groups_permissions gp, auth_users_groups ug
+ # WHERE p.id = gp.permission_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()])
return self._group_perm_cache
View
@@ -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
``OneToOneField``, takes an optional first positional argument -- a
-human-readable name. If the human-readable name isn't given, Django will use
-the machine-readable name, converting underscores to spaces.
+human-readable name. If the human-readable name isn't given, Django will
+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)
-For ``ForeignKey``, ``ManyToManyField`` and ``OneToOneField``, use the
-``verbose_name`` keyword argument::
+In this example, the human-readable name is ``"first name"``::
+
+ 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")
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,
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``
If ``True``, ``django-admin.py sqlindexes`` will output a ``CREATE INDEX``
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 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``
Names of extra exception subclasses to include in the generated module.
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"
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
- level of magic pluralization would get confusing.
+ name, plus ``"s"``. This "poor man's pluralization" is intentional: Any
+ other level of magic pluralization would get confusing.
``order_with_respect_to``
Marks this object as "orderable" with respect to the given field. This is
@@ -1,4 +1,4 @@
__all__ = ['basic', 'repr', 'custom_methods', 'many_to_one', 'many_to_many',
'ordering', 'lookup', 'get_latest', 'm2m_intermediary', 'one_to_one',
'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']
@@ -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]
+"""

0 comments on commit f6bf41e

Please sign in to comment.