Skip to content

Commit

Permalink
[soc2010/query-refactor] Merged up to trunk r13350.
Browse files Browse the repository at this point in the history
  • Loading branch information
alex committed Jun 14, 2010
1 parent 28499bb commit 4f395e7
Show file tree
Hide file tree
Showing 6 changed files with 34 additions and 105 deletions.
1 change: 0 additions & 1 deletion AUTHORS
Expand Up @@ -220,7 +220,6 @@ answer newbie questions, and generally made Django that much better:
Kieran Holland <http://www.kieranholland.com>
Sung-Jin Hong <serialx.net@gmail.com>
Leo "hylje" Honkanen <sealage@gmail.com>
Matt Hoskins <skaffenuk@googlemail.com>
Tareque Hossain <http://www.codexn.com>
Richard House <Richard.House@i-logue.com>
Robert Rock Howard <http://djangomojo.com/>
Expand Down
3 changes: 1 addition & 2 deletions django/db/backends/postgresql/creation.py
@@ -1,5 +1,4 @@
from django.db.backends.creation import BaseDatabaseCreation
from django.db.backends.util import truncate_name

class DatabaseCreation(BaseDatabaseCreation):
# This dictionary maps Field objects to their associated PostgreSQL column
Expand Down Expand Up @@ -52,7 +51,7 @@ def sql_indexes_for_field(self, model, f, style):

def get_index_sql(index_name, opclass=''):
return (style.SQL_KEYWORD('CREATE INDEX') + ' ' +
style.SQL_TABLE(qn(truncate_name(index_name,self.connection.ops.max_name_length()))) + ' ' +
style.SQL_TABLE(qn(index_name)) + ' ' +
style.SQL_KEYWORD('ON') + ' ' +
style.SQL_TABLE(qn(db_table)) + ' ' +
"(%s%s)" % (style.SQL_FIELD(qn(f.column)), opclass) +
Expand Down
30 changes: 11 additions & 19 deletions django/db/backends/postgresql/operations.py
Expand Up @@ -54,9 +54,7 @@ def field_cast_sql(self, db_type):
return '%s'

def last_insert_id(self, cursor, table_name, pk_name):
# Use pg_get_serial_sequence to get the underlying sequence name
# from the table name and column name (available since PostgreSQL 8)
cursor.execute("SELECT CURRVAL(pg_get_serial_sequence('%s','%s'))" % (table_name, pk_name))
cursor.execute("SELECT CURRVAL('\"%s_%s_seq\"')" % (table_name, pk_name))
return cursor.fetchone()[0]

def no_limit_value(self):
Expand Down Expand Up @@ -92,14 +90,13 @@ def sql_flush(self, style, tables, sequences):
for sequence_info in sequences:
table_name = sequence_info['table']
column_name = sequence_info['column']
if not (column_name and len(column_name) > 0):
# This will be the case if it's an m2m using an autogenerated
# intermediate table (see BaseDatabaseIntrospection.sequence_list)
column_name = 'id'
sql.append("%s setval(pg_get_serial_sequence('%s','%s'), 1, false);" % \
if column_name and len(column_name) > 0:
sequence_name = '%s_%s_seq' % (table_name, column_name)
else:
sequence_name = '%s_id_seq' % table_name
sql.append("%s setval('%s', 1, false);" % \
(style.SQL_KEYWORD('SELECT'),
style.SQL_TABLE(table_name),
style.SQL_FIELD(column_name))
style.SQL_FIELD(self.quote_name(sequence_name)))
)
return sql
else:
Expand All @@ -113,15 +110,11 @@ def sequence_reset_sql(self, style, model_list):
# Use `coalesce` to set the sequence for each model to the max pk value if there are records,
# or 1 if there are none. Set the `is_called` property (the third argument to `setval`) to true
# if there are records (as the max pk value is already in use), otherwise set it to false.
# Use pg_get_serial_sequence to get the underlying sequence name from the table name
# and column name (available since PostgreSQL 8)

for f in model._meta.local_fields:
if isinstance(f, models.AutoField):
output.append("%s setval(pg_get_serial_sequence('%s','%s'), coalesce(max(%s), 1), max(%s) %s null) %s %s;" % \
output.append("%s setval('%s', coalesce(max(%s), 1), max(%s) %s null) %s %s;" % \
(style.SQL_KEYWORD('SELECT'),
style.SQL_TABLE(model._meta.db_table),
style.SQL_FIELD(f.column),
style.SQL_FIELD(qn('%s_%s_seq' % (model._meta.db_table, f.column))),
style.SQL_FIELD(qn(f.column)),
style.SQL_FIELD(qn(f.column)),
style.SQL_KEYWORD('IS NOT'),
Expand All @@ -130,10 +123,9 @@ def sequence_reset_sql(self, style, model_list):
break # Only one AutoField is allowed per model, so don't bother continuing.
for f in model._meta.many_to_many:
if not f.rel.through:
output.append("%s setval(pg_get_serial_sequence('%s','%s'), coalesce(max(%s), 1), max(%s) %s null) %s %s;" % \
output.append("%s setval('%s', coalesce(max(%s), 1), max(%s) %s null) %s %s;" % \
(style.SQL_KEYWORD('SELECT'),
style.SQL_TABLE(model._meta.db_table),
style.SQL_FIELD('id'),
style.SQL_FIELD(qn('%s_id_seq' % f.m2m_db_table())),
style.SQL_FIELD(qn('id')),
style.SQL_FIELD(qn('id')),
style.SQL_KEYWORD('IS NOT'),
Expand Down
33 changes: 19 additions & 14 deletions docs/releases/1.2.txt
Expand Up @@ -21,11 +21,11 @@ Overview
Django 1.2 introduces several large, important new features, including:

* Support for `multiple database connections`_ in a single Django instance.

* `Model validation`_ inspired by Django's form validation.

* Vastly `improved protection against Cross-Site Request Forgery`_ (CSRF).

* A new `user "messages" framework`_ with support for cookie- and session-based
message for both anonymous and authenticated users.

Expand All @@ -49,9 +49,9 @@ be found below`_.

.. seealso::

`Django Advent`_ covered the release of Django 1.2 with a series of
`Django Advent`_ covered the release of Django 1.2 with a series of
articles and tutorials that cover some of the new features in depth.

.. _django advent: http://djangoadvent.com/

Wherever possible these features have been introduced in a backwards-compatible
Expand All @@ -66,20 +66,20 @@ backwards-incompatible. The big changes are:
* The new CSRF protection framework is not backwards-compatible with
the old system. Users of the old system will not be affected until
the old system is removed in Django 1.4.

However, upgrading to the new CSRF protection framework requires a few
important backwards-incompatible changes, detailed in `CSRF Protection`_,
below.

* Authors of custom :class:`~django.db.models.Field` subclasses should be
aware that a number of methods have had a change in prototype, detailed
under `get_db_prep_*() methods on Field`_, below.

* The internals of template tags have changed somewhat; authors of custom
template tags that need to store state (e.g. custom control flow tags)
should ensure that their code follows the new rules for `stateful template
tags`_

* The :func:`~django.contrib.auth.decorators.user_passes_test`,
:func:`~django.contrib.auth.decorators.login_required`, and
:func:`~django.contrib.auth.decorators.permission_required`, decorators
Expand Down Expand Up @@ -435,6 +435,8 @@ database-compatible values. A custom field might look something like::

class CustomModelField(models.Field):
# ...
def db_type(self):
# ...

def get_db_prep_save(self, value):
# ...
Expand All @@ -451,6 +453,9 @@ two extra methods have been introduced::
class CustomModelField(models.Field):
# ...

def db_type(self, connection):
# ...

def get_prep_value(self, value):
# ...

Expand All @@ -467,10 +472,10 @@ two extra methods have been introduced::
# ...

These changes are required to support multiple databases --
``get_db_prep_*`` can no longer make any assumptions regarding the
database for which it is preparing. The ``connection`` argument now
provides the preparation methods with the specific connection for
which the value is being prepared.
``db_type`` and ``get_db_prep_*`` can no longer make any assumptions
regarding the database for which it is preparing. The ``connection``
argument now provides the preparation methods with the specific
connection for which the value is being prepared.

The two new methods exist to differentiate general data-preparation
requirements from requirements that are database-specific. The
Expand Down Expand Up @@ -603,13 +608,13 @@ new keyword and so is not a valid variable name in this tag.
--------------

``LazyObject`` is an undocumented-but-often-used utility class used for lazily
wrapping other objects of unknown type.
wrapping other objects of unknown type.

In Django 1.1 and earlier, it handled introspection in a non-standard way,
depending on wrapped objects implementing a public method named
``get_all_members()``. Since this could easily lead to name clashes, it has been
changed to use the standard Python introspection method, involving
``__members__`` and ``__dir__()``.
``__members__`` and ``__dir__()``.

If you used ``LazyObject`` in your own code
and implemented the ``get_all_members()`` method for wrapped objects, you'll need
Expand Down
19 changes: 1 addition & 18 deletions tests/regressiontests/backends/models.py
@@ -1,7 +1,5 @@
from django.conf import settings
from django.db import models
from django.db import connection, DEFAULT_DB_ALIAS

from django.db import connection

class Square(models.Model):
root = models.IntegerField()
Expand All @@ -10,33 +8,18 @@ class Square(models.Model):
def __unicode__(self):
return "%s ** 2 == %s" % (self.root, self.square)


class Person(models.Model):
first_name = models.CharField(max_length=20)
last_name = models.CharField(max_length=20)

def __unicode__(self):
return u'%s %s' % (self.first_name, self.last_name)


class SchoolClass(models.Model):
year = models.PositiveIntegerField()
day = models.CharField(max_length=9, blank=True)
last_updated = models.DateTimeField()

# Unfortunately, the following model breaks MySQL hard.
# Until #13711 is fixed, this test can't be run under MySQL.
if settings.DATABASES[DEFAULT_DB_ALIAS]['ENGINE'] != 'django.db.backends.mysql':
class VeryLongModelNameZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ(models.Model):
class Meta:
# We need to use a short actual table name or
# we hit issue #8548 which we're not testing!
verbose_name = 'model_with_long_table_name'
primary_key_is_quite_long_zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz = models.AutoField(primary_key=True)
charfield_is_quite_long_zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz = models.CharField(max_length=100)
m2m_also_quite_long_zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz = models.ManyToManyField(Person,blank=True)


qn = connection.ops.quote_name

__test__ = {'API_TESTS': """
Expand Down
53 changes: 2 additions & 51 deletions tests/regressiontests/backends/tests.py
@@ -1,17 +1,13 @@
# -*- coding: utf-8 -*-
# Unit and doctests for specific database backends.
import datetime
import models
import unittest

from django.conf import settings
from django.core import management
from django.core.management.color import no_style
from django.db import backend, connection, DEFAULT_DB_ALIAS
from django.db.backends.signals import connection_created
from django.conf import settings
from django.test import TestCase

from regressiontests.backends import models

class Callproc(unittest.TestCase):

def test_dbms_session(self):
Expand Down Expand Up @@ -80,7 +76,6 @@ def test_django_extract(self):
classes = models.SchoolClass.objects.filter(last_updated__day=20)
self.assertEqual(len(classes), 1)


class ParameterHandlingTest(TestCase):
def test_bad_parameter_count(self):
"An executemany call with too many/not enough parameters will raise an exception (Refs #12612)"
Expand All @@ -93,50 +88,6 @@ def test_bad_parameter_count(self):
self.assertRaises(Exception, cursor.executemany, query, [(1,2,3),])
self.assertRaises(Exception, cursor.executemany, query, [(1,),])

# Unfortunately, the following tests would be a good test to run on all
# backends, but it breaks MySQL hard. Until #13711 is fixed, it can't be run
# everywhere (although it would be an effective test of #13711).
if settings.DATABASES[DEFAULT_DB_ALIAS]['ENGINE'] != 'django.db.backends.mysql':
class LongNameTest(TestCase):
"""Long primary keys and model names can result in a sequence name
that exceeds the database limits, which will result in truncation
on certain databases (e.g., Postgres). The backend needs to use
the correct sequence name in last_insert_id and other places, so
check it is. Refs #8901.
"""

def test_sequence_name_length_limits_create(self):
"""Test creation of model with long name and long pk name doesn't error. Ref #8901"""
models.VeryLongModelNameZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ.objects.create()

def test_sequence_name_length_limits_m2m(self):
"""Test an m2m save of a model with a long name and a long m2m field name doesn't error as on Django >=1.2 this now uses object saves. Ref #8901"""
obj = models.VeryLongModelNameZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ.objects.create()
rel_obj = models.Person.objects.create(first_name='Django', last_name='Reinhardt')
obj.m2m_also_quite_long_zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz.add(rel_obj)

def test_sequence_name_length_limits_flush(self):
"""Test that sequence resetting as part of a flush with model with long name and long pk name doesn't error. Ref #8901"""
# A full flush is expensive to the full test, so we dig into the
# internals to generate the likely offending SQL and run it manually

# Some convenience aliases
VLM = models.VeryLongModelNameZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ
VLM_m2m = VLM.m2m_also_quite_long_zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz.through
tables = [
VLM._meta.db_table,
VLM_m2m._meta.db_table,
]
sequences = [
{
'column': VLM._meta.pk.column,
'table': VLM._meta.db_table
},
]
cursor = connection.cursor()
for statement in connection.ops.sql_flush(no_style(), tables, sequences):
cursor.execute(statement)


def connection_created_test(sender, **kwargs):
print 'connection_created signal'
Expand Down

0 comments on commit 4f395e7

Please sign in to comment.