summaryrefslogtreecommitdiff
path: root/django/db
diff options
context:
space:
mode:
authorAndrew Godwin <andrew@aeracode.org>2013-06-28 17:32:57 +0100
committerAndrew Godwin <andrew@aeracode.org>2013-06-28 17:32:57 +0100
commit7a47ba6f6aeca36b8b092dbafd36abb342d34d4b (patch)
treee959922f7d4d08057225459e31697f410e26df26 /django/db
parente2d7e83256234251a81ad3388428f6579795a672 (diff)
parent48dd1e63bbb93479666208535a56f8c7c4aeab3a (diff)
Merge remote-tracking branch 'core/master' into schema-alteration
Conflicts: django/db/backends/__init__.py django/db/models/fields/related.py tests/field_deconstruction/tests.py
Diffstat (limited to 'django/db')
-rw-r--r--django/db/backends/__init__.py14
-rw-r--r--django/db/backends/oracle/base.py66
-rw-r--r--django/db/backends/postgresql_psycopg2/base.py2
-rw-r--r--django/db/backends/postgresql_psycopg2/creation.py2
-rw-r--r--django/db/backends/postgresql_psycopg2/operations.py2
-rw-r--r--django/db/backends/sqlite3/base.py7
-rw-r--r--django/db/models/fields/related.py22
-rw-r--r--django/db/models/options.py24
-rw-r--r--django/db/models/query.py5
-rw-r--r--django/db/transaction.py20
10 files changed, 125 insertions, 39 deletions
diff --git a/django/db/backends/__init__.py b/django/db/backends/__init__.py
index 2fcf6b9604..26ca9a7fa1 100644
--- a/django/db/backends/__init__.py
+++ b/django/db/backends/__init__.py
@@ -330,6 +330,15 @@ class BaseDatabaseWrapper(object):
self._set_autocommit(autocommit)
self.autocommit = autocommit
+ def set_rollback(self, rollback):
+ """
+ Set or unset the "needs rollback" flag -- for *advanced use* only.
+ """
+ if not self.in_atomic_block:
+ raise TransactionManagementError(
+ "needs_rollback doesn't work outside of an 'atomic' block.")
+ self.needs_rollback = rollback
+
def validate_no_atomic_block(self):
"""
Raise an error if an atomic block is active.
@@ -623,6 +632,11 @@ class BaseDatabaseFeatures(object):
# Does it support CHECK constraints?
supports_check_constraints = True
+ # Does the backend support 'pyformat' style ("... %(name)s ...", {'name': value})
+ # parameter passing? Note this can be provided by the backend even if not
+ # supported by the Python driver
+ supports_paramstyle_pyformat = True
+
def __init__(self, connection):
self.connection = connection
diff --git a/django/db/backends/oracle/base.py b/django/db/backends/oracle/base.py
index 3105ad17e3..5fb806807d 100644
--- a/django/db/backends/oracle/base.py
+++ b/django/db/backends/oracle/base.py
@@ -762,20 +762,37 @@ class FormatStylePlaceholderCursor(object):
self.cursor.arraysize = 100
def _format_params(self, params):
- return tuple([OracleParam(p, self, True) for p in params])
+ try:
+ return dict((k,OracleParam(v, self, True)) for k,v in params.items())
+ except AttributeError:
+ return tuple([OracleParam(p, self, True) for p in params])
def _guess_input_sizes(self, params_list):
- sizes = [None] * len(params_list[0])
- for params in params_list:
- for i, value in enumerate(params):
- if value.input_size:
- sizes[i] = value.input_size
- self.setinputsizes(*sizes)
+ # Try dict handling; if that fails, treat as sequence
+ if hasattr(params_list[0], 'keys'):
+ sizes = {}
+ for params in params_list:
+ for k, value in params.items():
+ if value.input_size:
+ sizes[k] = value.input_size
+ self.setinputsizes(**sizes)
+ else:
+ # It's not a list of dicts; it's a list of sequences
+ sizes = [None] * len(params_list[0])
+ for params in params_list:
+ for i, value in enumerate(params):
+ if value.input_size:
+ sizes[i] = value.input_size
+ self.setinputsizes(*sizes)
def _param_generator(self, params):
- return [p.force_bytes for p in params]
+ # Try dict handling; if that fails, treat as sequence
+ if hasattr(params, 'items'):
+ return dict((k, v.force_bytes) for k,v in params.items())
+ else:
+ return [p.force_bytes for p in params]
- def execute(self, query, params=None):
+ def _fix_for_params(self, query, params):
# cx_Oracle wants no trailing ';' for SQL statements. For PL/SQL, it
# it does want a trailing ';' but not a trailing '/'. However, these
# characters must be included in the original query in case the query
@@ -785,10 +802,18 @@ class FormatStylePlaceholderCursor(object):
if params is None:
params = []
query = convert_unicode(query, self.charset)
+ elif hasattr(params, 'keys'):
+ # Handle params as dict
+ args = dict((k, ":%s"%k) for k in params.keys())
+ query = convert_unicode(query % args, self.charset)
else:
- params = self._format_params(params)
+ # Handle params as sequence
args = [(':arg%d' % i) for i in range(len(params))]
query = convert_unicode(query % tuple(args), self.charset)
+ return query, self._format_params(params)
+
+ def execute(self, query, params=None):
+ query, params = self._fix_for_params(query, params)
self._guess_input_sizes([params])
try:
return self.cursor.execute(query, self._param_generator(params))
@@ -799,22 +824,15 @@ class FormatStylePlaceholderCursor(object):
raise
def executemany(self, query, params=None):
- # cx_Oracle doesn't support iterators, convert them to lists
- if params is not None and not isinstance(params, (list, tuple)):
- params = list(params)
- try:
- args = [(':arg%d' % i) for i in range(len(params[0]))]
- except (IndexError, TypeError):
+ if not params:
# No params given, nothing to do
return None
- # cx_Oracle wants no trailing ';' for SQL statements. For PL/SQL, it
- # it does want a trailing ';' but not a trailing '/'. However, these
- # characters must be included in the original query in case the query
- # is being passed to SQL*Plus.
- if query.endswith(';') or query.endswith('/'):
- query = query[:-1]
- query = convert_unicode(query % tuple(args), self.charset)
- formatted = [self._format_params(i) for i in params]
+ # uniform treatment for sequences and iterables
+ params_iter = iter(params)
+ query, firstparams = self._fix_for_params(query, next(params_iter))
+ # we build a list of formatted params; as we're going to traverse it
+ # more than once, we can't make it lazy by using a generator
+ formatted = [firstparams]+[self._format_params(p) for p in params_iter]
self._guess_input_sizes(formatted)
try:
return self.cursor.executemany(query,
diff --git a/django/db/backends/postgresql_psycopg2/base.py b/django/db/backends/postgresql_psycopg2/base.py
index de86758653..bf75459b4a 100644
--- a/django/db/backends/postgresql_psycopg2/base.py
+++ b/django/db/backends/postgresql_psycopg2/base.py
@@ -6,7 +6,6 @@ Requires psycopg 2: http://initd.org/projects/psycopg2
import logging
import sys
-from django.db import utils
from django.db.backends import *
from django.db.backends.postgresql_psycopg2.operations import DatabaseOperations
from django.db.backends.postgresql_psycopg2.client import DatabaseClient
@@ -17,7 +16,6 @@ from django.db.backends.postgresql_psycopg2.schema import DatabaseSchemaEditor
from django.utils.encoding import force_str
from django.utils.functional import cached_property
from django.utils.safestring import SafeText, SafeBytes
-from django.utils import six
from django.utils.timezone import utc
try:
diff --git a/django/db/backends/postgresql_psycopg2/creation.py b/django/db/backends/postgresql_psycopg2/creation.py
index 819dd7a29e..1bfda6354d 100644
--- a/django/db/backends/postgresql_psycopg2/creation.py
+++ b/django/db/backends/postgresql_psycopg2/creation.py
@@ -1,5 +1,3 @@
-import psycopg2.extensions
-
from django.db.backends.creation import BaseDatabaseCreation
from django.db.backends.util import truncate_name
diff --git a/django/db/backends/postgresql_psycopg2/operations.py b/django/db/backends/postgresql_psycopg2/operations.py
index f96757da8b..c5aab84693 100644
--- a/django/db/backends/postgresql_psycopg2/operations.py
+++ b/django/db/backends/postgresql_psycopg2/operations.py
@@ -69,7 +69,7 @@ class DatabaseOperations(BaseDatabaseOperations):
# Cast text lookups to text to allow things like filter(x__contains=4)
if lookup_type in ('iexact', 'contains', 'icontains', 'startswith',
- 'istartswith', 'endswith', 'iendswith'):
+ 'istartswith', 'endswith', 'iendswith', 'regex', 'iregex'):
lookup = "%s::text"
# Use UPPER(x) for case-insensitive lookups; it's faster.
diff --git a/django/db/backends/sqlite3/base.py b/django/db/backends/sqlite3/base.py
index a53d3ee53e..64da0a9f25 100644
--- a/django/db/backends/sqlite3/base.py
+++ b/django/db/backends/sqlite3/base.py
@@ -20,6 +20,7 @@ from django.db.backends.sqlite3.schema import DatabaseSchemaEditor
from django.db.models import fields
from django.db.models.sql import aggregates
from django.utils.dateparse import parse_date, parse_datetime, parse_time
+from django.utils.encoding import force_text
from django.utils.functional import cached_property
from django.utils.safestring import SafeBytes
from django.utils import six
@@ -103,6 +104,7 @@ class DatabaseFeatures(BaseDatabaseFeatures):
supports_foreign_keys = False
supports_check_constraints = False
autocommits_when_autocommit_is_off = True
+ supports_paramstyle_pyformat = False
@cached_property
def uses_savepoints(self):
@@ -253,6 +255,9 @@ class DatabaseOperations(BaseDatabaseOperations):
and gets dates and datetimes wrong.
For consistency with other backends, coerce when required.
"""
+ if value is None:
+ return None
+
internal_type = field.get_internal_type()
if internal_type == 'DecimalField':
return util.typecast_decimal(field.format_number(value))
@@ -526,4 +531,4 @@ def _sqlite_format_dtdelta(dt, conn, days, secs, usecs):
return str(dt)
def _sqlite_regexp(re_pattern, re_string):
- return bool(re.search(re_pattern, re_string))
+ return bool(re.search(re_pattern, force_text(re_string))) if re_string is not None else False
diff --git a/django/db/models/fields/related.py b/django/db/models/fields/related.py
index 64890fff47..9f2e01e781 100644
--- a/django/db/models/fields/related.py
+++ b/django/db/models/fields/related.py
@@ -137,7 +137,7 @@ class RelatedField(Field):
# related object in a table-spanning query. It uses the lower-cased
# object_name by default, but this can be overridden with the
# "related_name" option.
- return self.rel.related_name or self.opts.model_name
+ return self.rel.related_query_name or self.rel.related_name or self.opts.model_name
class RenameRelatedObjectDescriptorMethods(RenameMethodsBase):
@@ -824,7 +824,7 @@ class ReverseManyRelatedObjectsDescriptor(object):
class ForeignObjectRel(object):
def __init__(self, field, to, related_name=None, limit_choices_to=None,
- parent_link=False, on_delete=None):
+ parent_link=False, on_delete=None, related_query_name=None):
try:
to._meta
except AttributeError: # to._meta doesn't exist, so it must be RECURSIVE_RELATIONSHIP_CONSTANT
@@ -833,6 +833,7 @@ class ForeignObjectRel(object):
self.field = field
self.to = to
self.related_name = related_name
+ self.related_query_name = related_query_name
self.limit_choices_to = {} if limit_choices_to is None else limit_choices_to
self.multiple = True
self.parent_link = parent_link
@@ -860,10 +861,10 @@ class ForeignObjectRel(object):
class ManyToOneRel(ForeignObjectRel):
def __init__(self, field, to, field_name, related_name=None, limit_choices_to=None,
- parent_link=False, on_delete=None):
+ parent_link=False, on_delete=None, related_query_name=None):
super(ManyToOneRel, self).__init__(
field, to, related_name=related_name, limit_choices_to=limit_choices_to,
- parent_link=parent_link, on_delete=on_delete)
+ parent_link=parent_link, on_delete=on_delete, related_query_name=related_query_name)
self.field_name = field_name
def get_related_field(self):
@@ -883,21 +884,22 @@ class ManyToOneRel(ForeignObjectRel):
class OneToOneRel(ManyToOneRel):
def __init__(self, field, to, field_name, related_name=None, limit_choices_to=None,
- parent_link=False, on_delete=None):
+ parent_link=False, on_delete=None, related_query_name=None):
super(OneToOneRel, self).__init__(field, to, field_name,
related_name=related_name, limit_choices_to=limit_choices_to,
- parent_link=parent_link, on_delete=on_delete
+ parent_link=parent_link, on_delete=on_delete, related_query_name=related_query_name,
)
self.multiple = False
class ManyToManyRel(object):
def __init__(self, to, related_name=None, limit_choices_to=None,
- symmetrical=True, through=None, db_constraint=True):
+ symmetrical=True, through=None, db_constraint=True, related_query_name=None):
if through and not db_constraint:
raise ValueError("Can't supply a through model and db_constraint=False")
self.to = to
self.related_name = related_name
+ self.related_query_name = related_query_name
if limit_choices_to is None:
limit_choices_to = {}
self.limit_choices_to = limit_choices_to
@@ -931,6 +933,7 @@ class ForeignObject(RelatedField):
kwargs['rel'] = ForeignObjectRel(
self, to,
related_name=kwargs.pop('related_name', None),
+ related_query_name=kwargs.pop('related_query_name', None),
limit_choices_to=kwargs.pop('limit_choices_to', None),
parent_link=kwargs.pop('parent_link', False),
on_delete=kwargs.pop('on_delete', CASCADE),
@@ -1141,6 +1144,7 @@ class ForeignKey(ForeignObject):
kwargs['rel'] = rel_class(
self, to, to_field,
related_name=kwargs.pop('related_name', None),
+ related_query_name=kwargs.pop('related_query_name', None),
limit_choices_to=kwargs.pop('limit_choices_to', None),
parent_link=kwargs.pop('parent_link', False),
on_delete=kwargs.pop('on_delete', CASCADE),
@@ -1371,6 +1375,7 @@ class ManyToManyField(RelatedField):
kwargs['verbose_name'] = kwargs.get('verbose_name', None)
kwargs['rel'] = ManyToManyRel(to,
related_name=kwargs.pop('related_name', None),
+ related_query_name=kwargs.pop('related_query_name', None),
limit_choices_to=kwargs.pop('limit_choices_to', None),
symmetrical=kwargs.pop('symmetrical', to == RECURSIVE_RELATIONSHIP_CONSTANT),
through=kwargs.pop('through', None),
@@ -1388,7 +1393,8 @@ class ManyToManyField(RelatedField):
# Handle the simpler arguments
if self.rel.db_constraint is not True:
kwargs['db_constraint'] = self.db_constraint
- del kwargs['help_text']
+ if "help_text" in kwargs:
+ del kwargs['help_text']
# Rel needs more work.
rel = self.rel
if isinstance(self.rel.to, basestring):
diff --git a/django/db/models/options.py b/django/db/models/options.py
index 1a9421c0fa..b173da9538 100644
--- a/django/db/models/options.py
+++ b/django/db/models/options.py
@@ -422,12 +422,36 @@ class Options(object):
return cache
def get_add_permission(self):
+ """
+ This method has been deprecated in favor of
+ `django.contrib.auth.get_permission_codename`. refs #20642
+ """
+ warnings.warn(
+ "`Options.get_add_permission` has been deprecated in favor "
+ "of `django.contrib.auth.get_permission_codename`.",
+ PendingDeprecationWarning, stacklevel=2)
return 'add_%s' % self.model_name
def get_change_permission(self):
+ """
+ This method has been deprecated in favor of
+ `django.contrib.auth.get_permission_codename`. refs #20642
+ """
+ warnings.warn(
+ "`Options.get_change_permission` has been deprecated in favor "
+ "of `django.contrib.auth.get_permission_codename`.",
+ PendingDeprecationWarning, stacklevel=2)
return 'change_%s' % self.model_name
def get_delete_permission(self):
+ """
+ This method has been deprecated in favor of
+ `django.contrib.auth.get_permission_codename`. refs #20642
+ """
+ warnings.warn(
+ "`Options.get_delete_permission` has been deprecated in favor "
+ "of `django.contrib.auth.get_permission_codename`.",
+ PendingDeprecationWarning, stacklevel=2)
return 'delete_%s' % self.model_name
def get_all_related_objects(self, local_only=False, include_hidden=False,
diff --git a/django/db/models/query.py b/django/db/models/query.py
index b0ce25f5b5..27a87a3f65 100644
--- a/django/db/models/query.py
+++ b/django/db/models/query.py
@@ -1445,7 +1445,10 @@ class RawQuerySet(object):
yield instance
def __repr__(self):
- return "<RawQuerySet: %r>" % (self.raw_query % tuple(self.params))
+ text = self.raw_query
+ if self.params:
+ text = text % (self.params if hasattr(self.params, 'keys') else tuple(self.params))
+ return "<RawQuerySet: %r>" % text
def __getitem__(self, k):
return list(self)[k]
diff --git a/django/db/transaction.py b/django/db/transaction.py
index f770f2efa7..95b9ae165e 100644
--- a/django/db/transaction.py
+++ b/django/db/transaction.py
@@ -171,6 +171,26 @@ def clean_savepoints(using=None):
"""
get_connection(using).clean_savepoints()
+def get_rollback(using=None):
+ """
+ Gets the "needs rollback" flag -- for *advanced use* only.
+ """
+ return get_connection(using).needs_rollback
+
+def set_rollback(rollback, using=None):
+ """
+ Sets or unsets the "needs rollback" flag -- for *advanced use* only.
+
+ When `rollback` is `True`, it triggers a rollback when exiting the
+ innermost enclosing atomic block that has `savepoint=True` (that's the
+ default). Use this to force a rollback without raising an exception.
+
+ When `rollback` is `False`, it prevents such a rollback. Use this only
+ after rolling back to a known-good state! Otherwise, you break the atomic
+ block and data corruption may occur.
+ """
+ return get_connection(using).set_rollback(rollback)
+
#################################
# Decorators / context managers #
#################################