mirror of
https://github.com/django/django.git
synced 2025-07-05 02:09:13 +00:00
newforms-admin: Merged to [5983]
git-svn-id: http://code.djangoproject.com/svn/django/branches/newforms-admin@5984 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
parent
0ae4e102dc
commit
c49f2d21ea
2
AUTHORS
2
AUTHORS
@ -103,6 +103,7 @@ answer newbie questions, and generally made Django that much better:
|
|||||||
dusk@woofle.net
|
dusk@woofle.net
|
||||||
Andy Dustman <farcepest@gmail.com>
|
Andy Dustman <farcepest@gmail.com>
|
||||||
Clint Ecker
|
Clint Ecker
|
||||||
|
Nick Efford <nick@efford.org>
|
||||||
eibaan@gmail.com
|
eibaan@gmail.com
|
||||||
enlight
|
enlight
|
||||||
Enrico <rico.bl@gmail.com>
|
Enrico <rico.bl@gmail.com>
|
||||||
@ -197,6 +198,7 @@ answer newbie questions, and generally made Django that much better:
|
|||||||
mccutchen@gmail.com
|
mccutchen@gmail.com
|
||||||
michael.mcewan@gmail.com
|
michael.mcewan@gmail.com
|
||||||
mikko@sorl.net
|
mikko@sorl.net
|
||||||
|
Slawek Mikula <slawek dot mikula at gmail dot com>
|
||||||
mitakummaa@gmail.com
|
mitakummaa@gmail.com
|
||||||
mmarshall
|
mmarshall
|
||||||
Andreas Mock <andreas.mock@web.de>
|
Andreas Mock <andreas.mock@web.de>
|
||||||
|
@ -25,7 +25,8 @@ ADMINS = ()
|
|||||||
INTERNAL_IPS = ()
|
INTERNAL_IPS = ()
|
||||||
|
|
||||||
# Local time zone for this installation. All choices can be found here:
|
# Local time zone for this installation. All choices can be found here:
|
||||||
# http://www.postgresql.org/docs/8.1/static/datetime-keywords.html#DATETIME-TIMEZONE-SET-TABLE
|
# http://en.wikipedia.org/wiki/List_of_tz_zones_by_name (although not all
|
||||||
|
# systems may support all possibilities).
|
||||||
TIME_ZONE = 'America/Chicago'
|
TIME_ZONE = 'America/Chicago'
|
||||||
|
|
||||||
# Language code for this installation. All choices can be found here:
|
# Language code for this installation. All choices can be found here:
|
||||||
|
Binary file not shown.
@ -6,7 +6,7 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: django\n"
|
"Project-Id-Version: django\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2007-08-13 11:29-0400\n"
|
"POT-Creation-Date: 2007-08-17 15:35-0400\n"
|
||||||
"PO-Revision-Date: 2007-07-14 13:00-0500\n"
|
"PO-Revision-Date: 2007-07-14 13:00-0500\n"
|
||||||
"Last-Translator: Mario Gonzalez <gonzalemario @t gmail.com>\n"
|
"Last-Translator: Mario Gonzalez <gonzalemario @t gmail.com>\n"
|
||||||
"Language-Team: Castellano <Django-I18N@googlegroups.com>\n"
|
"Language-Team: Castellano <Django-I18N@googlegroups.com>\n"
|
||||||
@ -22,6 +22,7 @@ msgstr "%(object)s de este %(type)s ya existen en este %(field)s."
|
|||||||
|
|
||||||
#: db/models/manipulators.py:310 contrib/admin/views/main.py:342
|
#: db/models/manipulators.py:310 contrib/admin/views/main.py:342
|
||||||
#: contrib/admin/views/main.py:344 contrib/admin/views/main.py:346
|
#: contrib/admin/views/main.py:344 contrib/admin/views/main.py:346
|
||||||
|
#: core/validators.py:275
|
||||||
msgid "and"
|
msgid "and"
|
||||||
msgstr "y"
|
msgstr "y"
|
||||||
|
|
||||||
@ -510,6 +511,10 @@ msgstr "%(number)d %(type)s"
|
|||||||
msgid ", %(number)d %(type)s"
|
msgid ", %(number)d %(type)s"
|
||||||
msgstr ", %(number)d %(type)s"
|
msgstr ", %(number)d %(type)s"
|
||||||
|
|
||||||
|
#: utils/text.py:127
|
||||||
|
msgid "or"
|
||||||
|
msgstr "o"
|
||||||
|
|
||||||
#: utils/dateformat.py:41
|
#: utils/dateformat.py:41
|
||||||
msgid "p.m."
|
msgid "p.m."
|
||||||
msgstr "p.m"
|
msgstr "p.m"
|
||||||
@ -765,7 +770,7 @@ msgstr "ocho"
|
|||||||
msgid "nine"
|
msgid "nine"
|
||||||
msgstr "nueve"
|
msgstr "nueve"
|
||||||
|
|
||||||
#: contrib/auth/views.py:41
|
#: contrib/auth/views.py:47
|
||||||
msgid "Logged out"
|
msgid "Logged out"
|
||||||
msgstr "Sesión terminada"
|
msgstr "Sesión terminada"
|
||||||
|
|
||||||
|
@ -17,8 +17,8 @@ DATABASE_HOST = '' # Set to empty string for localhost. Not used wit
|
|||||||
DATABASE_PORT = '' # Set to empty string for default. Not used with sqlite3.
|
DATABASE_PORT = '' # Set to empty string for default. Not used with sqlite3.
|
||||||
|
|
||||||
# Local time zone for this installation. Choices can be found here:
|
# Local time zone for this installation. Choices can be found here:
|
||||||
# http://www.postgresql.org/docs/8.1/static/datetime-keywords.html#DATETIME-TIMEZONE-SET-TABLE
|
# http://en.wikipedia.org/wiki/List_of_tz_zones_by_name
|
||||||
# although not all variations may be possible on all operating systems.
|
# although not all choices may be avilable on all operating systems.
|
||||||
# If running in a Windows environment this must be set to the same as your
|
# If running in a Windows environment this must be set to the same as your
|
||||||
# system time zone.
|
# system time zone.
|
||||||
TIME_ZONE = 'America/Chicago'
|
TIME_ZONE = 'America/Chicago'
|
||||||
|
@ -59,7 +59,7 @@
|
|||||||
{% else %}
|
{% else %}
|
||||||
<ul class="actionlist">
|
<ul class="actionlist">
|
||||||
{% for entry in admin_log %}
|
{% for entry in admin_log %}
|
||||||
<li class="{% if entry.is_addition %}addlink{% endif %}{% if entry.is_change %}changelink{% endif %}{% if entry.is_deletion %}deletelink{% endif %}">{% if not entry.is_deletion %}<a href="{{ entry.get_admin_url }}">{% endif %}{{ entry.object_repr|escape }}{% if not entry.is_deletion %}</a>{% endif %}<br /><span class="mini quiet">{{ entry.content_type.name|capfirst|escape }}</span></li>
|
<li class="{% if entry.is_addition %}addlink{% endif %}{% if entry.is_change %}changelink{% endif %}{% if entry.is_deletion %}deletelink{% endif %}">{% if not entry.is_deletion %}<a href="{{ entry.get_admin_url }}">{% endif %}{{ entry.object_repr|escape }}{% if not entry.is_deletion %}</a>{% endif %}<br /><span class="mini quiet">{% filter capfirst|escape %}{% trans entry.content_type.name %}{% endfilter %}</span></li>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</ul>
|
</ul>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
from django.core import validators
|
from django.core import validators
|
||||||
from django.core.exceptions import ImproperlyConfigured
|
from django.core.exceptions import ImproperlyConfigured
|
||||||
from django.db import backend, connection, models
|
from django.db import connection, models
|
||||||
from django.contrib.contenttypes.models import ContentType
|
from django.contrib.contenttypes.models import ContentType
|
||||||
from django.utils.encoding import smart_str
|
from django.utils.encoding import smart_str
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
@ -188,6 +188,7 @@ class User(models.Model):
|
|||||||
# AND gp."group_id" = ug."group_id"
|
# AND gp."group_id" = ug."group_id"
|
||||||
# AND ct."id" = p."content_type_id"
|
# AND ct."id" = p."content_type_id"
|
||||||
# AND ug."user_id" = %s, [self.id])
|
# AND ug."user_id" = %s, [self.id])
|
||||||
|
qn = connection.ops.quote_name
|
||||||
sql = """
|
sql = """
|
||||||
SELECT ct.%s, p.%s
|
SELECT ct.%s, p.%s
|
||||||
FROM %s p, %s gp, %s ug, %s ct
|
FROM %s p, %s gp, %s ug, %s ct
|
||||||
@ -195,13 +196,13 @@ class User(models.Model):
|
|||||||
AND gp.%s = ug.%s
|
AND gp.%s = ug.%s
|
||||||
AND ct.%s = p.%s
|
AND ct.%s = p.%s
|
||||||
AND ug.%s = %%s""" % (
|
AND ug.%s = %%s""" % (
|
||||||
backend.quote_name('app_label'), backend.quote_name('codename'),
|
qn('app_label'), qn('codename'),
|
||||||
backend.quote_name('auth_permission'), backend.quote_name('auth_group_permissions'),
|
qn('auth_permission'), qn('auth_group_permissions'),
|
||||||
backend.quote_name('auth_user_groups'), backend.quote_name('django_content_type'),
|
qn('auth_user_groups'), qn('django_content_type'),
|
||||||
backend.quote_name('id'), backend.quote_name('permission_id'),
|
qn('id'), qn('permission_id'),
|
||||||
backend.quote_name('group_id'), backend.quote_name('group_id'),
|
qn('group_id'), qn('group_id'),
|
||||||
backend.quote_name('id'), backend.quote_name('content_type_id'),
|
qn('id'), qn('content_type_id'),
|
||||||
backend.quote_name('user_id'),)
|
qn('user_id'),)
|
||||||
cursor.execute(sql, [self.id])
|
cursor.execute(sql, [self.id])
|
||||||
self._group_perm_cache = set(["%s.%s" % (row[0], row[1]) for row in cursor.fetchall()])
|
self._group_perm_cache = set(["%s.%s" % (row[0], row[1]) for row in cursor.fetchall()])
|
||||||
return self._group_perm_cache
|
return self._group_perm_cache
|
||||||
|
@ -4,7 +4,7 @@ Classes allowing "generic" relations through ContentType and object-id fields.
|
|||||||
|
|
||||||
from django import oldforms
|
from django import oldforms
|
||||||
from django.core.exceptions import ObjectDoesNotExist
|
from django.core.exceptions import ObjectDoesNotExist
|
||||||
from django.db import backend
|
from django.db import connection
|
||||||
from django.db.models import signals
|
from django.db.models import signals
|
||||||
from django.db.models.fields.related import RelatedField, Field, ManyToManyRel
|
from django.db.models.fields.related import RelatedField, Field, ManyToManyRel
|
||||||
from django.db.models.loading import get_model
|
from django.db.models.loading import get_model
|
||||||
@ -163,13 +163,15 @@ class ReverseGenericRelatedObjectsDescriptor(object):
|
|||||||
superclass = rel_model._default_manager.__class__
|
superclass = rel_model._default_manager.__class__
|
||||||
RelatedManager = create_generic_related_manager(superclass)
|
RelatedManager = create_generic_related_manager(superclass)
|
||||||
|
|
||||||
|
qn = connection.ops.quote_name
|
||||||
|
|
||||||
manager = RelatedManager(
|
manager = RelatedManager(
|
||||||
model = rel_model,
|
model = rel_model,
|
||||||
instance = instance,
|
instance = instance,
|
||||||
symmetrical = (self.field.rel.symmetrical and instance.__class__ == rel_model),
|
symmetrical = (self.field.rel.symmetrical and instance.__class__ == rel_model),
|
||||||
join_table = backend.quote_name(self.field.m2m_db_table()),
|
join_table = qn(self.field.m2m_db_table()),
|
||||||
source_col_name = backend.quote_name(self.field.m2m_column_name()),
|
source_col_name = qn(self.field.m2m_column_name()),
|
||||||
target_col_name = backend.quote_name(self.field.m2m_reverse_name()),
|
target_col_name = qn(self.field.m2m_reverse_name()),
|
||||||
content_type = ContentType.objects.get_for_model(self.field.model),
|
content_type = ContentType.objects.get_for_model(self.field.model),
|
||||||
content_type_field_name = self.field.content_type_field_name,
|
content_type_field_name = self.field.content_type_field_name,
|
||||||
object_id_field_name = self.field.object_id_field_name
|
object_id_field_name = self.field.object_id_field_name
|
||||||
|
@ -8,6 +8,7 @@ from django.utils import dateformat
|
|||||||
from django.utils.text import capfirst
|
from django.utils.text import capfirst
|
||||||
from django.utils.translation import get_date_formats
|
from django.utils.translation import get_date_formats
|
||||||
from django.utils.encoding import smart_unicode, smart_str, iri_to_uri
|
from django.utils.encoding import smart_unicode, smart_str, iri_to_uri
|
||||||
|
from django.db.models.query import QuerySet
|
||||||
|
|
||||||
EMPTY_VALUE = '(None)'
|
EMPTY_VALUE = '(None)'
|
||||||
|
|
||||||
@ -30,8 +31,12 @@ class EasyModel(object):
|
|||||||
return '%s%s/%s/' % (self.site.root_url, self.model._meta.app_label, self.model._meta.module_name)
|
return '%s%s/%s/' % (self.site.root_url, self.model._meta.app_label, self.model._meta.module_name)
|
||||||
|
|
||||||
def objects(self, **kwargs):
|
def objects(self, **kwargs):
|
||||||
for obj in self.model._default_manager.filter(**kwargs):
|
return self.get_query_set().filter(**kwargs)
|
||||||
yield EasyInstance(self, obj)
|
|
||||||
|
def get_query_set(self):
|
||||||
|
easy_qs = self.model._default_manager.get_query_set()._clone(klass=EasyQuerySet)
|
||||||
|
easy_qs._easymodel = self
|
||||||
|
return easy_qs
|
||||||
|
|
||||||
def object_by_pk(self, pk):
|
def object_by_pk(self, pk):
|
||||||
return EasyInstance(self, self.model._default_manager.get(pk=pk))
|
return EasyInstance(self, self.model._default_manager.get(pk=pk))
|
||||||
@ -194,3 +199,17 @@ class EasyInstanceField(object):
|
|||||||
else:
|
else:
|
||||||
lst = [(self.values()[0], None)]
|
lst = [(self.values()[0], None)]
|
||||||
return lst
|
return lst
|
||||||
|
|
||||||
|
class EasyQuerySet(QuerySet):
|
||||||
|
"""
|
||||||
|
When creating (or cloning to) an `EasyQuerySet`, make sure to set the
|
||||||
|
`_easymodel` variable to the related `EasyModel`.
|
||||||
|
"""
|
||||||
|
def iterator(self, *args, **kwargs):
|
||||||
|
for obj in super(EasyQuerySet, self).iterator(*args, **kwargs):
|
||||||
|
yield EasyInstance(self._easymodel, obj)
|
||||||
|
|
||||||
|
def _clone(self, *args, **kwargs):
|
||||||
|
c = super(EasyQuerySet, self)._clone(*args, **kwargs)
|
||||||
|
c._easymodel = self._easymodel
|
||||||
|
return c
|
||||||
|
@ -64,22 +64,22 @@ class CalendarPlugin(DatabrowsePlugin):
|
|||||||
|
|
||||||
def calendar_view(self, request, field, year=None, month=None, day=None):
|
def calendar_view(self, request, field, year=None, month=None, day=None):
|
||||||
easy_model = EasyModel(self.site, self.model)
|
easy_model = EasyModel(self.site, self.model)
|
||||||
|
queryset = easy_model.get_query_set()
|
||||||
extra_context = {'root_url': self.site.root_url, 'model': easy_model, 'field': field}
|
extra_context = {'root_url': self.site.root_url, 'model': easy_model, 'field': field}
|
||||||
if day is not None:
|
if day is not None:
|
||||||
# TODO: The objects in this template should be EasyInstances
|
return date_based.archive_day(request, year, month, day, queryset, field.name,
|
||||||
return date_based.archive_day(request, year, month, day, self.model.objects.all(), field.name,
|
|
||||||
template_name='databrowse/calendar_day.html', allow_empty=False, allow_future=True,
|
template_name='databrowse/calendar_day.html', allow_empty=False, allow_future=True,
|
||||||
extra_context=extra_context)
|
extra_context=extra_context)
|
||||||
elif month is not None:
|
elif month is not None:
|
||||||
return date_based.archive_month(request, year, month, self.model.objects.all(), field.name,
|
return date_based.archive_month(request, year, month, queryset, field.name,
|
||||||
template_name='databrowse/calendar_month.html', allow_empty=False, allow_future=True,
|
template_name='databrowse/calendar_month.html', allow_empty=False, allow_future=True,
|
||||||
extra_context=extra_context)
|
extra_context=extra_context)
|
||||||
elif year is not None:
|
elif year is not None:
|
||||||
return date_based.archive_year(request, year, self.model.objects.all(), field.name,
|
return date_based.archive_year(request, year, queryset, field.name,
|
||||||
template_name='databrowse/calendar_year.html', allow_empty=False, allow_future=True,
|
template_name='databrowse/calendar_year.html', allow_empty=False, allow_future=True,
|
||||||
extra_context=extra_context)
|
extra_context=extra_context)
|
||||||
else:
|
else:
|
||||||
return date_based.archive_index(request, self.model.objects.all(), field.name,
|
return date_based.archive_index(request, queryset, field.name,
|
||||||
template_name='databrowse/calendar_main.html', allow_empty=True, allow_future=True,
|
template_name='databrowse/calendar_main.html', allow_empty=True, allow_future=True,
|
||||||
extra_context=extra_context)
|
extra_context=extra_context)
|
||||||
assert False, ('%s, %s, %s, %s' % (field, year, month, day))
|
assert False, ('%s, %s, %s, %s' % (field, year, month, day))
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
<html xmlns="http://www.w3.org/1999/xhtml" lang="{{ LANGUAGE_CODE }}" xml:lang="{{ LANGUAGE_CODE }}" {% if LANGUAGE_BIDI %}dir="rtl"{% endif %}>
|
<html xmlns="http://www.w3.org/1999/xhtml" lang="{{ LANGUAGE_CODE }}" xml:lang="{{ LANGUAGE_CODE }}" {% if LANGUAGE_BIDI %}dir="rtl"{% endif %}>
|
||||||
<head>
|
<head>
|
||||||
<title>{% block title %}{% endblock %}</title>
|
<title>{% block title %}{% endblock %}</title>
|
||||||
|
{% block style %}
|
||||||
<style type="text/css">
|
<style type="text/css">
|
||||||
* { margin:0; padding:0; }
|
* { margin:0; padding:0; }
|
||||||
body { background:#eee; color:#333; font:76%/1.6 "Lucida Grande","Bitstream Vera Sans",Verdana,sans-serif; }
|
body { background:#eee; color:#333; font:76%/1.6 "Lucida Grande","Bitstream Vera Sans",Verdana,sans-serif; }
|
||||||
@ -48,9 +49,11 @@ p { margin:0.5em 0 1em 0; }
|
|||||||
/* CONTENT */
|
/* CONTENT */
|
||||||
#content { background:#fff; border-bottom:1px solid #ddd; padding:0 20px; }
|
#content { background:#fff; border-bottom:1px solid #ddd; padding:0 20px; }
|
||||||
</style>
|
</style>
|
||||||
|
{% endblock %}
|
||||||
|
{% block extrahead %}{% endblock %}
|
||||||
</head>
|
</head>
|
||||||
<body id="{% block bodyid %}page{% endblock %}">
|
<body id="{% block bodyid %}page{% endblock %}">
|
||||||
<div id="header"><a href="{{ root_url }}">Databrowse</a></div>
|
<div id="header"><a href="{{ root_url }}">{% block databrowse_title %}Databrowse{% endblock %}</a></div>
|
||||||
<div id="content">
|
<div id="content">
|
||||||
{% block content %}{% endblock %}
|
{% block content %}{% endblock %}
|
||||||
</div>
|
</div>
|
||||||
|
@ -0,0 +1 @@
|
|||||||
|
{% extends "databrowse/base.html" %}
|
@ -1,4 +1,4 @@
|
|||||||
{% extends "databrowse/base.html" %}
|
{% extends "databrowse/base_site.html" %}
|
||||||
|
|
||||||
{% block title %}{{ model.verbose_name_plural|capfirst }} with {{ field.verbose_name }} {{ day|date:"F j, Y" }}{% endblock %}
|
{% block title %}{{ model.verbose_name_plural|capfirst }} with {{ field.verbose_name }} {{ day|date:"F j, Y" }}{% endblock %}
|
||||||
|
|
||||||
@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
<div id="breadcrumbs"><a href="{{ root_url }}">Home</a> / <a href="{{ model.url }}">{{ model.verbose_name_plural|capfirst }}</a> / <a href="../../../../">Calendars</a> / <a href="../../../">By {{ field.verbose_name }}</a> / <a href="../../">{{ day.year }}</a> / <a href="../">{{ day|date:"F" }}</a> / {{ day.day }}</div>
|
<div id="breadcrumbs"><a href="{{ root_url }}">Home</a> / <a href="{{ model.url }}">{{ model.verbose_name_plural|capfirst }}</a> / <a href="../../../../">Calendars</a> / <a href="../../../">By {{ field.verbose_name }}</a> / <a href="../../">{{ day.year }}</a> / <a href="../">{{ day|date:"F" }}</a> / {{ day.day }}</div>
|
||||||
|
|
||||||
<h1>{{ model.verbose_name_plural|capfirst }} with {{ field.verbose_name }} on {{ day|date:"F j, Y" }}</h1>
|
<h1>{{ object_list.count }} {% if object_list.count|pluralize %}{{ model.verbose_name_plural|escape }}{% else %}{{ model.verbose_name|escape }}{% endif %} with {{ field.verbose_name }} on {{ day|date:"F j, Y" }}</h1>
|
||||||
|
|
||||||
<ul class="objectlist">
|
<ul class="objectlist">
|
||||||
{% for object in object_list %}
|
{% for object in object_list %}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
{% extends "databrowse/base.html" %}
|
{% extends "databrowse/base_site.html" %}
|
||||||
|
|
||||||
{% block title %}Calendars{% endblock %}
|
{% block title %}Calendars{% endblock %}
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
{% extends "databrowse/base.html" %}
|
{% extends "databrowse/base_site.html" %}
|
||||||
|
|
||||||
{% block title %}{{ field.verbose_name|capfirst }} calendar{% endblock %}
|
{% block title %}{{ field.verbose_name|capfirst }} calendar{% endblock %}
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
{% extends "databrowse/base.html" %}
|
{% extends "databrowse/base_site.html" %}
|
||||||
|
|
||||||
{% block title %}{{ model.verbose_name_plural|capfirst }} with {{ field.verbose_name }} in {{ month|date:"F Y" }}{% endblock %}
|
{% block title %}{{ model.verbose_name_plural|capfirst }} with {{ field.verbose_name }} in {{ month|date:"F Y" }}{% endblock %}
|
||||||
|
|
||||||
@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
<div id="breadcrumbs"><a href="{{ root_url }}">Home</a> / <a href="{{ model.url }}">{{ model.verbose_name_plural|capfirst }}</a> / <a href="../../../">Calendars</a> / <a href="../../">By {{ field.verbose_name }}</a> / <a href="../">{{ month.year }}</a> / {{ month|date:"F" }}</div>
|
<div id="breadcrumbs"><a href="{{ root_url }}">Home</a> / <a href="{{ model.url }}">{{ model.verbose_name_plural|capfirst }}</a> / <a href="../../../">Calendars</a> / <a href="../../">By {{ field.verbose_name }}</a> / <a href="../">{{ month.year }}</a> / {{ month|date:"F" }}</div>
|
||||||
|
|
||||||
<h1>{{ model.verbose_name_plural|capfirst }} with {{ field.verbose_name }} in {{ month|date:"F Y" }}</h1>
|
<h1>{{ object_list.count }} {% if object_list.count|pluralize %}{{ model.verbose_name_plural|escape }}{% else %}{{ model.verbose_name|escape }}{% endif %} with {{ field.verbose_name }} on {{ day|date:"F Y" }}</h1>
|
||||||
|
|
||||||
<ul class="objectlist">
|
<ul class="objectlist">
|
||||||
{% for object in object_list %}
|
{% for object in object_list %}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
{% extends "databrowse/base.html" %}
|
{% extends "databrowse/base_site.html" %}
|
||||||
|
|
||||||
{% block title %}{{ model.verbose_name_plural|capfirst }} with {{ field.verbose_name }} in {{ year }}{% endblock %}
|
{% block title %}{{ model.verbose_name_plural|capfirst }} with {{ field.verbose_name }} in {{ year }}{% endblock %}
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
{% extends "databrowse/base.html" %}
|
{% extends "databrowse/base_site.html" %}
|
||||||
|
|
||||||
{% block title %}{{ model.verbose_name_plural|capfirst }} by {{ field.field.verbose_name }}: {{ value|escape }}{% endblock %}
|
{% block title %}{{ model.verbose_name_plural|capfirst }} by {{ field.field.verbose_name }}: {{ value|escape }}{% endblock %}
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
{% extends "databrowse/base.html" %}
|
{% extends "databrowse/base_site.html" %}
|
||||||
|
|
||||||
{% block title %}{{ model.verbose_name_plural|capfirst }} by {{ field.field.verbose_name }}{% endblock %}
|
{% block title %}{{ model.verbose_name_plural|capfirst }} by {{ field.field.verbose_name }}{% endblock %}
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
{% extends "databrowse/base.html" %}
|
{% extends "databrowse/base_site.html" %}
|
||||||
|
|
||||||
{% block title %}{{ model.verbose_name_plural|capfirst|escape }} with {{ field.field.verbose_name|escape }} {{ value|escape }}{% endblock %}
|
{% block title %}{{ model.verbose_name_plural|capfirst|escape }} with {{ field.field.verbose_name|escape }} {{ value|escape }}{% endblock %}
|
||||||
|
|
||||||
@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
<div id="breadcrumbs"><a href="{{ root_url }}">Home</a> / <a href="{{ model.url }}">{{ model.verbose_name_plural|capfirst }}</a> / <a href="../../">Fields</a> / <a href="../">By {{ field.field.verbose_name|escape }}</a> / {{ value|escape }}</div>
|
<div id="breadcrumbs"><a href="{{ root_url }}">Home</a> / <a href="{{ model.url }}">{{ model.verbose_name_plural|capfirst }}</a> / <a href="../../">Fields</a> / <a href="../">By {{ field.field.verbose_name|escape }}</a> / {{ value|escape }}</div>
|
||||||
|
|
||||||
<h1>{{ model.verbose_name_plural|capfirst|escape }} with {{ field.field.verbose_name|escape }} {{ value|escape }}</h1>
|
<h1>{{ object_list.count }} {% if object_list.count|pluralize %}{{ model.verbose_name_plural|escape }}{% else %}{{ model.verbose_name|escape }}{% endif %} with {{ field.field.verbose_name|escape }} {{ value|escape }}</h1>
|
||||||
|
|
||||||
<ul class="objectlist">
|
<ul class="objectlist">
|
||||||
{% for object in object_list %}
|
{% for object in object_list %}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
{% extends "databrowse/base.html" %}
|
{% extends "databrowse/base_site.html" %}
|
||||||
|
|
||||||
{% block title %}Browsable fields in {{ model.verbose_name_plural|escape }}{% endblock %}
|
{% block title %}Browsable fields in {{ model.verbose_name_plural|escape }}{% endblock %}
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
{% extends "databrowse/base.html" %}
|
{% extends "databrowse/base_site.html" %}
|
||||||
|
|
||||||
{% block title %}{{ model.verbose_name_plural|capfirst|escape }} by {{ field.field.verbose_name|escape }}{% endblock %}
|
{% block title %}{{ model.verbose_name_plural|capfirst|escape }} by {{ field.field.verbose_name|escape }}{% endblock %}
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
{% extends "databrowse/base.html" %}
|
{% extends "databrowse/base_site.html" %}
|
||||||
|
|
||||||
{% block title %}Databrowse{% endblock %}
|
{% block title %}Databrowse{% endblock %}
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
{% extends "databrowse/base.html" %}
|
{% extends "databrowse/base_site.html" %}
|
||||||
|
|
||||||
{% block title %}{{ model.verbose_name_plural|capfirst }}{% endblock %}
|
{% block title %}{{ model.verbose_name_plural|capfirst }}{% endblock %}
|
||||||
|
|
||||||
@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
<div id="breadcrumbs"><a href="{{ root_url }}">Home</a> / {{ model.verbose_name_plural|capfirst }}</div>
|
<div id="breadcrumbs"><a href="{{ root_url }}">Home</a> / {{ model.verbose_name_plural|capfirst }}</div>
|
||||||
|
|
||||||
<h1>{{ model.verbose_name_plural|capfirst }}</h1>
|
<h1>{{ model.objects.count }} {% if model.objects.count|pluralize %}{{ model.verbose_name_plural }}{% else %}{{ model.verbose_name }}{% endif %}</h1>
|
||||||
|
|
||||||
{{ plugin_html }}
|
{{ plugin_html }}
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
{% extends "databrowse/base.html" %}
|
{% extends "databrowse/base_site.html" %}
|
||||||
|
|
||||||
{% block title %}{{ object.model.verbose_name|capfirst }}: {{ object }}{% endblock %}
|
{% block title %}{{ object.model.verbose_name|capfirst }}: {{ object }}{% endblock %}
|
||||||
|
|
||||||
|
0
django/contrib/localflavor/pl/__init__.py
Normal file
0
django/contrib/localflavor/pl/__init__.py
Normal file
84
django/contrib/localflavor/pl/forms.py
Normal file
84
django/contrib/localflavor/pl/forms.py
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
"""
|
||||||
|
Polish-specific form helpers
|
||||||
|
"""
|
||||||
|
|
||||||
|
from django.newforms import ValidationError
|
||||||
|
from django.newforms.fields import Select, RegexField
|
||||||
|
from django.utils.translation import ugettext as _
|
||||||
|
|
||||||
|
class PLVoivodeshipSelect(Select):
|
||||||
|
"""
|
||||||
|
A select widget with list of Polish voivodeships (administrative provinces)
|
||||||
|
as choices.
|
||||||
|
"""
|
||||||
|
def __init__(self, attrs=None):
|
||||||
|
from pl_voivodeships import VOIVODESHIP_CHOICES
|
||||||
|
super(PLVoivodeshipSelect, self).__init__(attrs, choices=VOIVODESHIP_CHOICES)
|
||||||
|
|
||||||
|
class PLAdministrativeUnitSelect(Select):
|
||||||
|
"""
|
||||||
|
A select widget with list of Polish administrative units as choices.
|
||||||
|
"""
|
||||||
|
def __init__(self, attrs=None):
|
||||||
|
from pl_administrativeunits import ADMINISTRATIVE_UNIT_CHOICES
|
||||||
|
super(PLAdministrativeUnitSelect, self).__init__(attrs, choices=ADMINISTRATIVE_UNIT_CHOICES)
|
||||||
|
|
||||||
|
class PLNationalIdentificationNumberField(RegexField):
|
||||||
|
"""
|
||||||
|
A form field that validates as Polish Identification Number (PESEL).
|
||||||
|
|
||||||
|
Checks the following rules:
|
||||||
|
* the length consist of 11 digits
|
||||||
|
* has a valid checksum
|
||||||
|
|
||||||
|
The algorithm is documented at http://en.wikipedia.org/wiki/PESEL.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def has_valid_checksum(self, number):
|
||||||
|
"""
|
||||||
|
Calculates a checksum with the provided algorithm.
|
||||||
|
"""
|
||||||
|
multiple_table = (1, 3, 7, 9, 1, 3, 7, 9, 1, 3, 1)
|
||||||
|
result = 0
|
||||||
|
for i in range(len(number)):
|
||||||
|
result += int(number[i])*multiple_table[i]
|
||||||
|
|
||||||
|
if result % 10 == 0:
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super(PLNationalIdentificationNumberField, self).__init__(r'^\d{11}$',
|
||||||
|
max_length=None, min_length=None, error_message=_(u'National Identification Number consists of 11 digits.'),
|
||||||
|
*args, **kwargs)
|
||||||
|
|
||||||
|
def clean(self,value):
|
||||||
|
super(PLNationalIdentificationNumberField, self).clean(value)
|
||||||
|
if not self.has_valid_checksum(value):
|
||||||
|
raise ValidationError(_(u'Wrong checksum for the National Identification Number.'))
|
||||||
|
return u'%s' % value
|
||||||
|
|
||||||
|
|
||||||
|
class PLTaxNumberField(RegexField):
|
||||||
|
"""
|
||||||
|
A form field that validates as Polish Tax Number (NIP).
|
||||||
|
Valid forms are: XXX-XXX-YY-YY or XX-XX-YYY-YYY.
|
||||||
|
"""
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super(PLTaxNumberField, self).__init__(r'^\d{3}-\d{3}-\d{2}-\d{2}$|^\d{2}-\d{2}-\d{3}-\d{3}$',
|
||||||
|
max_length=None, min_length=None,
|
||||||
|
error_message=_(u'Enter a tax number field (NIP) in the format XXX-XXX-XX-XX or XX-XX-XXX-XXX.'), *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class PLPostalCodeField(RegexField):
|
||||||
|
"""
|
||||||
|
A form field that validates as Polish postal code.
|
||||||
|
Valid code is XX-XXX where X is digit.
|
||||||
|
"""
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super(PLPostalCodeField, self).__init__(r'^\d{2}-\d{3}$',
|
||||||
|
max_length=None, min_length=None,
|
||||||
|
error_message=_(u'Enter a postal code in the format XX-XXX.'),
|
||||||
|
*args, **kwargs)
|
||||||
|
|
385
django/contrib/localflavor/pl/pl_administrativeunits.py
Normal file
385
django/contrib/localflavor/pl/pl_administrativeunits.py
Normal file
@ -0,0 +1,385 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
Polish administrative units as in http://pl.wikipedia.org/wiki/Podzia%C5%82_administracyjny_Polski
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
ADMINISTRATIVE_UNIT_CHOICES = (
|
||||||
|
('wroclaw', u'Wrocław'),
|
||||||
|
('jeleniagora', u'Jelenia Góra'),
|
||||||
|
('legnica', u'Legnica'),
|
||||||
|
('boleslawiecki', u'bolesławiecki'),
|
||||||
|
('dzierzoniowski', u'dzierżoniowski'),
|
||||||
|
('glogowski', u'głogowski'),
|
||||||
|
('gorowski', u'górowski'),
|
||||||
|
('jaworski', u'jaworski'),
|
||||||
|
('jeleniogorski', u'jeleniogórski'),
|
||||||
|
('kamiennogorski', u'kamiennogórski'),
|
||||||
|
('klodzki', u'kłodzki'),
|
||||||
|
('legnicki', u'legnicki'),
|
||||||
|
('lubanski', u'lubański'),
|
||||||
|
('lubinski', u'lubiński'),
|
||||||
|
('lwowecki', u'lwówecki'),
|
||||||
|
('milicki', u'milicki'),
|
||||||
|
('olesnicki', u'oleśnicki'),
|
||||||
|
('olawski', u'oławski'),
|
||||||
|
('polkowicki', u'polkowicki'),
|
||||||
|
('strzelinski', u'strzeliński'),
|
||||||
|
('sredzki', u'średzki'),
|
||||||
|
('swidnicki', u'świdnicki'),
|
||||||
|
('trzebnicki', u'trzebnicki'),
|
||||||
|
('walbrzyski', u'wałbrzyski'),
|
||||||
|
('wolowski', u'wołowski'),
|
||||||
|
('wroclawski', u'wrocławski'),
|
||||||
|
('zabkowicki', u'ząbkowicki'),
|
||||||
|
('zgorzelecki', u'zgorzelecki'),
|
||||||
|
('zlotoryjski', u'złotoryjski'),
|
||||||
|
('bydgoszcz', u'Bydgoszcz'),
|
||||||
|
('torun', u'Toruń'),
|
||||||
|
('wloclawek', u'Włocławek'),
|
||||||
|
('grudziadz', u'Grudziądz'),
|
||||||
|
('aleksandrowski', u'aleksandrowski'),
|
||||||
|
('brodnicki', u'brodnicki'),
|
||||||
|
('bydgoski', u'bydgoski'),
|
||||||
|
('chelminski', u'chełmiński'),
|
||||||
|
('golubsko-dobrzynski', u'golubsko-dobrzyński'),
|
||||||
|
('grudziadzki', u'grudziądzki'),
|
||||||
|
('inowroclawski', u'inowrocławski'),
|
||||||
|
('lipnowski', u'lipnowski'),
|
||||||
|
('mogilenski', u'mogileński'),
|
||||||
|
('nakielski', u'nakielski'),
|
||||||
|
('radziejowski', u'radziejowski'),
|
||||||
|
('rypinski', u'rypiński'),
|
||||||
|
('sepolenski', u'sępoleński'),
|
||||||
|
('swiecki', u'świecki'),
|
||||||
|
('torunski', u'toruński'),
|
||||||
|
('tucholski', u'tucholski'),
|
||||||
|
('wabrzeski', u'wąbrzeski'),
|
||||||
|
('wloclawski', u'wrocławski'),
|
||||||
|
('zninski', u'źniński'),
|
||||||
|
('lublin', u'Lublin'),
|
||||||
|
('biala-podlaska', u'Biała Podlaska'),
|
||||||
|
('chelm', u'Chełm'),
|
||||||
|
('zamosc', u'Zamość'),
|
||||||
|
('bialski', u'bialski'),
|
||||||
|
('bilgorajski', u'biłgorajski'),
|
||||||
|
('chelmski', u'chełmski'),
|
||||||
|
('hrubieszowski', u'hrubieszowski'),
|
||||||
|
('janowski', u'janowski'),
|
||||||
|
('krasnostawski', u'krasnostawski'),
|
||||||
|
('krasnicki', u'kraśnicki'),
|
||||||
|
('lubartowski', u'lubartowski'),
|
||||||
|
('lubelski', u'lubelski'),
|
||||||
|
('leczynski', u'łęczyński'),
|
||||||
|
('lukowski', u'łukowski'),
|
||||||
|
('opolski', u'opolski'),
|
||||||
|
('parczewski', u'parczewski'),
|
||||||
|
('pulawski', u'puławski'),
|
||||||
|
('radzynski', u'radzyński'),
|
||||||
|
('rycki', u'rycki'),
|
||||||
|
('swidnicki', u'świdnicki'),
|
||||||
|
('tomaszowski', u'tomaszowski'),
|
||||||
|
('wlodawski', u'włodawski'),
|
||||||
|
('zamojski', u'zamojski'),
|
||||||
|
('gorzow-wielkopolski', u'Gorzów Wielkopolski'),
|
||||||
|
('zielona-gora', u'Zielona Góra'),
|
||||||
|
('gorzowski', u'gorzowski'),
|
||||||
|
('krosnienski', u'krośnieński'),
|
||||||
|
('miedzyrzecki', u'międzyrzecki'),
|
||||||
|
('nowosolski', u'nowosolski'),
|
||||||
|
('slubicki', u'słubicki'),
|
||||||
|
('strzelecko-drezdenecki', u'strzelecko-drezdenecki'),
|
||||||
|
('sulecinski', u'suleńciński'),
|
||||||
|
('swiebodzinski', u'świebodziński'),
|
||||||
|
('wschowski', u'wschowski'),
|
||||||
|
('zielonogorski', u'zielonogórski'),
|
||||||
|
('zaganski', u'żagański'),
|
||||||
|
('zarski', u'żarski'),
|
||||||
|
('lodz', u'Łódź'),
|
||||||
|
('piotrkow-trybunalski', u'Piotrków Trybunalski'),
|
||||||
|
('skierniewice', u'Skierniewice'),
|
||||||
|
('belchatowski', u'bełchatowski'),
|
||||||
|
('brzezinski', u'brzeziński'),
|
||||||
|
('kutnowski', u'kutnowski'),
|
||||||
|
('laski', u'łaski'),
|
||||||
|
('leczycki', u'łęczycki'),
|
||||||
|
('lowicki', u'łowicki'),
|
||||||
|
('lodzki wschodni', u'łódzki wschodni'),
|
||||||
|
('opoczynski', u'opoczyński'),
|
||||||
|
('pabianicki', u'pabianicki'),
|
||||||
|
('pajeczanski', u'pajęczański'),
|
||||||
|
('piotrkowski', u'piotrkowski'),
|
||||||
|
('poddebicki', u'poddębicki'),
|
||||||
|
('radomszczanski', u'radomszczański'),
|
||||||
|
('rawski', u'rawski'),
|
||||||
|
('sieradzki', u'sieradzki'),
|
||||||
|
('skierniewicki', u'skierniewicki'),
|
||||||
|
('tomaszowski', u'tomaszowski'),
|
||||||
|
('wielunski', u'wieluński'),
|
||||||
|
('wieruszowski', u'wieruszowski'),
|
||||||
|
('zdunskowolski', u'zduńskowolski'),
|
||||||
|
('zgierski', u'zgierski'),
|
||||||
|
('krakow', u'Kraków'),
|
||||||
|
('tarnow', u'Tarnów'),
|
||||||
|
('nowy-sacz', u'Nowy Sącz'),
|
||||||
|
('bochenski', u'bocheński'),
|
||||||
|
('brzeski', u'brzeski'),
|
||||||
|
('chrzanowski', u'chrzanowski'),
|
||||||
|
('dabrowski', u'dąbrowski'),
|
||||||
|
('gorlicki', u'gorlicki'),
|
||||||
|
('krakowski', u'krakowski'),
|
||||||
|
('limanowski', u'limanowski'),
|
||||||
|
('miechowski', u'miechowski'),
|
||||||
|
('myslenicki', u'myślenicki'),
|
||||||
|
('nowosadecki', u'nowosądecki'),
|
||||||
|
('nowotarski', u'nowotarski'),
|
||||||
|
('olkuski', u'olkuski'),
|
||||||
|
('oswiecimski', u'oświęcimski'),
|
||||||
|
('proszowicki', u'proszowicki'),
|
||||||
|
('suski', u'suski'),
|
||||||
|
('tarnowski', u'tarnowski'),
|
||||||
|
('tatrzanski', u'tatrzański'),
|
||||||
|
('wadowicki', u'wadowicki'),
|
||||||
|
('wielicki', u'wielicki'),
|
||||||
|
('warszawa', u'Warszawa'),
|
||||||
|
('ostroleka', u'Ostrołęka'),
|
||||||
|
('plock', u'Płock'),
|
||||||
|
('radom', u'Radom'),
|
||||||
|
('siedlce', u'Siedlce'),
|
||||||
|
('bialobrzeski', u'białobrzeski'),
|
||||||
|
('ciechanowski', u'ciechanowski'),
|
||||||
|
('garwolinski', u'garwoliński'),
|
||||||
|
('gostyninski', u'gostyniński'),
|
||||||
|
('grodziski', u'grodziski'),
|
||||||
|
('grojecki', u'grójecki'),
|
||||||
|
('kozienicki', u'kozenicki'),
|
||||||
|
('legionowski', u'legionowski'),
|
||||||
|
('lipski', u'lipski'),
|
||||||
|
('losicki', u'łosicki'),
|
||||||
|
('makowski', u'makowski'),
|
||||||
|
('minski', u'miński'),
|
||||||
|
('mlawski', u'mławski'),
|
||||||
|
('nowodworski', u'nowodworski'),
|
||||||
|
('ostrolecki', u'ostrołęcki'),
|
||||||
|
('ostrowski', u'ostrowski'),
|
||||||
|
('otwocki', u'otwocki'),
|
||||||
|
('piaseczynski', u'piaseczyński'),
|
||||||
|
('plocki', u'płocki'),
|
||||||
|
('plonski', u'płoński'),
|
||||||
|
('pruszkowski', u'pruszkowski'),
|
||||||
|
('przasnyski', u'przasnyski'),
|
||||||
|
('przysuski', u'przysuski'),
|
||||||
|
('pultuski', u'pułtuski'),
|
||||||
|
('radomski', u'radomski'),
|
||||||
|
('siedlecki', u'siedlecki'),
|
||||||
|
('sierpecki', u'sierpecki'),
|
||||||
|
('sochaczewski', u'sochaczewski'),
|
||||||
|
('sokolowski', u'sokołowski'),
|
||||||
|
('szydlowiecki', u'szydłowiecki'),
|
||||||
|
('warszawski-zachodni', u'warszawski zachodni'),
|
||||||
|
('wegrowski', u'węgrowski'),
|
||||||
|
('wolominski', u'wołomiński'),
|
||||||
|
('wyszkowski', u'wyszkowski'),
|
||||||
|
('zwolenski', u'zwoleński'),
|
||||||
|
('zurominski', u'żuromiński'),
|
||||||
|
('zyrardowski', u'żyrardowski'),
|
||||||
|
('opole', u'Opole'),
|
||||||
|
('brzeski', u'brzeski'),
|
||||||
|
('glubczycki', u'głubczyski'),
|
||||||
|
('kedzierzynsko-kozielski', u'kędzierzyński-kozielski'),
|
||||||
|
('kluczborski', u'kluczborski'),
|
||||||
|
('krapkowicki', u'krapkowicki'),
|
||||||
|
('namyslowski', u'namysłowski'),
|
||||||
|
('nyski', u'nyski'),
|
||||||
|
('oleski', u'oleski'),
|
||||||
|
('opolski', u'opolski'),
|
||||||
|
('prudnicki', u'prudnicki'),
|
||||||
|
('strzelecki', u'strzelecki'),
|
||||||
|
('rzeszow', u'Rzeszów'),
|
||||||
|
('krosno', u'Krosno'),
|
||||||
|
('przemysl', u'Przemyśl'),
|
||||||
|
('tarnobrzeg', u'Tarnobrzeg'),
|
||||||
|
('bieszczadzki', u'bieszczadzki'),
|
||||||
|
('brzozowski', u'brzozowski'),
|
||||||
|
('debicki', u'dębicki'),
|
||||||
|
('jaroslawski', u'jarosławski'),
|
||||||
|
('jasielski', u'jasielski'),
|
||||||
|
('kolbuszowski', u'kolbuszowski'),
|
||||||
|
('krosnienski', u'krośnieński'),
|
||||||
|
('leski', u'leski'),
|
||||||
|
('lezajski', u'leżajski'),
|
||||||
|
('lubaczowski', u'lubaczowski'),
|
||||||
|
('lancucki', u'łańcucki'),
|
||||||
|
('mielecki', u'mielecki'),
|
||||||
|
('nizanski', u'niżański'),
|
||||||
|
('przemyski', u'przemyski'),
|
||||||
|
('przeworski', u'przeworski'),
|
||||||
|
('ropczycko-sedziszowski', u'ropczycko-sędziszowski'),
|
||||||
|
('rzeszowski', u'rzeszowski'),
|
||||||
|
('sanocki', u'sanocki'),
|
||||||
|
('stalowowolski', u'stalowowolski'),
|
||||||
|
('strzyzowski', u'strzyżowski'),
|
||||||
|
('tarnobrzeski', u'tarnobrzeski'),
|
||||||
|
('bialystok', u'Białystok'),
|
||||||
|
('lomza', u'Łomża'),
|
||||||
|
('suwalki', u'Suwałki'),
|
||||||
|
('augustowski', u'augustowski'),
|
||||||
|
('bialostocki', u'białostocki'),
|
||||||
|
('bielski', u'bielski'),
|
||||||
|
('grajewski', u'grajewski'),
|
||||||
|
('hajnowski', u'hajnowski'),
|
||||||
|
('kolnenski', u'kolneński'),
|
||||||
|
('łomzynski', u'łomżyński'),
|
||||||
|
('moniecki', u'moniecki'),
|
||||||
|
('sejnenski', u'sejneński'),
|
||||||
|
('siemiatycki', u'siematycki'),
|
||||||
|
('sokolski', u'sokólski'),
|
||||||
|
('suwalski', u'suwalski'),
|
||||||
|
('wysokomazowiecki', u'wysokomazowiecki'),
|
||||||
|
('zambrowski', u'zambrowski'),
|
||||||
|
('gdansk', u'Gdańsk'),
|
||||||
|
('gdynia', u'Gdynia'),
|
||||||
|
('slupsk', u'Słupsk'),
|
||||||
|
('sopot', u'Sopot'),
|
||||||
|
('bytowski', u'bytowski'),
|
||||||
|
('chojnicki', u'chojnicki'),
|
||||||
|
('czluchowski', u'człuchowski'),
|
||||||
|
('kartuski', u'kartuski'),
|
||||||
|
('koscierski', u'kościerski'),
|
||||||
|
('kwidzynski', u'kwidzyński'),
|
||||||
|
('leborski', u'lęborski'),
|
||||||
|
('malborski', u'malborski'),
|
||||||
|
('nowodworski', u'nowodworski'),
|
||||||
|
('gdanski', u'gdański'),
|
||||||
|
('pucki', u'pucki'),
|
||||||
|
('slupski', u'słupski'),
|
||||||
|
('starogardzki', u'starogardzki'),
|
||||||
|
('sztumski', u'sztumski'),
|
||||||
|
('tczewski', u'tczewski'),
|
||||||
|
('wejherowski', u'wejcherowski'),
|
||||||
|
('katowice', u'Katowice'),
|
||||||
|
('bielsko-biala', u'Bielsko-Biała'),
|
||||||
|
('bytom', u'Bytom'),
|
||||||
|
('chorzow', u'Chorzów'),
|
||||||
|
('czestochowa', u'Częstochowa'),
|
||||||
|
('dabrowa-gornicza', u'Dąbrowa Górnicza'),
|
||||||
|
('gliwice', u'Gliwice'),
|
||||||
|
('jastrzebie-zdroj', u'Jastrzębie Zdrój'),
|
||||||
|
('jaworzno', u'Jaworzno'),
|
||||||
|
('myslowice', u'Mysłowice'),
|
||||||
|
('piekary-slaskie', u'Piekary Śląskie'),
|
||||||
|
('ruda-slaska', u'Ruda Śląska'),
|
||||||
|
('rybnik', u'Rybnik'),
|
||||||
|
('siemianowice-slaskie', u'Siemianowice Śląskie'),
|
||||||
|
('sosnowiec', u'Sosnowiec'),
|
||||||
|
('swietochlowice', u'Świętochłowice'),
|
||||||
|
('tychy', u'Tychy'),
|
||||||
|
('zabrze', u'Zabrze'),
|
||||||
|
('zory', u'Żory'),
|
||||||
|
('bedzinski', u'będziński'),
|
||||||
|
('bielski', u'bielski'),
|
||||||
|
('bierunsko-ledzinski', u'bieruńsko-lędziński'),
|
||||||
|
('cieszynski', u'cieszyński'),
|
||||||
|
('czestochowski', u'częstochowski'),
|
||||||
|
('gliwicki', u'gliwicki'),
|
||||||
|
('klobucki', u'kłobucki'),
|
||||||
|
('lubliniecki', u'lubliniecki'),
|
||||||
|
('mikolowski', u'mikołowski'),
|
||||||
|
('myszkowski', u'myszkowski'),
|
||||||
|
('pszczynski', u'pszczyński'),
|
||||||
|
('raciborski', u'raciborski'),
|
||||||
|
('rybnicki', u'rybnicki'),
|
||||||
|
('tarnogorski', u'tarnogórski'),
|
||||||
|
('wodzislawski', u'wodzisławski'),
|
||||||
|
('zawiercianski', u'zawierciański'),
|
||||||
|
('zywiecki', u'żywiecki'),
|
||||||
|
('kielce', u'Kielce'),
|
||||||
|
('buski', u'buski'),
|
||||||
|
('jedrzejowski', u'jędrzejowski'),
|
||||||
|
('kazimierski', u'kazimierski'),
|
||||||
|
('kielecki', u'kielecki'),
|
||||||
|
('konecki', u'konecki'),
|
||||||
|
('opatowski', u'opatowski'),
|
||||||
|
('ostrowiecki', u'ostrowiecki'),
|
||||||
|
('pinczowski', u'pińczowski'),
|
||||||
|
('sandomierski', u'sandomierski'),
|
||||||
|
('skarzyski', u'skarżyski'),
|
||||||
|
('starachowicki', u'starachowicki'),
|
||||||
|
('staszowski', u'staszowski'),
|
||||||
|
('wloszczowski', u'włoszczowski'),
|
||||||
|
('olsztyn', u'Olsztyn'),
|
||||||
|
('elblag', u'Elbląg'),
|
||||||
|
('bartoszycki', u'bartoszycki'),
|
||||||
|
('braniewski', u'braniewski'),
|
||||||
|
('dzialdowski', u'działdowski'),
|
||||||
|
('elblaski', u'elbląski'),
|
||||||
|
('elcki', u'ełcki'),
|
||||||
|
('gizycki', u'giżycki'),
|
||||||
|
('goldapski', u'gołdapski'),
|
||||||
|
('ilawski', u'iławski'),
|
||||||
|
('ketrzynski', u'kętrzyński'),
|
||||||
|
('lidzbarski', u'lidzbarski'),
|
||||||
|
('mragowski', u'mrągowski'),
|
||||||
|
('nidzicki', u'nidzicki'),
|
||||||
|
('nowomiejski', u'nowomiejski'),
|
||||||
|
('olecki', u'olecki'),
|
||||||
|
('olsztynski', u'olsztyński'),
|
||||||
|
('ostrodzki', u'ostródzki'),
|
||||||
|
('piski', u'piski'),
|
||||||
|
('szczycienski', u'szczycieński'),
|
||||||
|
('wegorzewski', u'węgorzewski'),
|
||||||
|
('poznan', u'Poznań'),
|
||||||
|
('kalisz', u'Kalisz'),
|
||||||
|
('konin', u'Konin'),
|
||||||
|
('leszno', u'Leszno'),
|
||||||
|
('chodzieski', u'chodziejski'),
|
||||||
|
('czarnkowsko-trzcianecki', u'czarnkowsko-trzcianecki'),
|
||||||
|
('gnieznienski', u'gnieźnieński'),
|
||||||
|
('gostynski', u'gostyński'),
|
||||||
|
('grodziski', u'grodziski'),
|
||||||
|
('jarocinski', u'jarociński'),
|
||||||
|
('kaliski', u'kaliski'),
|
||||||
|
('kepinski', u'kępiński'),
|
||||||
|
('kolski', u'kolski'),
|
||||||
|
('koninski', u'koniński'),
|
||||||
|
('koscianski', u'kościański'),
|
||||||
|
('krotoszynski', u'krotoszyński'),
|
||||||
|
('leszczynski', u'leszczyński'),
|
||||||
|
('miedzychodzki', u'międzychodzki'),
|
||||||
|
('nowotomyski', u'nowotomyski'),
|
||||||
|
('obornicki', u'obornicki'),
|
||||||
|
('ostrowski', u'ostrowski'),
|
||||||
|
('ostrzeszowski', u'ostrzeszowski'),
|
||||||
|
('pilski', u'pilski'),
|
||||||
|
('pleszewski', u'pleszewski'),
|
||||||
|
('poznanski', u'poznański'),
|
||||||
|
('rawicki', u'rawicki'),
|
||||||
|
('slupecki', u'słupecki'),
|
||||||
|
('szamotulski', u'szamotulski'),
|
||||||
|
('sredzki', u'średzki'),
|
||||||
|
('sremski', u'śremski'),
|
||||||
|
('turecki', u'turecki'),
|
||||||
|
('wagrowiecki', u'wągrowiecki'),
|
||||||
|
('wolsztynski', u'wolsztyński'),
|
||||||
|
('wrzesinski', u'wrzesiński'),
|
||||||
|
('zlotowski', u'złotowski'),
|
||||||
|
('bialogardzki', u'białogardzki'),
|
||||||
|
('choszczenski', u'choszczeński'),
|
||||||
|
('drawski', u'drawski'),
|
||||||
|
('goleniowski', u'goleniowski'),
|
||||||
|
('gryficki', u'gryficki'),
|
||||||
|
('gryfinski', u'gryfiński'),
|
||||||
|
('kamienski', u'kamieński'),
|
||||||
|
('kolobrzeski', u'kołobrzeski'),
|
||||||
|
('koszalinski', u'koszaliński'),
|
||||||
|
('lobeski', u'łobeski'),
|
||||||
|
('mysliborski', u'myśliborski'),
|
||||||
|
('policki', u'policki'),
|
||||||
|
('pyrzycki', u'pyrzycki'),
|
||||||
|
('slawienski', u'sławieński'),
|
||||||
|
('stargardzki', u'stargardzki'),
|
||||||
|
('szczecinecki', u'szczecinecki'),
|
||||||
|
('swidwinski', u'świdwiński'),
|
||||||
|
('walecki', u'wałecki'),
|
||||||
|
)
|
||||||
|
|
24
django/contrib/localflavor/pl/pl_voivodeships.py
Normal file
24
django/contrib/localflavor/pl/pl_voivodeships.py
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
"""
|
||||||
|
Polish voivodeship as in http://en.wikipedia.org/wiki/Poland#Administrative_division
|
||||||
|
"""
|
||||||
|
|
||||||
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
|
VOIVODESHIP_CHOICES = (
|
||||||
|
('lower_silesia', _('Lower Silesia')),
|
||||||
|
('kuyavia-pomerania', _('Kuyavia-Pomerania')),
|
||||||
|
('lublin', _('Lublin')),
|
||||||
|
('lubusz', _('Lubusz')),
|
||||||
|
('lodz', _('Lodz')),
|
||||||
|
('lesser_poland', _('Lesser Poland')),
|
||||||
|
('masovia', _('Masovia')),
|
||||||
|
('opole', _('Opole')),
|
||||||
|
('subcarpatia', _('Subcarpatia')),
|
||||||
|
('podlasie', _('Podlasie')),
|
||||||
|
('pomerania', _('Pomerania')),
|
||||||
|
('silesia', _('Silesia')),
|
||||||
|
('swietokrzyskie', _('Swietokrzyskie')),
|
||||||
|
('warmia-masuria', _('Warmia-Masuria')),
|
||||||
|
('greater_poland', _('Greater Poland')),
|
||||||
|
('west_pomerania', _('West Pomerania')),
|
||||||
|
)
|
@ -34,9 +34,9 @@ class BaseCommand(object):
|
|||||||
if output:
|
if output:
|
||||||
if self.output_transaction:
|
if self.output_transaction:
|
||||||
# This needs to be imported here, because it relies on settings.
|
# This needs to be imported here, because it relies on settings.
|
||||||
from django.db import backend
|
from django.db import connection
|
||||||
if backend.get_start_transaction_sql():
|
if connection.ops.start_transaction_sql():
|
||||||
print self.style.SQL_KEYWORD(backend.get_start_transaction_sql())
|
print self.style.SQL_KEYWORD(connection.ops.start_transaction_sql())
|
||||||
print output
|
print output
|
||||||
if self.output_transaction:
|
if self.output_transaction:
|
||||||
print self.style.SQL_KEYWORD("COMMIT;")
|
print self.style.SQL_KEYWORD("COMMIT;")
|
||||||
|
@ -8,7 +8,7 @@ class Command(LabelCommand):
|
|||||||
requires_model_validation = False
|
requires_model_validation = False
|
||||||
|
|
||||||
def handle_label(self, tablename, **options):
|
def handle_label(self, tablename, **options):
|
||||||
from django.db import backend, connection, transaction, models
|
from django.db import connection, transaction, models
|
||||||
fields = (
|
fields = (
|
||||||
# "key" is a reserved word in MySQL, so use "cache_key" instead.
|
# "key" is a reserved word in MySQL, so use "cache_key" instead.
|
||||||
models.CharField(name='cache_key', max_length=255, unique=True, primary_key=True),
|
models.CharField(name='cache_key', max_length=255, unique=True, primary_key=True),
|
||||||
@ -17,8 +17,9 @@ class Command(LabelCommand):
|
|||||||
)
|
)
|
||||||
table_output = []
|
table_output = []
|
||||||
index_output = []
|
index_output = []
|
||||||
|
qn = connection.ops.quote_name
|
||||||
for f in fields:
|
for f in fields:
|
||||||
field_output = [backend.quote_name(f.name), f.db_type()]
|
field_output = [qn(f.name), f.db_type()]
|
||||||
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")
|
||||||
@ -27,10 +28,10 @@ class Command(LabelCommand):
|
|||||||
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);" % \
|
index_output.append("CREATE %sINDEX %s_%s ON %s (%s);" % \
|
||||||
(unique, tablename, f.name, backend.quote_name(tablename),
|
(unique, tablename, f.name, qn(tablename),
|
||||||
backend.quote_name(f.name)))
|
qn(f.name)))
|
||||||
table_output.append(" ".join(field_output))
|
table_output.append(" ".join(field_output))
|
||||||
full_statement = ["CREATE TABLE %s (" % backend.quote_name(tablename)]
|
full_statement = ["CREATE TABLE %s (" % qn(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(');')
|
||||||
|
@ -15,7 +15,7 @@ class Command(BaseCommand):
|
|||||||
def handle(self, *fixture_labels, **options):
|
def handle(self, *fixture_labels, **options):
|
||||||
from django.db.models import get_apps
|
from django.db.models import get_apps
|
||||||
from django.core import serializers
|
from django.core import serializers
|
||||||
from django.db import connection, transaction, backend
|
from django.db import connection, transaction
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
|
||||||
self.style = no_style()
|
self.style = no_style()
|
||||||
@ -105,7 +105,7 @@ class Command(BaseCommand):
|
|||||||
(format, fixture_name, humanize(fixture_dir))
|
(format, fixture_name, humanize(fixture_dir))
|
||||||
|
|
||||||
if count[0] > 0:
|
if count[0] > 0:
|
||||||
sequence_sql = backend.get_sql_sequence_reset(self.style, models)
|
sequence_sql = connection.ops.sequence_reset_sql(self.style, models)
|
||||||
if sequence_sql:
|
if sequence_sql:
|
||||||
if verbosity > 1:
|
if verbosity > 1:
|
||||||
print "Resetting sequences"
|
print "Resetting sequences"
|
||||||
|
@ -5,5 +5,5 @@ class Command(AppCommand):
|
|||||||
output_transaction = True
|
output_transaction = True
|
||||||
|
|
||||||
def handle_app(self, app, **options):
|
def handle_app(self, app, **options):
|
||||||
from django.db import backend, models
|
from django.db import connection, models
|
||||||
return '\n'.join(backend.get_sql_sequence_reset(self.style, models.get_models(app)))
|
return '\n'.join(connection.ops.sequence_reset_sql(self.style, models.get_models(app)))
|
||||||
|
@ -12,7 +12,7 @@ class Command(NoArgsCommand):
|
|||||||
args = '[--verbosity] [--noinput]'
|
args = '[--verbosity] [--noinput]'
|
||||||
|
|
||||||
def handle_noargs(self, **options):
|
def handle_noargs(self, **options):
|
||||||
from django.db import backend, connection, transaction, models
|
from django.db import connection, transaction, models
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.core.management.sql import table_list, installed_models, sql_model_create, sql_for_pending_references, many_to_many_sql_for_model, custom_sql_for_model, sql_indexes_for_model, emit_post_sync_signal
|
from django.core.management.sql import table_list, installed_models, sql_model_create, sql_for_pending_references, many_to_many_sql_for_model, custom_sql_for_model, sql_indexes_for_model, emit_post_sync_signal
|
||||||
|
|
||||||
@ -34,7 +34,7 @@ class Command(NoArgsCommand):
|
|||||||
# Get a list of all existing database tables,
|
# Get a list of all existing database tables,
|
||||||
# so we know what needs to be added.
|
# so we know what needs to be added.
|
||||||
table_list = table_list()
|
table_list = table_list()
|
||||||
if backend.uses_case_insensitive_names:
|
if connection.features.uses_case_insensitive_names:
|
||||||
table_name_converter = str.upper
|
table_name_converter = str.upper
|
||||||
else:
|
else:
|
||||||
table_name_converter = lambda x: x
|
table_name_converter = lambda x: x
|
||||||
@ -125,6 +125,6 @@ class Command(NoArgsCommand):
|
|||||||
else:
|
else:
|
||||||
transaction.commit_unless_managed()
|
transaction.commit_unless_managed()
|
||||||
|
|
||||||
# Install the 'initialdata' fixture, using format discovery
|
# Install the 'initial_data' fixture, using format discovery
|
||||||
from django.core.management import call_command
|
from django.core.management import call_command
|
||||||
call_command('loaddata', 'initial_data', **options)
|
call_command('loaddata', 'initial_data', verbosity=verbosity)
|
||||||
|
@ -15,12 +15,12 @@ def table_list():
|
|||||||
|
|
||||||
def installed_models(table_list):
|
def installed_models(table_list):
|
||||||
"Returns a set of all models that are installed, given a list of existing table names."
|
"Returns a set of all models that are installed, given a list of existing table names."
|
||||||
from django.db import backend, models
|
from django.db import connection, models
|
||||||
all_models = []
|
all_models = []
|
||||||
for app in models.get_apps():
|
for app in models.get_apps():
|
||||||
for model in models.get_models(app):
|
for model in models.get_models(app):
|
||||||
all_models.append(model)
|
all_models.append(model)
|
||||||
if backend.uses_case_insensitive_names:
|
if connection.features.uses_case_insensitive_names:
|
||||||
converter = lambda x: x.upper()
|
converter = lambda x: x.upper()
|
||||||
else:
|
else:
|
||||||
converter = lambda x: x
|
converter = lambda x: x
|
||||||
@ -95,7 +95,7 @@ def sql_create(app, style):
|
|||||||
|
|
||||||
def sql_delete(app, style):
|
def sql_delete(app, style):
|
||||||
"Returns a list of the DROP TABLE SQL statements for the given app."
|
"Returns a list of the DROP TABLE SQL statements for the given app."
|
||||||
from django.db import backend, connection, models, get_introspection_module
|
from django.db import connection, models, get_introspection_module
|
||||||
from django.db.backends.util import truncate_name
|
from django.db.backends.util import truncate_name
|
||||||
introspection = get_introspection_module()
|
introspection = get_introspection_module()
|
||||||
|
|
||||||
@ -110,12 +110,13 @@ def sql_delete(app, style):
|
|||||||
table_names = introspection.get_table_list(cursor)
|
table_names = introspection.get_table_list(cursor)
|
||||||
else:
|
else:
|
||||||
table_names = []
|
table_names = []
|
||||||
if backend.uses_case_insensitive_names:
|
if connection.features.uses_case_insensitive_names:
|
||||||
table_name_converter = str.upper
|
table_name_converter = str.upper
|
||||||
else:
|
else:
|
||||||
table_name_converter = lambda x: x
|
table_name_converter = lambda x: x
|
||||||
|
|
||||||
output = []
|
output = []
|
||||||
|
qn = connection.ops.quote_name
|
||||||
|
|
||||||
# Output DROP TABLE statements for standard application tables.
|
# Output DROP TABLE statements for standard application tables.
|
||||||
to_delete = set()
|
to_delete = set()
|
||||||
@ -136,8 +137,8 @@ def sql_delete(app, style):
|
|||||||
if cursor and table_name_converter(model._meta.db_table) in table_names:
|
if cursor and table_name_converter(model._meta.db_table) in table_names:
|
||||||
# Drop the table now
|
# Drop the table now
|
||||||
output.append('%s %s;' % (style.SQL_KEYWORD('DROP TABLE'),
|
output.append('%s %s;' % (style.SQL_KEYWORD('DROP TABLE'),
|
||||||
style.SQL_TABLE(backend.quote_name(model._meta.db_table))))
|
style.SQL_TABLE(qn(model._meta.db_table))))
|
||||||
if backend.supports_constraints and model in references_to_delete:
|
if connection.features.supports_constraints and model in references_to_delete:
|
||||||
for rel_class, f in references_to_delete[model]:
|
for rel_class, f in references_to_delete[model]:
|
||||||
table = rel_class._meta.db_table
|
table = rel_class._meta.db_table
|
||||||
col = f.column
|
col = f.column
|
||||||
@ -146,12 +147,14 @@ def sql_delete(app, style):
|
|||||||
r_name = '%s_refs_%s_%x' % (col, r_col, abs(hash((table, r_table))))
|
r_name = '%s_refs_%s_%x' % (col, r_col, abs(hash((table, r_table))))
|
||||||
output.append('%s %s %s %s;' % \
|
output.append('%s %s %s %s;' % \
|
||||||
(style.SQL_KEYWORD('ALTER TABLE'),
|
(style.SQL_KEYWORD('ALTER TABLE'),
|
||||||
style.SQL_TABLE(backend.quote_name(table)),
|
style.SQL_TABLE(qn(table)),
|
||||||
style.SQL_KEYWORD(backend.get_drop_foreignkey_sql()),
|
style.SQL_KEYWORD(connection.ops.drop_foreignkey_sql()),
|
||||||
style.SQL_FIELD(truncate_name(r_name, backend.get_max_name_length()))))
|
style.SQL_FIELD(truncate_name(r_name, connection.ops.max_name_length()))))
|
||||||
del references_to_delete[model]
|
del references_to_delete[model]
|
||||||
if model._meta.has_auto_field and hasattr(backend, 'get_drop_sequence'):
|
if model._meta.has_auto_field:
|
||||||
output.append(backend.get_drop_sequence(model._meta.db_table))
|
ds = connection.ops.drop_sequence_sql(model._meta.db_table)
|
||||||
|
if ds:
|
||||||
|
output.append(ds)
|
||||||
|
|
||||||
# Output DROP TABLE statements for many-to-many tables.
|
# Output DROP TABLE statements for many-to-many tables.
|
||||||
for model in app_models:
|
for model in app_models:
|
||||||
@ -159,9 +162,10 @@ def sql_delete(app, style):
|
|||||||
for f in opts.many_to_many:
|
for f in opts.many_to_many:
|
||||||
if cursor and table_name_converter(f.m2m_db_table()) in table_names:
|
if cursor and table_name_converter(f.m2m_db_table()) in table_names:
|
||||||
output.append("%s %s;" % (style.SQL_KEYWORD('DROP TABLE'),
|
output.append("%s %s;" % (style.SQL_KEYWORD('DROP TABLE'),
|
||||||
style.SQL_TABLE(backend.quote_name(f.m2m_db_table()))))
|
style.SQL_TABLE(qn(f.m2m_db_table()))))
|
||||||
if hasattr(backend, 'get_drop_sequence'):
|
ds = connection.ops.drop_sequence_sql("%s_%s" % (model._meta.db_table, f.column))
|
||||||
output.append(backend.get_drop_sequence("%s_%s" % (model._meta.db_table, f.column)))
|
if ds:
|
||||||
|
output.append(ds)
|
||||||
|
|
||||||
app_label = app_models[0]._meta.app_label
|
app_label = app_models[0]._meta.app_label
|
||||||
|
|
||||||
@ -178,9 +182,9 @@ def sql_reset(app, style):
|
|||||||
return sql_delete(app, style) + sql_all(app, style)
|
return sql_delete(app, style) + sql_all(app, style)
|
||||||
|
|
||||||
def sql_flush(style):
|
def sql_flush(style):
|
||||||
"Returns a list of the SQL statements used to flush the database"
|
"Returns a list of the SQL statements used to flush the database."
|
||||||
from django.db import backend
|
from django.db import connection
|
||||||
statements = backend.get_sql_flush(style, table_list(), sequence_list())
|
statements = connection.ops.sql_flush(style, table_list(), sequence_list())
|
||||||
return statements
|
return statements
|
||||||
|
|
||||||
def sql_custom(app):
|
def sql_custom(app):
|
||||||
@ -213,12 +217,13 @@ def sql_model_create(model, style, known_models=set()):
|
|||||||
Returns the SQL required to create a single model, as a tuple of:
|
Returns the SQL required to create a single model, as a tuple of:
|
||||||
(list_of_sql, pending_references_dict)
|
(list_of_sql, pending_references_dict)
|
||||||
"""
|
"""
|
||||||
from django.db import backend, models
|
from django.db import connection, models
|
||||||
|
|
||||||
opts = model._meta
|
opts = model._meta
|
||||||
final_output = []
|
final_output = []
|
||||||
table_output = []
|
table_output = []
|
||||||
pending_references = {}
|
pending_references = {}
|
||||||
|
qn = connection.ops.quote_name
|
||||||
for f in opts.fields:
|
for f in opts.fields:
|
||||||
col_type = f.db_type()
|
col_type = f.db_type()
|
||||||
tablespace = f.db_tablespace or opts.db_tablespace
|
tablespace = f.db_tablespace or opts.db_tablespace
|
||||||
@ -227,23 +232,23 @@ def sql_model_create(model, style, known_models=set()):
|
|||||||
# database columns in this table.
|
# database columns in this table.
|
||||||
continue
|
continue
|
||||||
# Make the definition (e.g. 'foo VARCHAR(30)') for this field.
|
# Make the definition (e.g. 'foo VARCHAR(30)') for this field.
|
||||||
field_output = [style.SQL_FIELD(backend.quote_name(f.column)),
|
field_output = [style.SQL_FIELD(qn(f.column)),
|
||||||
style.SQL_COLTYPE(col_type)]
|
style.SQL_COLTYPE(col_type)]
|
||||||
field_output.append(style.SQL_KEYWORD('%sNULL' % (not f.null and 'NOT ' or '')))
|
field_output.append(style.SQL_KEYWORD('%sNULL' % (not f.null and 'NOT ' or '')))
|
||||||
if f.unique and (not f.primary_key or backend.allows_unique_and_pk):
|
if f.unique and (not f.primary_key or connection.features.allows_unique_and_pk):
|
||||||
field_output.append(style.SQL_KEYWORD('UNIQUE'))
|
field_output.append(style.SQL_KEYWORD('UNIQUE'))
|
||||||
if f.primary_key:
|
if f.primary_key:
|
||||||
field_output.append(style.SQL_KEYWORD('PRIMARY KEY'))
|
field_output.append(style.SQL_KEYWORD('PRIMARY KEY'))
|
||||||
if tablespace and backend.supports_tablespaces and (f.unique or f.primary_key) and backend.autoindexes_primary_keys:
|
if tablespace and connection.features.supports_tablespaces and (f.unique or f.primary_key) and connection.features.autoindexes_primary_keys:
|
||||||
# We must specify the index tablespace inline, because we
|
# We must specify the index tablespace inline, because we
|
||||||
# won't be generating a CREATE INDEX statement for this field.
|
# won't be generating a CREATE INDEX statement for this field.
|
||||||
field_output.append(backend.get_tablespace_sql(tablespace, inline=True))
|
field_output.append(connection.ops.tablespace_sql(tablespace, inline=True))
|
||||||
if f.rel:
|
if f.rel:
|
||||||
if f.rel.to in known_models:
|
if f.rel.to in known_models:
|
||||||
field_output.append(style.SQL_KEYWORD('REFERENCES') + ' ' + \
|
field_output.append(style.SQL_KEYWORD('REFERENCES') + ' ' + \
|
||||||
style.SQL_TABLE(backend.quote_name(f.rel.to._meta.db_table)) + ' (' + \
|
style.SQL_TABLE(qn(f.rel.to._meta.db_table)) + ' (' + \
|
||||||
style.SQL_FIELD(backend.quote_name(f.rel.to._meta.get_field(f.rel.field_name).column)) + ')' +
|
style.SQL_FIELD(qn(f.rel.to._meta.get_field(f.rel.field_name).column)) + ')' +
|
||||||
backend.get_deferrable_sql()
|
connection.ops.deferrable_sql()
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
# We haven't yet created the table to which this field
|
# We haven't yet created the table to which this field
|
||||||
@ -251,25 +256,25 @@ def sql_model_create(model, style, known_models=set()):
|
|||||||
pr = pending_references.setdefault(f.rel.to, []).append((model, f))
|
pr = pending_references.setdefault(f.rel.to, []).append((model, f))
|
||||||
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(style.SQL_FIELD(backend.quote_name('_order')) + ' ' + \
|
table_output.append(style.SQL_FIELD(qn('_order')) + ' ' + \
|
||||||
style.SQL_COLTYPE(models.IntegerField().db_type()) + ' ' + \
|
style.SQL_COLTYPE(models.IntegerField().db_type()) + ' ' + \
|
||||||
style.SQL_KEYWORD('NULL'))
|
style.SQL_KEYWORD('NULL'))
|
||||||
for field_constraints in opts.unique_together:
|
for field_constraints in opts.unique_together:
|
||||||
table_output.append(style.SQL_KEYWORD('UNIQUE') + ' (%s)' % \
|
table_output.append(style.SQL_KEYWORD('UNIQUE') + ' (%s)' % \
|
||||||
", ".join([backend.quote_name(style.SQL_FIELD(opts.get_field(f).column)) for f in field_constraints]))
|
", ".join([qn(style.SQL_FIELD(opts.get_field(f).column)) for f in field_constraints]))
|
||||||
|
|
||||||
full_statement = [style.SQL_KEYWORD('CREATE TABLE') + ' ' + style.SQL_TABLE(backend.quote_name(opts.db_table)) + ' (']
|
full_statement = [style.SQL_KEYWORD('CREATE TABLE') + ' ' + style.SQL_TABLE(qn(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(')')
|
||||||
if opts.db_tablespace and backend.supports_tablespaces:
|
if opts.db_tablespace and connection.features.supports_tablespaces:
|
||||||
full_statement.append(backend.get_tablespace_sql(opts.db_tablespace))
|
full_statement.append(connection.ops.tablespace_sql(opts.db_tablespace))
|
||||||
full_statement.append(';')
|
full_statement.append(';')
|
||||||
final_output.append('\n'.join(full_statement))
|
final_output.append('\n'.join(full_statement))
|
||||||
|
|
||||||
if opts.has_auto_field and hasattr(backend, 'get_autoinc_sql'):
|
if opts.has_auto_field:
|
||||||
# Add any extra SQL needed to support auto-incrementing primary keys
|
# Add any extra SQL needed to support auto-incrementing primary keys.
|
||||||
autoinc_sql = backend.get_autoinc_sql(opts.db_table)
|
autoinc_sql = connection.ops.autoinc_sql(opts.db_table)
|
||||||
if autoinc_sql:
|
if autoinc_sql:
|
||||||
for stmt in autoinc_sql:
|
for stmt in autoinc_sql:
|
||||||
final_output.append(stmt)
|
final_output.append(stmt)
|
||||||
@ -280,11 +285,12 @@ def sql_for_pending_references(model, style, pending_references):
|
|||||||
"""
|
"""
|
||||||
Returns any ALTER TABLE statements to add constraints after the fact.
|
Returns any ALTER TABLE statements to add constraints after the fact.
|
||||||
"""
|
"""
|
||||||
from django.db import backend
|
from django.db import connection
|
||||||
from django.db.backends.util import truncate_name
|
from django.db.backends.util import truncate_name
|
||||||
|
|
||||||
|
qn = connection.ops.quote_name
|
||||||
final_output = []
|
final_output = []
|
||||||
if backend.supports_constraints:
|
if connection.features.supports_constraints:
|
||||||
opts = model._meta
|
opts = model._meta
|
||||||
if model in pending_references:
|
if model in pending_references:
|
||||||
for rel_class, f in pending_references[model]:
|
for rel_class, f in pending_references[model]:
|
||||||
@ -297,60 +303,61 @@ def sql_for_pending_references(model, style, pending_references):
|
|||||||
# So we are careful with character usage here.
|
# So we are careful with character usage here.
|
||||||
r_name = '%s_refs_%s_%x' % (r_col, col, abs(hash((r_table, table))))
|
r_name = '%s_refs_%s_%x' % (r_col, col, abs(hash((r_table, table))))
|
||||||
final_output.append(style.SQL_KEYWORD('ALTER TABLE') + ' %s ADD CONSTRAINT %s FOREIGN KEY (%s) REFERENCES %s (%s)%s;' % \
|
final_output.append(style.SQL_KEYWORD('ALTER TABLE') + ' %s ADD CONSTRAINT %s FOREIGN KEY (%s) REFERENCES %s (%s)%s;' % \
|
||||||
(backend.quote_name(r_table), truncate_name(r_name, backend.get_max_name_length()),
|
(qn(r_table), truncate_name(r_name, connection.ops.max_name_length()),
|
||||||
backend.quote_name(r_col), backend.quote_name(table), backend.quote_name(col),
|
qn(r_col), qn(table), qn(col),
|
||||||
backend.get_deferrable_sql()))
|
connection.ops.deferrable_sql()))
|
||||||
del pending_references[model]
|
del pending_references[model]
|
||||||
return final_output
|
return final_output
|
||||||
|
|
||||||
def many_to_many_sql_for_model(model, style):
|
def many_to_many_sql_for_model(model, style):
|
||||||
from django.db import backend, models
|
from django.db import connection, models
|
||||||
from django.contrib.contenttypes import generic
|
from django.contrib.contenttypes import generic
|
||||||
|
|
||||||
opts = model._meta
|
opts = model._meta
|
||||||
final_output = []
|
final_output = []
|
||||||
|
qn = connection.ops.quote_name
|
||||||
for f in opts.many_to_many:
|
for f in opts.many_to_many:
|
||||||
if not isinstance(f.rel, generic.GenericRel):
|
if not isinstance(f.rel, generic.GenericRel):
|
||||||
tablespace = f.db_tablespace or opts.db_tablespace
|
tablespace = f.db_tablespace or opts.db_tablespace
|
||||||
if tablespace and backend.supports_tablespaces and backend.autoindexes_primary_keys:
|
if tablespace and connection.features.supports_tablespaces and connection.features.autoindexes_primary_keys:
|
||||||
tablespace_sql = ' ' + backend.get_tablespace_sql(tablespace, inline=True)
|
tablespace_sql = ' ' + connection.ops.tablespace_sql(tablespace, inline=True)
|
||||||
else:
|
else:
|
||||||
tablespace_sql = ''
|
tablespace_sql = ''
|
||||||
table_output = [style.SQL_KEYWORD('CREATE TABLE') + ' ' + \
|
table_output = [style.SQL_KEYWORD('CREATE TABLE') + ' ' + \
|
||||||
style.SQL_TABLE(backend.quote_name(f.m2m_db_table())) + ' (']
|
style.SQL_TABLE(qn(f.m2m_db_table())) + ' (']
|
||||||
table_output.append(' %s %s %s%s,' % \
|
table_output.append(' %s %s %s%s,' % \
|
||||||
(style.SQL_FIELD(backend.quote_name('id')),
|
(style.SQL_FIELD(qn('id')),
|
||||||
style.SQL_COLTYPE(models.AutoField(primary_key=True).db_type()),
|
style.SQL_COLTYPE(models.AutoField(primary_key=True).db_type()),
|
||||||
style.SQL_KEYWORD('NOT NULL PRIMARY KEY'),
|
style.SQL_KEYWORD('NOT NULL PRIMARY KEY'),
|
||||||
tablespace_sql))
|
tablespace_sql))
|
||||||
table_output.append(' %s %s %s %s (%s)%s,' % \
|
table_output.append(' %s %s %s %s (%s)%s,' % \
|
||||||
(style.SQL_FIELD(backend.quote_name(f.m2m_column_name())),
|
(style.SQL_FIELD(qn(f.m2m_column_name())),
|
||||||
style.SQL_COLTYPE(models.ForeignKey(model).db_type()),
|
style.SQL_COLTYPE(models.ForeignKey(model).db_type()),
|
||||||
style.SQL_KEYWORD('NOT NULL REFERENCES'),
|
style.SQL_KEYWORD('NOT NULL REFERENCES'),
|
||||||
style.SQL_TABLE(backend.quote_name(opts.db_table)),
|
style.SQL_TABLE(qn(opts.db_table)),
|
||||||
style.SQL_FIELD(backend.quote_name(opts.pk.column)),
|
style.SQL_FIELD(qn(opts.pk.column)),
|
||||||
backend.get_deferrable_sql()))
|
connection.ops.deferrable_sql()))
|
||||||
table_output.append(' %s %s %s %s (%s)%s,' % \
|
table_output.append(' %s %s %s %s (%s)%s,' % \
|
||||||
(style.SQL_FIELD(backend.quote_name(f.m2m_reverse_name())),
|
(style.SQL_FIELD(qn(f.m2m_reverse_name())),
|
||||||
style.SQL_COLTYPE(models.ForeignKey(f.rel.to).db_type()),
|
style.SQL_COLTYPE(models.ForeignKey(f.rel.to).db_type()),
|
||||||
style.SQL_KEYWORD('NOT NULL REFERENCES'),
|
style.SQL_KEYWORD('NOT NULL REFERENCES'),
|
||||||
style.SQL_TABLE(backend.quote_name(f.rel.to._meta.db_table)),
|
style.SQL_TABLE(qn(f.rel.to._meta.db_table)),
|
||||||
style.SQL_FIELD(backend.quote_name(f.rel.to._meta.pk.column)),
|
style.SQL_FIELD(qn(f.rel.to._meta.pk.column)),
|
||||||
backend.get_deferrable_sql()))
|
connection.ops.deferrable_sql()))
|
||||||
table_output.append(' %s (%s, %s)%s' % \
|
table_output.append(' %s (%s, %s)%s' % \
|
||||||
(style.SQL_KEYWORD('UNIQUE'),
|
(style.SQL_KEYWORD('UNIQUE'),
|
||||||
style.SQL_FIELD(backend.quote_name(f.m2m_column_name())),
|
style.SQL_FIELD(qn(f.m2m_column_name())),
|
||||||
style.SQL_FIELD(backend.quote_name(f.m2m_reverse_name())),
|
style.SQL_FIELD(qn(f.m2m_reverse_name())),
|
||||||
tablespace_sql))
|
tablespace_sql))
|
||||||
table_output.append(')')
|
table_output.append(')')
|
||||||
if opts.db_tablespace and backend.supports_tablespaces:
|
if opts.db_tablespace and connection.features.supports_tablespaces:
|
||||||
# f.db_tablespace is only for indices, so ignore its value here.
|
# f.db_tablespace is only for indices, so ignore its value here.
|
||||||
table_output.append(backend.get_tablespace_sql(opts.db_tablespace))
|
table_output.append(connection.ops.tablespace_sql(opts.db_tablespace))
|
||||||
table_output.append(';')
|
table_output.append(';')
|
||||||
final_output.append('\n'.join(table_output))
|
final_output.append('\n'.join(table_output))
|
||||||
|
|
||||||
# Add any extra SQL needed to support auto-incrementing PKs
|
# Add any extra SQL needed to support auto-incrementing PKs
|
||||||
autoinc_sql = backend.get_autoinc_sql(f.m2m_db_table())
|
autoinc_sql = connection.ops.autoinc_sql(f.m2m_db_table())
|
||||||
if autoinc_sql:
|
if autoinc_sql:
|
||||||
for stmt in autoinc_sql:
|
for stmt in autoinc_sql:
|
||||||
final_output.append(stmt)
|
final_output.append(stmt)
|
||||||
@ -386,23 +393,24 @@ def custom_sql_for_model(model):
|
|||||||
|
|
||||||
def sql_indexes_for_model(model, style):
|
def sql_indexes_for_model(model, style):
|
||||||
"Returns the CREATE INDEX SQL statements for a single model"
|
"Returns the CREATE INDEX SQL statements for a single model"
|
||||||
from django.db import backend
|
from django.db import connection
|
||||||
output = []
|
output = []
|
||||||
|
|
||||||
|
qn = connection.ops.quote_name
|
||||||
for f in model._meta.fields:
|
for f in model._meta.fields:
|
||||||
if f.db_index and not ((f.primary_key or f.unique) and backend.autoindexes_primary_keys):
|
if f.db_index and not ((f.primary_key or f.unique) and connection.features.autoindexes_primary_keys):
|
||||||
unique = f.unique and 'UNIQUE ' or ''
|
unique = f.unique and 'UNIQUE ' or ''
|
||||||
tablespace = f.db_tablespace or model._meta.db_tablespace
|
tablespace = f.db_tablespace or model._meta.db_tablespace
|
||||||
if tablespace and backend.supports_tablespaces:
|
if tablespace and connection.features.supports_tablespaces:
|
||||||
tablespace_sql = ' ' + backend.get_tablespace_sql(tablespace)
|
tablespace_sql = ' ' + connection.ops.tablespace_sql(tablespace)
|
||||||
else:
|
else:
|
||||||
tablespace_sql = ''
|
tablespace_sql = ''
|
||||||
output.append(
|
output.append(
|
||||||
style.SQL_KEYWORD('CREATE %sINDEX' % unique) + ' ' + \
|
style.SQL_KEYWORD('CREATE %sINDEX' % unique) + ' ' + \
|
||||||
style.SQL_TABLE(backend.quote_name('%s_%s' % (model._meta.db_table, f.column))) + ' ' + \
|
style.SQL_TABLE(qn('%s_%s' % (model._meta.db_table, f.column))) + ' ' + \
|
||||||
style.SQL_KEYWORD('ON') + ' ' + \
|
style.SQL_KEYWORD('ON') + ' ' + \
|
||||||
style.SQL_TABLE(backend.quote_name(model._meta.db_table)) + ' ' + \
|
style.SQL_TABLE(qn(model._meta.db_table)) + ' ' + \
|
||||||
"(%s)" % style.SQL_FIELD(backend.quote_name(f.column)) + \
|
"(%s)" % style.SQL_FIELD(qn(f.column)) + \
|
||||||
"%s;" % tablespace_sql
|
"%s;" % tablespace_sql
|
||||||
)
|
)
|
||||||
return output
|
return output
|
||||||
|
@ -0,0 +1,218 @@
|
|||||||
|
try:
|
||||||
|
# Only exists in Python 2.4+
|
||||||
|
from threading import local
|
||||||
|
except ImportError:
|
||||||
|
# Import copy of _thread_local.py from Python 2.4
|
||||||
|
from django.utils._threading_local import local
|
||||||
|
|
||||||
|
class BaseDatabaseWrapper(local):
|
||||||
|
"""
|
||||||
|
Represents a database connection.
|
||||||
|
"""
|
||||||
|
ops = None
|
||||||
|
def __init__(self, **kwargs):
|
||||||
|
self.connection = None
|
||||||
|
self.queries = []
|
||||||
|
self.options = kwargs
|
||||||
|
|
||||||
|
def _commit(self):
|
||||||
|
if self.connection is not None:
|
||||||
|
return self.connection.commit()
|
||||||
|
|
||||||
|
def _rollback(self):
|
||||||
|
if self.connection is not None:
|
||||||
|
return self.connection.rollback()
|
||||||
|
|
||||||
|
def close(self):
|
||||||
|
if self.connection is not None:
|
||||||
|
self.connection.close()
|
||||||
|
self.connection = None
|
||||||
|
|
||||||
|
def cursor(self):
|
||||||
|
from django.conf import settings
|
||||||
|
cursor = self._cursor(settings)
|
||||||
|
if settings.DEBUG:
|
||||||
|
return self.make_debug_cursor(cursor)
|
||||||
|
return cursor
|
||||||
|
|
||||||
|
def make_debug_cursor(self, cursor):
|
||||||
|
from django.db.backends import util
|
||||||
|
return util.CursorDebugWrapper(cursor, self)
|
||||||
|
|
||||||
|
class BaseDatabaseFeatures(object):
|
||||||
|
allows_group_by_ordinal = True
|
||||||
|
allows_unique_and_pk = True
|
||||||
|
autoindexes_primary_keys = True
|
||||||
|
needs_datetime_string_cast = True
|
||||||
|
needs_upper_for_iops = False
|
||||||
|
supports_constraints = True
|
||||||
|
supports_tablespaces = False
|
||||||
|
uses_case_insensitive_names = False
|
||||||
|
uses_custom_queryset = False
|
||||||
|
|
||||||
|
class BaseDatabaseOperations(object):
|
||||||
|
"""
|
||||||
|
This class encapsulates all backend-specific differences, such as the way
|
||||||
|
a backend performs ordering or calculates the ID of a recently-inserted
|
||||||
|
row.
|
||||||
|
"""
|
||||||
|
def autoinc_sql(self, table):
|
||||||
|
"""
|
||||||
|
Returns any SQL needed to support auto-incrementing primary keys, or
|
||||||
|
None if no SQL is necessary.
|
||||||
|
|
||||||
|
This SQL is executed when a table is created.
|
||||||
|
"""
|
||||||
|
return None
|
||||||
|
|
||||||
|
def date_extract_sql(self, lookup_type, field_name):
|
||||||
|
"""
|
||||||
|
Given a lookup_type of 'year', 'month' or 'day', returns the SQL that
|
||||||
|
extracts a value from the given date field field_name.
|
||||||
|
"""
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
def date_trunc_sql(self, lookup_type, field_name):
|
||||||
|
"""
|
||||||
|
Given a lookup_type of 'year', 'month' or 'day', returns the SQL that
|
||||||
|
truncates the given date field field_name to a DATE object with only
|
||||||
|
the given specificity.
|
||||||
|
"""
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
def datetime_cast_sql(self):
|
||||||
|
"""
|
||||||
|
Returns the SQL necessary to cast a datetime value so that it will be
|
||||||
|
retrieved as a Python datetime object instead of a string.
|
||||||
|
|
||||||
|
This SQL should include a '%s' in place of the field's name. This
|
||||||
|
method should return None if no casting is necessary.
|
||||||
|
"""
|
||||||
|
return None
|
||||||
|
|
||||||
|
def deferrable_sql(self):
|
||||||
|
"""
|
||||||
|
Returns the SQL necessary to make a constraint "initially deferred"
|
||||||
|
during a CREATE TABLE statement.
|
||||||
|
"""
|
||||||
|
return ''
|
||||||
|
|
||||||
|
def drop_foreignkey_sql(self):
|
||||||
|
"""
|
||||||
|
Returns the SQL command that drops a foreign key.
|
||||||
|
"""
|
||||||
|
return "DROP CONSTRAINT"
|
||||||
|
|
||||||
|
def drop_sequence_sql(self, table):
|
||||||
|
"""
|
||||||
|
Returns any SQL necessary to drop the sequence for the given table.
|
||||||
|
Returns None if no SQL is necessary.
|
||||||
|
"""
|
||||||
|
return None
|
||||||
|
|
||||||
|
def field_cast_sql(self, db_type):
|
||||||
|
"""
|
||||||
|
Given a column type (e.g. 'BLOB', 'VARCHAR'), returns the SQL necessary
|
||||||
|
to cast it before using it in a WHERE statement. Note that the
|
||||||
|
resulting string should contain a '%s' placeholder for the column being
|
||||||
|
searched against.
|
||||||
|
"""
|
||||||
|
return '%s'
|
||||||
|
|
||||||
|
def fulltext_search_sql(self, field_name):
|
||||||
|
"""
|
||||||
|
Returns the SQL WHERE clause to use in order to perform a full-text
|
||||||
|
search of the given field_name. Note that the resulting string should
|
||||||
|
contain a '%s' placeholder for the value being searched against.
|
||||||
|
"""
|
||||||
|
raise NotImplementedError('Full-text search is not implemented for this database backend')
|
||||||
|
|
||||||
|
def last_insert_id(self, cursor, table_name, pk_name):
|
||||||
|
"""
|
||||||
|
Given a cursor object that has just performed an INSERT statement into
|
||||||
|
a table that has an auto-incrementing ID, returns the newly created ID.
|
||||||
|
|
||||||
|
This method also receives the table name and the name of the primary-key
|
||||||
|
column.
|
||||||
|
"""
|
||||||
|
return cursor.lastrowid
|
||||||
|
|
||||||
|
def limit_offset_sql(self, limit, offset=None):
|
||||||
|
"""
|
||||||
|
Returns a LIMIT/OFFSET SQL clause, given a limit and optional offset.
|
||||||
|
"""
|
||||||
|
# 'LIMIT 40 OFFSET 20'
|
||||||
|
sql = "LIMIT %s" % limit
|
||||||
|
if offset and offset != 0:
|
||||||
|
sql += " OFFSET %s" % offset
|
||||||
|
return sql
|
||||||
|
|
||||||
|
def max_name_length(self):
|
||||||
|
"""
|
||||||
|
Returns the maximum length of table and column names, or None if there
|
||||||
|
is no limit.
|
||||||
|
"""
|
||||||
|
return None
|
||||||
|
|
||||||
|
def pk_default_value(self):
|
||||||
|
"""
|
||||||
|
Returns the value to use during an INSERT statement to specify that
|
||||||
|
the field should use its default value.
|
||||||
|
"""
|
||||||
|
return 'DEFAULT'
|
||||||
|
|
||||||
|
def query_set_class(self, DefaultQuerySet):
|
||||||
|
"""
|
||||||
|
Given the default QuerySet class, returns a custom QuerySet class
|
||||||
|
to use for this backend. Returns None if a custom QuerySet isn't used.
|
||||||
|
See also BaseDatabaseFeatures.uses_custom_queryset, which regulates
|
||||||
|
whether this method is called at all.
|
||||||
|
"""
|
||||||
|
return None
|
||||||
|
|
||||||
|
def quote_name(self, name):
|
||||||
|
"""
|
||||||
|
Returns a quoted version of the given table, index or column name. Does
|
||||||
|
not quote the given name if it's already been quoted.
|
||||||
|
"""
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
def random_function_sql(self):
|
||||||
|
"""
|
||||||
|
Returns a SQL expression that returns a random value.
|
||||||
|
"""
|
||||||
|
return 'RANDOM()'
|
||||||
|
|
||||||
|
def sql_flush(self, style, tables, sequences):
|
||||||
|
"""
|
||||||
|
Returns a list of SQL statements required to remove all data from
|
||||||
|
the given database tables (without actually removing the tables
|
||||||
|
themselves).
|
||||||
|
|
||||||
|
The `style` argument is a Style object as returned by either
|
||||||
|
color_style() or no_style() in django.core.management.color.
|
||||||
|
"""
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
def sequence_reset_sql(self, style, model_list):
|
||||||
|
"""
|
||||||
|
Returns a list of the SQL statements required to reset sequences for
|
||||||
|
the given models.
|
||||||
|
|
||||||
|
The `style` argument is a Style object as returned by either
|
||||||
|
color_style() or no_style() in django.core.management.color.
|
||||||
|
"""
|
||||||
|
return [] # No sequence reset required by default.
|
||||||
|
|
||||||
|
def start_transaction_sql(self):
|
||||||
|
"""
|
||||||
|
Returns the SQL statement required to start a transaction.
|
||||||
|
"""
|
||||||
|
return "BEGIN;"
|
||||||
|
|
||||||
|
def tablespace_sql(self, tablespace, inline=False):
|
||||||
|
"""
|
||||||
|
Returns the tablespace SQL, or None if the backend doesn't use
|
||||||
|
tablespaces.
|
||||||
|
"""
|
||||||
|
return None
|
@ -4,12 +4,12 @@ ADO MSSQL database backend for Django.
|
|||||||
Requires adodbapi 2.0.1: http://adodbapi.sourceforge.net/
|
Requires adodbapi 2.0.1: http://adodbapi.sourceforge.net/
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from django.db.backends import util
|
from django.db.backends import BaseDatabaseWrapper, BaseDatabaseFeatures, BaseDatabaseOperations, util
|
||||||
try:
|
try:
|
||||||
import adodbapi as Database
|
import adodbapi as Database
|
||||||
except ImportError, e:
|
except ImportError, e:
|
||||||
from django.core.exceptions import ImproperlyConfigured
|
from django.core.exceptions import ImproperlyConfigured
|
||||||
raise ImproperlyConfigured, "Error loading adodbapi module: %s" % e
|
raise ImproperlyConfigured("Error loading adodbapi module: %s" % e)
|
||||||
import datetime
|
import datetime
|
||||||
try:
|
try:
|
||||||
import mx
|
import mx
|
||||||
@ -48,138 +48,43 @@ def variantToPython(variant, adType):
|
|||||||
return res
|
return res
|
||||||
Database.convertVariantToPython = variantToPython
|
Database.convertVariantToPython = variantToPython
|
||||||
|
|
||||||
try:
|
class DatabaseFeatures(BaseDatabaseFeatures):
|
||||||
# Only exists in Python 2.4+
|
supports_tablespaces = True
|
||||||
from threading import local
|
|
||||||
except ImportError:
|
|
||||||
# Import copy of _thread_local.py from Python 2.4
|
|
||||||
from django.utils._threading_local import local
|
|
||||||
|
|
||||||
class DatabaseWrapper(local):
|
class DatabaseOperations(BaseDatabaseOperations):
|
||||||
def __init__(self, **kwargs):
|
def date_extract_sql(self, lookup_type, field_name):
|
||||||
self.connection = None
|
return "DATEPART(%s, %s)" % (lookup_type, field_name)
|
||||||
self.queries = []
|
|
||||||
|
|
||||||
def cursor(self):
|
def date_trunc_sql(self, lookup_type, field_name):
|
||||||
from django.conf import settings
|
if lookup_type == 'year':
|
||||||
if self.connection is None:
|
return "Convert(datetime, Convert(varchar, DATEPART(year, %s)) + '/01/01')" % field_name
|
||||||
if settings.DATABASE_NAME == '' or settings.DATABASE_USER == '':
|
if lookup_type == 'month':
|
||||||
from django.core.exceptions import ImproperlyConfigured
|
return "Convert(datetime, Convert(varchar, DATEPART(year, %s)) + '/' + Convert(varchar, DATEPART(month, %s)) + '/01')" % (field_name, field_name)
|
||||||
raise ImproperlyConfigured, "You need to specify both DATABASE_NAME and DATABASE_USER in your Django settings file."
|
if lookup_type == 'day':
|
||||||
if not settings.DATABASE_HOST:
|
return "Convert(datetime, Convert(varchar(12), %s))" % field_name
|
||||||
settings.DATABASE_HOST = "127.0.0.1"
|
|
||||||
# TODO: Handle DATABASE_PORT.
|
|
||||||
conn_string = "PROVIDER=SQLOLEDB;DATA SOURCE=%s;UID=%s;PWD=%s;DATABASE=%s" % (settings.DATABASE_HOST, settings.DATABASE_USER, settings.DATABASE_PASSWORD, settings.DATABASE_NAME)
|
|
||||||
self.connection = Database.connect(conn_string)
|
|
||||||
cursor = self.connection.cursor()
|
|
||||||
if settings.DEBUG:
|
|
||||||
return util.CursorDebugWrapper(cursor, self)
|
|
||||||
return cursor
|
|
||||||
|
|
||||||
def _commit(self):
|
def deferrable_sql(self):
|
||||||
if self.connection is not None:
|
return " DEFERRABLE INITIALLY DEFERRED"
|
||||||
return self.connection.commit()
|
|
||||||
|
|
||||||
def _rollback(self):
|
def last_insert_id(self, cursor, table_name, pk_name):
|
||||||
if self.connection is not None:
|
cursor.execute("SELECT %s FROM %s WHERE %s = @@IDENTITY" % (pk_name, table_name, pk_name))
|
||||||
return self.connection.rollback()
|
return cursor.fetchone()[0]
|
||||||
|
|
||||||
def close(self):
|
def quote_name(self, name):
|
||||||
if self.connection is not None:
|
|
||||||
self.connection.close()
|
|
||||||
self.connection = None
|
|
||||||
|
|
||||||
allows_group_by_ordinal = True
|
|
||||||
allows_unique_and_pk = True
|
|
||||||
autoindexes_primary_keys = True
|
|
||||||
needs_datetime_string_cast = True
|
|
||||||
needs_upper_for_iops = False
|
|
||||||
supports_constraints = True
|
|
||||||
supports_tablespaces = True
|
|
||||||
uses_case_insensitive_names = False
|
|
||||||
|
|
||||||
def quote_name(name):
|
|
||||||
if name.startswith('[') and name.endswith(']'):
|
if name.startswith('[') and name.endswith(']'):
|
||||||
return name # Quoting once is enough.
|
return name # Quoting once is enough.
|
||||||
return '[%s]' % name
|
return '[%s]' % name
|
||||||
|
|
||||||
dictfetchone = util.dictfetchone
|
def random_function_sql(self):
|
||||||
dictfetchmany = util.dictfetchmany
|
return 'RAND()'
|
||||||
dictfetchall = util.dictfetchall
|
|
||||||
|
|
||||||
def get_last_insert_id(cursor, table_name, pk_name):
|
def tablespace_sql(self, tablespace, inline=False):
|
||||||
cursor.execute("SELECT %s FROM %s WHERE %s = @@IDENTITY" % (pk_name, table_name, pk_name))
|
return "ON %s" % self.quote_name(tablespace)
|
||||||
return cursor.fetchone()[0]
|
|
||||||
|
|
||||||
def get_date_extract_sql(lookup_type, table_name):
|
class DatabaseWrapper(BaseDatabaseWrapper):
|
||||||
# lookup_type is 'year', 'month', 'day'
|
features = DatabaseFeatures()
|
||||||
return "DATEPART(%s, %s)" % (lookup_type, table_name)
|
ops = DatabaseOperations()
|
||||||
|
operators = {
|
||||||
def get_date_trunc_sql(lookup_type, field_name):
|
|
||||||
# lookup_type is 'year', 'month', 'day'
|
|
||||||
if lookup_type=='year':
|
|
||||||
return "Convert(datetime, Convert(varchar, DATEPART(year, %s)) + '/01/01')" % field_name
|
|
||||||
if lookup_type=='month':
|
|
||||||
return "Convert(datetime, Convert(varchar, DATEPART(year, %s)) + '/' + Convert(varchar, DATEPART(month, %s)) + '/01')" % (field_name, field_name)
|
|
||||||
if lookup_type=='day':
|
|
||||||
return "Convert(datetime, Convert(varchar(12), %s))" % field_name
|
|
||||||
|
|
||||||
def get_datetime_cast_sql():
|
|
||||||
return None
|
|
||||||
|
|
||||||
def get_limit_offset_sql(limit, offset=None):
|
|
||||||
# TODO: This is a guess. Make sure this is correct.
|
|
||||||
sql = "LIMIT %s" % limit
|
|
||||||
if offset and offset != 0:
|
|
||||||
sql += " OFFSET %s" % offset
|
|
||||||
return sql
|
|
||||||
|
|
||||||
def get_random_function_sql():
|
|
||||||
return "RAND()"
|
|
||||||
|
|
||||||
def get_deferrable_sql():
|
|
||||||
return " DEFERRABLE INITIALLY DEFERRED"
|
|
||||||
|
|
||||||
def get_fulltext_search_sql(field_name):
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
def get_drop_foreignkey_sql():
|
|
||||||
return "DROP CONSTRAINT"
|
|
||||||
|
|
||||||
def get_pk_default_value():
|
|
||||||
return "DEFAULT"
|
|
||||||
|
|
||||||
def get_max_name_length():
|
|
||||||
return None
|
|
||||||
|
|
||||||
def get_start_transaction_sql():
|
|
||||||
return "BEGIN;"
|
|
||||||
|
|
||||||
def get_tablespace_sql(tablespace, inline=False):
|
|
||||||
return "ON %s" % quote_name(tablespace)
|
|
||||||
|
|
||||||
def get_autoinc_sql(table):
|
|
||||||
return None
|
|
||||||
|
|
||||||
def get_sql_flush(style, tables, sequences):
|
|
||||||
"""Return a list of SQL statements required to remove all data from
|
|
||||||
all tables in the database (without actually removing the tables
|
|
||||||
themselves) and put the database in an empty 'initial' state
|
|
||||||
"""
|
|
||||||
# Return a list of 'TRUNCATE x;', 'TRUNCATE y;', 'TRUNCATE z;'... style SQL statements
|
|
||||||
# TODO - SQL not actually tested against ADO MSSQL yet!
|
|
||||||
# TODO - autoincrement indices reset required? See other get_sql_flush() implementations
|
|
||||||
sql_list = ['%s %s;' % \
|
|
||||||
(style.SQL_KEYWORD('TRUNCATE'),
|
|
||||||
style.SQL_FIELD(quote_name(table))
|
|
||||||
) for table in tables]
|
|
||||||
|
|
||||||
def get_sql_sequence_reset(style, model_list):
|
|
||||||
"Returns a list of the SQL statements to reset sequences for the given models."
|
|
||||||
# No sequence reset required
|
|
||||||
return []
|
|
||||||
|
|
||||||
OPERATOR_MAPPING = {
|
|
||||||
'exact': '= %s',
|
'exact': '= %s',
|
||||||
'iexact': 'LIKE %s',
|
'iexact': 'LIKE %s',
|
||||||
'contains': 'LIKE %s',
|
'contains': 'LIKE %s',
|
||||||
@ -192,4 +97,16 @@ OPERATOR_MAPPING = {
|
|||||||
'endswith': 'LIKE %s',
|
'endswith': 'LIKE %s',
|
||||||
'istartswith': 'LIKE %s',
|
'istartswith': 'LIKE %s',
|
||||||
'iendswith': 'LIKE %s',
|
'iendswith': 'LIKE %s',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def _cursor(self, settings):
|
||||||
|
if self.connection is None:
|
||||||
|
if settings.DATABASE_NAME == '' or settings.DATABASE_USER == '':
|
||||||
|
from django.core.exceptions import ImproperlyConfigured
|
||||||
|
raise ImproperlyConfigured("You need to specify both DATABASE_NAME and DATABASE_USER in your Django settings file.")
|
||||||
|
if not settings.DATABASE_HOST:
|
||||||
|
settings.DATABASE_HOST = "127.0.0.1"
|
||||||
|
# TODO: Handle DATABASE_PORT.
|
||||||
|
conn_string = "PROVIDER=SQLOLEDB;DATA SOURCE=%s;UID=%s;PWD=%s;DATABASE=%s" % (settings.DATABASE_HOST, settings.DATABASE_USER, settings.DATABASE_PASSWORD, settings.DATABASE_NAME)
|
||||||
|
self.connection = Database.connect(conn_string)
|
||||||
|
return self.connection.cursor()
|
||||||
|
@ -21,7 +21,14 @@ class DatabaseError(Exception):
|
|||||||
class IntegrityError(DatabaseError):
|
class IntegrityError(DatabaseError):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
class DatabaseWrapper:
|
class ComplainOnGetattr(object):
|
||||||
|
def __getattr__(self, *args, **kwargs):
|
||||||
|
complain()
|
||||||
|
|
||||||
|
class DatabaseWrapper(object):
|
||||||
|
features = ComplainOnGetattr()
|
||||||
|
ops = ComplainOnGetattr()
|
||||||
|
operators = {}
|
||||||
cursor = complain
|
cursor = complain
|
||||||
_commit = complain
|
_commit = complain
|
||||||
_rollback = ignore
|
_rollback = ignore
|
||||||
@ -30,28 +37,4 @@ class DatabaseWrapper:
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
def close(self):
|
def close(self):
|
||||||
pass # close()
|
pass
|
||||||
|
|
||||||
supports_constraints = False
|
|
||||||
supports_tablespaces = False
|
|
||||||
quote_name = complain
|
|
||||||
dictfetchone = complain
|
|
||||||
dictfetchmany = complain
|
|
||||||
dictfetchall = complain
|
|
||||||
get_last_insert_id = complain
|
|
||||||
get_date_extract_sql = complain
|
|
||||||
get_date_trunc_sql = complain
|
|
||||||
get_datetime_cast_sql = complain
|
|
||||||
get_limit_offset_sql = complain
|
|
||||||
get_random_function_sql = complain
|
|
||||||
get_deferrable_sql = complain
|
|
||||||
get_fulltext_search_sql = complain
|
|
||||||
get_drop_foreignkey_sql = complain
|
|
||||||
get_pk_default_value = complain
|
|
||||||
get_max_name_length = ignore
|
|
||||||
get_start_transaction_sql = complain
|
|
||||||
get_autoinc_sql = complain
|
|
||||||
get_sql_flush = complain
|
|
||||||
get_sql_sequence_reset = complain
|
|
||||||
|
|
||||||
OPERATOR_MAPPING = {}
|
|
||||||
|
@ -4,12 +4,12 @@ MySQL database backend for Django.
|
|||||||
Requires MySQLdb: http://sourceforge.net/projects/mysql-python
|
Requires MySQLdb: http://sourceforge.net/projects/mysql-python
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from django.db.backends import util
|
from django.db.backends import BaseDatabaseWrapper, BaseDatabaseFeatures, BaseDatabaseOperations, util
|
||||||
try:
|
try:
|
||||||
import MySQLdb as Database
|
import MySQLdb as Database
|
||||||
except ImportError, e:
|
except ImportError, e:
|
||||||
from django.core.exceptions import ImproperlyConfigured
|
from django.core.exceptions import ImproperlyConfigured
|
||||||
raise ImproperlyConfigured, "Error loading MySQLdb module: %s" % e
|
raise ImproperlyConfigured("Error loading MySQLdb module: %s" % e)
|
||||||
|
|
||||||
# We want version (1, 2, 1, 'final', 2) or later. We can't just use
|
# We want version (1, 2, 1, 'final', 2) or later. We can't just use
|
||||||
# lexicographic ordering in this check because then (1, 2, 1, 'gamma')
|
# lexicographic ordering in this check because then (1, 2, 1, 'gamma')
|
||||||
@ -17,7 +17,7 @@ except ImportError, e:
|
|||||||
version = Database.version_info
|
version = Database.version_info
|
||||||
if (version < (1,2,1) or (version[:3] == (1, 2, 1) and
|
if (version < (1,2,1) or (version[:3] == (1, 2, 1) and
|
||||||
(len(version) < 5 or version[3] != 'final' or version[4] < 2))):
|
(len(version) < 5 or version[3] != 'final' or version[4] < 2))):
|
||||||
raise ImportError, "MySQLdb-1.2.1p2 or newer is required; you have %s" % Database.__version__
|
raise ImportError("MySQLdb-1.2.1p2 or newer is required; you have %s" % Database.__version__)
|
||||||
|
|
||||||
from MySQLdb.converters import conversions
|
from MySQLdb.converters import conversions
|
||||||
from MySQLdb.constants import FIELD_TYPE
|
from MySQLdb.constants import FIELD_TYPE
|
||||||
@ -53,19 +53,94 @@ server_version_re = re.compile(r'(\d{1,2})\.(\d{1,2})\.(\d{1,2})')
|
|||||||
# standard util.CursorDebugWrapper can be used. Also, using sql_mode
|
# standard util.CursorDebugWrapper can be used. Also, using sql_mode
|
||||||
# TRADITIONAL will automatically cause most warnings to be treated as errors.
|
# TRADITIONAL will automatically cause most warnings to be treated as errors.
|
||||||
|
|
||||||
try:
|
class DatabaseFeatures(BaseDatabaseFeatures):
|
||||||
# Only exists in Python 2.4+
|
autoindexes_primary_keys = False
|
||||||
from threading import local
|
|
||||||
except ImportError:
|
class DatabaseOperations(BaseDatabaseOperations):
|
||||||
# Import copy of _thread_local.py from Python 2.4
|
def date_extract_sql(self, lookup_type, field_name):
|
||||||
from django.utils._threading_local import local
|
# http://dev.mysql.com/doc/mysql/en/date-and-time-functions.html
|
||||||
|
return "EXTRACT(%s FROM %s)" % (lookup_type.upper(), field_name)
|
||||||
|
|
||||||
|
def date_trunc_sql(self, lookup_type, field_name):
|
||||||
|
fields = ['year', 'month', 'day', 'hour', 'minute', 'second']
|
||||||
|
format = ('%%Y-', '%%m', '-%%d', ' %%H:', '%%i', ':%%s') # Use double percents to escape.
|
||||||
|
format_def = ('0000-', '01', '-01', ' 00:', '00', ':00')
|
||||||
|
try:
|
||||||
|
i = fields.index(lookup_type) + 1
|
||||||
|
except ValueError:
|
||||||
|
sql = field_name
|
||||||
|
else:
|
||||||
|
format_str = ''.join([f for f in format[:i]] + [f for f in format_def[i:]])
|
||||||
|
sql = "CAST(DATE_FORMAT(%s, '%s') AS DATETIME)" % (field_name, format_str)
|
||||||
|
return sql
|
||||||
|
|
||||||
|
def drop_foreignkey_sql(self):
|
||||||
|
return "DROP FOREIGN KEY"
|
||||||
|
|
||||||
|
def fulltext_search_sql(self, field_name):
|
||||||
|
return 'MATCH (%s) AGAINST (%%s IN BOOLEAN MODE)' % field_name
|
||||||
|
|
||||||
|
def limit_offset_sql(self, limit, offset=None):
|
||||||
|
# 'LIMIT 20,40'
|
||||||
|
sql = "LIMIT "
|
||||||
|
if offset and offset != 0:
|
||||||
|
sql += "%s," % offset
|
||||||
|
return sql + str(limit)
|
||||||
|
|
||||||
|
def quote_name(self, name):
|
||||||
|
if name.startswith("`") and name.endswith("`"):
|
||||||
|
return name # Quoting once is enough.
|
||||||
|
return "`%s`" % name
|
||||||
|
|
||||||
|
def random_function_sql(self):
|
||||||
|
return 'RAND()'
|
||||||
|
|
||||||
|
def sql_flush(self, style, tables, sequences):
|
||||||
|
# NB: The generated SQL below is specific to MySQL
|
||||||
|
# 'TRUNCATE x;', 'TRUNCATE y;', 'TRUNCATE z;'... style SQL statements
|
||||||
|
# to clear all tables of all data
|
||||||
|
if tables:
|
||||||
|
sql = ['SET FOREIGN_KEY_CHECKS = 0;']
|
||||||
|
for table in tables:
|
||||||
|
sql.append('%s %s;' % (style.SQL_KEYWORD('TRUNCATE'), style.SQL_FIELD(self.quote_name(table))))
|
||||||
|
sql.append('SET FOREIGN_KEY_CHECKS = 1;')
|
||||||
|
|
||||||
|
# 'ALTER TABLE table AUTO_INCREMENT = 1;'... style SQL statements
|
||||||
|
# to reset sequence indices
|
||||||
|
sql.extend(["%s %s %s %s %s;" % \
|
||||||
|
(style.SQL_KEYWORD('ALTER'),
|
||||||
|
style.SQL_KEYWORD('TABLE'),
|
||||||
|
style.SQL_TABLE(self.quote_name(sequence['table'])),
|
||||||
|
style.SQL_KEYWORD('AUTO_INCREMENT'),
|
||||||
|
style.SQL_FIELD('= 1'),
|
||||||
|
) for sequence in sequences])
|
||||||
|
return sql
|
||||||
|
else:
|
||||||
|
return []
|
||||||
|
|
||||||
|
class DatabaseWrapper(BaseDatabaseWrapper):
|
||||||
|
features = DatabaseFeatures()
|
||||||
|
ops = DatabaseOperations()
|
||||||
|
operators = {
|
||||||
|
'exact': '= %s',
|
||||||
|
'iexact': 'LIKE %s',
|
||||||
|
'contains': 'LIKE BINARY %s',
|
||||||
|
'icontains': 'LIKE %s',
|
||||||
|
'regex': 'REGEXP BINARY %s',
|
||||||
|
'iregex': 'REGEXP %s',
|
||||||
|
'gt': '> %s',
|
||||||
|
'gte': '>= %s',
|
||||||
|
'lt': '< %s',
|
||||||
|
'lte': '<= %s',
|
||||||
|
'startswith': 'LIKE BINARY %s',
|
||||||
|
'endswith': 'LIKE BINARY %s',
|
||||||
|
'istartswith': 'LIKE %s',
|
||||||
|
'iendswith': 'LIKE %s',
|
||||||
|
}
|
||||||
|
|
||||||
class DatabaseWrapper(local):
|
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
self.connection = None
|
super(DatabaseWrapper, self).__init__(**kwargs)
|
||||||
self.queries = []
|
|
||||||
self.server_version = None
|
self.server_version = None
|
||||||
self.options = kwargs
|
|
||||||
|
|
||||||
def _valid_connection(self):
|
def _valid_connection(self):
|
||||||
if self.connection is not None:
|
if self.connection is not None:
|
||||||
@ -77,8 +152,7 @@ class DatabaseWrapper(local):
|
|||||||
self.connection = None
|
self.connection = None
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def cursor(self):
|
def _cursor(self, settings):
|
||||||
from django.conf import settings
|
|
||||||
from warnings import filterwarnings
|
from warnings import filterwarnings
|
||||||
if not self._valid_connection():
|
if not self._valid_connection():
|
||||||
kwargs = {
|
kwargs = {
|
||||||
@ -101,29 +175,16 @@ class DatabaseWrapper(local):
|
|||||||
kwargs.update(self.options)
|
kwargs.update(self.options)
|
||||||
self.connection = Database.connect(**kwargs)
|
self.connection = Database.connect(**kwargs)
|
||||||
cursor = self.connection.cursor()
|
cursor = self.connection.cursor()
|
||||||
else:
|
|
||||||
cursor = self.connection.cursor()
|
|
||||||
if settings.DEBUG:
|
if settings.DEBUG:
|
||||||
filterwarnings("error", category=Database.Warning)
|
filterwarnings("error", category=Database.Warning)
|
||||||
return util.CursorDebugWrapper(cursor, self)
|
|
||||||
return cursor
|
return cursor
|
||||||
|
|
||||||
def _commit(self):
|
|
||||||
if self.connection is not None:
|
|
||||||
self.connection.commit()
|
|
||||||
|
|
||||||
def _rollback(self):
|
def _rollback(self):
|
||||||
if self.connection is not None:
|
|
||||||
try:
|
try:
|
||||||
self.connection.rollback()
|
BaseDatabaseWrapper._rollback(self)
|
||||||
except Database.NotSupportedError:
|
except Database.NotSupportedError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def close(self):
|
|
||||||
if self.connection is not None:
|
|
||||||
self.connection.close()
|
|
||||||
self.connection = None
|
|
||||||
|
|
||||||
def get_server_version(self):
|
def get_server_version(self):
|
||||||
if not self.server_version:
|
if not self.server_version:
|
||||||
if not self._valid_connection():
|
if not self._valid_connection():
|
||||||
@ -133,128 +194,3 @@ class DatabaseWrapper(local):
|
|||||||
raise Exception('Unable to determine MySQL version from version string %r' % self.connection.get_server_info())
|
raise Exception('Unable to determine MySQL version from version string %r' % self.connection.get_server_info())
|
||||||
self.server_version = tuple([int(x) for x in m.groups()])
|
self.server_version = tuple([int(x) for x in m.groups()])
|
||||||
return self.server_version
|
return self.server_version
|
||||||
|
|
||||||
allows_group_by_ordinal = True
|
|
||||||
allows_unique_and_pk = True
|
|
||||||
autoindexes_primary_keys = False
|
|
||||||
needs_datetime_string_cast = True # MySQLdb requires a typecast for dates
|
|
||||||
needs_upper_for_iops = False
|
|
||||||
supports_constraints = True
|
|
||||||
supports_tablespaces = False
|
|
||||||
uses_case_insensitive_names = False
|
|
||||||
|
|
||||||
def quote_name(name):
|
|
||||||
if name.startswith("`") and name.endswith("`"):
|
|
||||||
return name # Quoting once is enough.
|
|
||||||
return "`%s`" % name
|
|
||||||
|
|
||||||
dictfetchone = util.dictfetchone
|
|
||||||
dictfetchmany = util.dictfetchmany
|
|
||||||
dictfetchall = util.dictfetchall
|
|
||||||
|
|
||||||
def get_last_insert_id(cursor, table_name, pk_name):
|
|
||||||
return cursor.lastrowid
|
|
||||||
|
|
||||||
def get_date_extract_sql(lookup_type, table_name):
|
|
||||||
# lookup_type is 'year', 'month', 'day'
|
|
||||||
# http://dev.mysql.com/doc/mysql/en/date-and-time-functions.html
|
|
||||||
return "EXTRACT(%s FROM %s)" % (lookup_type.upper(), table_name)
|
|
||||||
|
|
||||||
def get_date_trunc_sql(lookup_type, field_name):
|
|
||||||
# lookup_type is 'year', 'month', 'day'
|
|
||||||
fields = ['year', 'month', 'day', 'hour', 'minute', 'second']
|
|
||||||
format = ('%%Y-', '%%m', '-%%d', ' %%H:', '%%i', ':%%s') # Use double percents to escape.
|
|
||||||
format_def = ('0000-', '01', '-01', ' 00:', '00', ':00')
|
|
||||||
try:
|
|
||||||
i = fields.index(lookup_type) + 1
|
|
||||||
except ValueError:
|
|
||||||
sql = field_name
|
|
||||||
else:
|
|
||||||
format_str = ''.join([f for f in format[:i]] + [f for f in format_def[i:]])
|
|
||||||
sql = "CAST(DATE_FORMAT(%s, '%s') AS DATETIME)" % (field_name, format_str)
|
|
||||||
return sql
|
|
||||||
|
|
||||||
def get_datetime_cast_sql():
|
|
||||||
return None
|
|
||||||
|
|
||||||
def get_limit_offset_sql(limit, offset=None):
|
|
||||||
sql = "LIMIT "
|
|
||||||
if offset and offset != 0:
|
|
||||||
sql += "%s," % offset
|
|
||||||
return sql + str(limit)
|
|
||||||
|
|
||||||
def get_random_function_sql():
|
|
||||||
return "RAND()"
|
|
||||||
|
|
||||||
def get_deferrable_sql():
|
|
||||||
return ""
|
|
||||||
|
|
||||||
def get_fulltext_search_sql(field_name):
|
|
||||||
return 'MATCH (%s) AGAINST (%%s IN BOOLEAN MODE)' % field_name
|
|
||||||
|
|
||||||
def get_drop_foreignkey_sql():
|
|
||||||
return "DROP FOREIGN KEY"
|
|
||||||
|
|
||||||
def get_pk_default_value():
|
|
||||||
return "DEFAULT"
|
|
||||||
|
|
||||||
def get_max_name_length():
|
|
||||||
return None;
|
|
||||||
|
|
||||||
def get_start_transaction_sql():
|
|
||||||
return "BEGIN;"
|
|
||||||
|
|
||||||
def get_autoinc_sql(table):
|
|
||||||
return None
|
|
||||||
|
|
||||||
def get_sql_flush(style, tables, sequences):
|
|
||||||
"""Return a list of SQL statements required to remove all data from
|
|
||||||
all tables in the database (without actually removing the tables
|
|
||||||
themselves) and put the database in an empty 'initial' state
|
|
||||||
|
|
||||||
"""
|
|
||||||
# NB: The generated SQL below is specific to MySQL
|
|
||||||
# 'TRUNCATE x;', 'TRUNCATE y;', 'TRUNCATE z;'... style SQL statements
|
|
||||||
# to clear all tables of all data
|
|
||||||
if tables:
|
|
||||||
sql = ['SET FOREIGN_KEY_CHECKS = 0;'] + \
|
|
||||||
['%s %s;' % \
|
|
||||||
(style.SQL_KEYWORD('TRUNCATE'),
|
|
||||||
style.SQL_FIELD(quote_name(table))
|
|
||||||
) for table in tables] + \
|
|
||||||
['SET FOREIGN_KEY_CHECKS = 1;']
|
|
||||||
|
|
||||||
# 'ALTER TABLE table AUTO_INCREMENT = 1;'... style SQL statements
|
|
||||||
# to reset sequence indices
|
|
||||||
sql.extend(["%s %s %s %s %s;" % \
|
|
||||||
(style.SQL_KEYWORD('ALTER'),
|
|
||||||
style.SQL_KEYWORD('TABLE'),
|
|
||||||
style.SQL_TABLE(quote_name(sequence['table'])),
|
|
||||||
style.SQL_KEYWORD('AUTO_INCREMENT'),
|
|
||||||
style.SQL_FIELD('= 1'),
|
|
||||||
) for sequence in sequences])
|
|
||||||
return sql
|
|
||||||
else:
|
|
||||||
return []
|
|
||||||
|
|
||||||
def get_sql_sequence_reset(style, model_list):
|
|
||||||
"Returns a list of the SQL statements to reset sequences for the given models."
|
|
||||||
# No sequence reset required
|
|
||||||
return []
|
|
||||||
|
|
||||||
OPERATOR_MAPPING = {
|
|
||||||
'exact': '= %s',
|
|
||||||
'iexact': 'LIKE %s',
|
|
||||||
'contains': 'LIKE BINARY %s',
|
|
||||||
'icontains': 'LIKE %s',
|
|
||||||
'regex': 'REGEXP BINARY %s',
|
|
||||||
'iregex': 'REGEXP %s',
|
|
||||||
'gt': '> %s',
|
|
||||||
'gte': '>= %s',
|
|
||||||
'lt': '< %s',
|
|
||||||
'lte': '<= %s',
|
|
||||||
'startswith': 'LIKE BINARY %s',
|
|
||||||
'endswith': 'LIKE BINARY %s',
|
|
||||||
'istartswith': 'LIKE %s',
|
|
||||||
'iendswith': 'LIKE %s',
|
|
||||||
}
|
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
from django.db.backends.mysql.base import quote_name
|
from django.db.backends.mysql.base import DatabaseOperations
|
||||||
from MySQLdb import ProgrammingError, OperationalError
|
from MySQLdb import ProgrammingError, OperationalError
|
||||||
from MySQLdb.constants import FIELD_TYPE
|
from MySQLdb.constants import FIELD_TYPE
|
||||||
import re
|
import re
|
||||||
|
|
||||||
|
quote_name = DatabaseOperations().quote_name
|
||||||
foreign_key_re = re.compile(r"\sCONSTRAINT `[^`]*` FOREIGN KEY \(`([^`]*)`\) REFERENCES `([^`]*)` \(`([^`]*)`\)")
|
foreign_key_re = re.compile(r"\sCONSTRAINT `[^`]*` FOREIGN KEY \(`([^`]*)`\) REFERENCES `([^`]*)` \(`([^`]*)`\)")
|
||||||
|
|
||||||
def get_table_list(cursor):
|
def get_table_list(cursor):
|
||||||
|
@ -4,13 +4,13 @@ MySQL database backend for Django.
|
|||||||
Requires MySQLdb: http://sourceforge.net/projects/mysql-python
|
Requires MySQLdb: http://sourceforge.net/projects/mysql-python
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from django.db.backends import util
|
from django.db.backends import BaseDatabaseWrapper, BaseDatabaseFeatures, BaseDatabaseOperations, util
|
||||||
from django.utils.encoding import force_unicode
|
from django.utils.encoding import force_unicode
|
||||||
try:
|
try:
|
||||||
import MySQLdb as Database
|
import MySQLdb as Database
|
||||||
except ImportError, e:
|
except ImportError, e:
|
||||||
from django.core.exceptions import ImproperlyConfigured
|
from django.core.exceptions import ImproperlyConfigured
|
||||||
raise ImproperlyConfigured, "Error loading MySQLdb module: %s" % e
|
raise ImproperlyConfigured("Error loading MySQLdb module: %s" % e)
|
||||||
from MySQLdb.converters import conversions
|
from MySQLdb.converters import conversions
|
||||||
from MySQLdb.constants import FIELD_TYPE
|
from MySQLdb.constants import FIELD_TYPE
|
||||||
import types
|
import types
|
||||||
@ -48,14 +48,14 @@ class MysqlDebugWrapper:
|
|||||||
return self.cursor.execute(sql, params)
|
return self.cursor.execute(sql, params)
|
||||||
except Database.Warning, w:
|
except Database.Warning, w:
|
||||||
self.cursor.execute("SHOW WARNINGS")
|
self.cursor.execute("SHOW WARNINGS")
|
||||||
raise Database.Warning, "%s: %s" % (w, self.cursor.fetchall())
|
raise Database.Warning("%s: %s" % (w, self.cursor.fetchall()))
|
||||||
|
|
||||||
def executemany(self, sql, param_list):
|
def executemany(self, sql, param_list):
|
||||||
try:
|
try:
|
||||||
return self.cursor.executemany(sql, param_list)
|
return self.cursor.executemany(sql, param_list)
|
||||||
except Database.Warning, w:
|
except Database.Warning, w:
|
||||||
self.cursor.execute("SHOW WARNINGS")
|
self.cursor.execute("SHOW WARNINGS")
|
||||||
raise Database.Warning, "%s: %s" % (w, self.cursor.fetchall())
|
raise Database.Warning("%s: %s" % (w, self.cursor.fetchall()))
|
||||||
|
|
||||||
def __getattr__(self, attr):
|
def __getattr__(self, attr):
|
||||||
if attr in self.__dict__:
|
if attr in self.__dict__:
|
||||||
@ -63,19 +63,94 @@ class MysqlDebugWrapper:
|
|||||||
else:
|
else:
|
||||||
return getattr(self.cursor, attr)
|
return getattr(self.cursor, attr)
|
||||||
|
|
||||||
try:
|
class DatabaseFeatures(BaseDatabaseFeatures):
|
||||||
# Only exists in Python 2.4+
|
autoindexes_primary_keys = False
|
||||||
from threading import local
|
|
||||||
except ImportError:
|
class DatabaseOperations(BaseDatabaseOperations):
|
||||||
# Import copy of _thread_local.py from Python 2.4
|
def date_extract_sql(self, lookup_type, field_name):
|
||||||
from django.utils._threading_local import local
|
# http://dev.mysql.com/doc/mysql/en/date-and-time-functions.html
|
||||||
|
return "EXTRACT(%s FROM %s)" % (lookup_type.upper(), field_name)
|
||||||
|
|
||||||
|
def date_trunc_sql(self, lookup_type, field_name):
|
||||||
|
fields = ['year', 'month', 'day', 'hour', 'minute', 'second']
|
||||||
|
format = ('%%Y-', '%%m', '-%%d', ' %%H:', '%%i', ':%%s') # Use double percents to escape.
|
||||||
|
format_def = ('0000-', '01', '-01', ' 00:', '00', ':00')
|
||||||
|
try:
|
||||||
|
i = fields.index(lookup_type) + 1
|
||||||
|
except ValueError:
|
||||||
|
sql = field_name
|
||||||
|
else:
|
||||||
|
format_str = ''.join([f for f in format[:i]] + [f for f in format_def[i:]])
|
||||||
|
sql = "CAST(DATE_FORMAT(%s, '%s') AS DATETIME)" % (field_name, format_str)
|
||||||
|
return sql
|
||||||
|
|
||||||
|
def drop_foreignkey_sql(self):
|
||||||
|
return "DROP FOREIGN KEY"
|
||||||
|
|
||||||
|
def fulltext_search_sql(self, field_name):
|
||||||
|
return 'MATCH (%s) AGAINST (%%s IN BOOLEAN MODE)' % field_name
|
||||||
|
|
||||||
|
def limit_offset_sql(self, limit, offset=None):
|
||||||
|
# 'LIMIT 20,40'
|
||||||
|
sql = "LIMIT "
|
||||||
|
if offset and offset != 0:
|
||||||
|
sql += "%s," % offset
|
||||||
|
return sql + str(limit)
|
||||||
|
|
||||||
|
def quote_name(self, name):
|
||||||
|
if name.startswith("`") and name.endswith("`"):
|
||||||
|
return name # Quoting once is enough.
|
||||||
|
return "`%s`" % name
|
||||||
|
|
||||||
|
def random_function_sql(self):
|
||||||
|
return 'RAND()'
|
||||||
|
|
||||||
|
def sql_flush(self, style, tables, sequences):
|
||||||
|
# NB: The generated SQL below is specific to MySQL
|
||||||
|
# 'TRUNCATE x;', 'TRUNCATE y;', 'TRUNCATE z;'... style SQL statements
|
||||||
|
# to clear all tables of all data
|
||||||
|
if tables:
|
||||||
|
sql = ['SET FOREIGN_KEY_CHECKS = 0;']
|
||||||
|
for table in tables:
|
||||||
|
sql.append('%s %s;' % (style.SQL_KEYWORD('TRUNCATE'), style.SQL_FIELD(self.quote_name(table))))
|
||||||
|
sql.append('SET FOREIGN_KEY_CHECKS = 1;')
|
||||||
|
|
||||||
|
# 'ALTER TABLE table AUTO_INCREMENT = 1;'... style SQL statements
|
||||||
|
# to reset sequence indices
|
||||||
|
sql.extend(["%s %s %s %s %s;" % \
|
||||||
|
(style.SQL_KEYWORD('ALTER'),
|
||||||
|
style.SQL_KEYWORD('TABLE'),
|
||||||
|
style.SQL_TABLE(self.quote_name(sequence['table'])),
|
||||||
|
style.SQL_KEYWORD('AUTO_INCREMENT'),
|
||||||
|
style.SQL_FIELD('= 1'),
|
||||||
|
) for sequence in sequences])
|
||||||
|
return sql
|
||||||
|
else:
|
||||||
|
return []
|
||||||
|
|
||||||
|
class DatabaseWrapper(BaseDatabaseWrapper):
|
||||||
|
features = DatabaseFeatures()
|
||||||
|
ops = DatabaseOperations()
|
||||||
|
operators = {
|
||||||
|
'exact': '= %s',
|
||||||
|
'iexact': 'LIKE %s',
|
||||||
|
'contains': 'LIKE BINARY %s',
|
||||||
|
'icontains': 'LIKE %s',
|
||||||
|
'regex': 'REGEXP BINARY %s',
|
||||||
|
'iregex': 'REGEXP %s',
|
||||||
|
'gt': '> %s',
|
||||||
|
'gte': '>= %s',
|
||||||
|
'lt': '< %s',
|
||||||
|
'lte': '<= %s',
|
||||||
|
'startswith': 'LIKE BINARY %s',
|
||||||
|
'endswith': 'LIKE BINARY %s',
|
||||||
|
'istartswith': 'LIKE %s',
|
||||||
|
'iendswith': 'LIKE %s',
|
||||||
|
}
|
||||||
|
|
||||||
class DatabaseWrapper(local):
|
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
self.connection = None
|
super(DatabaseWrapper, self).__init__(**kwargs)
|
||||||
self.queries = []
|
|
||||||
self.server_version = None
|
self.server_version = None
|
||||||
self.options = kwargs
|
|
||||||
|
|
||||||
def _valid_connection(self):
|
def _valid_connection(self):
|
||||||
if self.connection is not None:
|
if self.connection is not None:
|
||||||
@ -87,8 +162,7 @@ class DatabaseWrapper(local):
|
|||||||
self.connection = None
|
self.connection = None
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def cursor(self):
|
def _cursor(self, settings):
|
||||||
from django.conf import settings
|
|
||||||
if not self._valid_connection():
|
if not self._valid_connection():
|
||||||
kwargs = {
|
kwargs = {
|
||||||
# Note: use_unicode intentonally not set to work around some
|
# Note: use_unicode intentonally not set to work around some
|
||||||
@ -119,26 +193,17 @@ class DatabaseWrapper(local):
|
|||||||
self.connection.set_character_set('utf8')
|
self.connection.set_character_set('utf8')
|
||||||
else:
|
else:
|
||||||
cursor = self.connection.cursor()
|
cursor = self.connection.cursor()
|
||||||
if settings.DEBUG:
|
|
||||||
return util.CursorDebugWrapper(MysqlDebugWrapper(cursor), self)
|
|
||||||
return cursor
|
return cursor
|
||||||
|
|
||||||
def _commit(self):
|
def make_debug_cursor(self, cursor):
|
||||||
if self.connection is not None:
|
return BaseDatabaseWrapper.make_debug_cursor(self, MysqlDebugWrapper(cursor))
|
||||||
self.connection.commit()
|
|
||||||
|
|
||||||
def _rollback(self):
|
def _rollback(self):
|
||||||
if self.connection is not None:
|
|
||||||
try:
|
try:
|
||||||
self.connection.rollback()
|
BaseDatabaseWrapper._rollback(self)
|
||||||
except Database.NotSupportedError:
|
except Database.NotSupportedError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def close(self):
|
|
||||||
if self.connection is not None:
|
|
||||||
self.connection.close()
|
|
||||||
self.connection = None
|
|
||||||
|
|
||||||
def get_server_version(self):
|
def get_server_version(self):
|
||||||
if not self.server_version:
|
if not self.server_version:
|
||||||
if not self._valid_connection():
|
if not self._valid_connection():
|
||||||
@ -148,128 +213,3 @@ class DatabaseWrapper(local):
|
|||||||
raise Exception('Unable to determine MySQL version from version string %r' % self.connection.get_server_info())
|
raise Exception('Unable to determine MySQL version from version string %r' % self.connection.get_server_info())
|
||||||
self.server_version = tuple([int(x) for x in m.groups()])
|
self.server_version = tuple([int(x) for x in m.groups()])
|
||||||
return self.server_version
|
return self.server_version
|
||||||
|
|
||||||
allows_group_by_ordinal = True
|
|
||||||
allows_unique_and_pk = True
|
|
||||||
autoindexes_primary_keys = False
|
|
||||||
needs_datetime_string_cast = True # MySQLdb requires a typecast for dates
|
|
||||||
needs_upper_for_iops = False
|
|
||||||
supports_constraints = True
|
|
||||||
supports_tablespaces = False
|
|
||||||
uses_case_insensitive_names = False
|
|
||||||
|
|
||||||
def quote_name(name):
|
|
||||||
if name.startswith("`") and name.endswith("`"):
|
|
||||||
return name # Quoting once is enough.
|
|
||||||
return "`%s`" % name
|
|
||||||
|
|
||||||
dictfetchone = util.dictfetchone
|
|
||||||
dictfetchmany = util.dictfetchmany
|
|
||||||
dictfetchall = util.dictfetchall
|
|
||||||
|
|
||||||
def get_last_insert_id(cursor, table_name, pk_name):
|
|
||||||
return cursor.lastrowid
|
|
||||||
|
|
||||||
def get_date_extract_sql(lookup_type, table_name):
|
|
||||||
# lookup_type is 'year', 'month', 'day'
|
|
||||||
# http://dev.mysql.com/doc/mysql/en/date-and-time-functions.html
|
|
||||||
return "EXTRACT(%s FROM %s)" % (lookup_type.upper(), table_name)
|
|
||||||
|
|
||||||
def get_date_trunc_sql(lookup_type, field_name):
|
|
||||||
# lookup_type is 'year', 'month', 'day'
|
|
||||||
fields = ['year', 'month', 'day', 'hour', 'minute', 'second']
|
|
||||||
format = ('%%Y-', '%%m', '-%%d', ' %%H:', '%%i', ':%%s') # Use double percents to escape.
|
|
||||||
format_def = ('0000-', '01', '-01', ' 00:', '00', ':00')
|
|
||||||
try:
|
|
||||||
i = fields.index(lookup_type) + 1
|
|
||||||
except ValueError:
|
|
||||||
sql = field_name
|
|
||||||
else:
|
|
||||||
format_str = ''.join([f for f in format[:i]] + [f for f in format_def[i:]])
|
|
||||||
sql = "CAST(DATE_FORMAT(%s, '%s') AS DATETIME)" % (field_name, format_str)
|
|
||||||
return sql
|
|
||||||
|
|
||||||
def get_datetime_cast_sql():
|
|
||||||
return None
|
|
||||||
|
|
||||||
def get_limit_offset_sql(limit, offset=None):
|
|
||||||
sql = "LIMIT "
|
|
||||||
if offset and offset != 0:
|
|
||||||
sql += "%s," % offset
|
|
||||||
return sql + str(limit)
|
|
||||||
|
|
||||||
def get_random_function_sql():
|
|
||||||
return "RAND()"
|
|
||||||
|
|
||||||
def get_deferrable_sql():
|
|
||||||
return ""
|
|
||||||
|
|
||||||
def get_fulltext_search_sql(field_name):
|
|
||||||
return 'MATCH (%s) AGAINST (%%s IN BOOLEAN MODE)' % field_name
|
|
||||||
|
|
||||||
def get_drop_foreignkey_sql():
|
|
||||||
return "DROP FOREIGN KEY"
|
|
||||||
|
|
||||||
def get_pk_default_value():
|
|
||||||
return "DEFAULT"
|
|
||||||
|
|
||||||
def get_max_name_length():
|
|
||||||
return None;
|
|
||||||
|
|
||||||
def get_start_transaction_sql():
|
|
||||||
return "BEGIN;"
|
|
||||||
|
|
||||||
def get_autoinc_sql(table):
|
|
||||||
return None
|
|
||||||
|
|
||||||
def get_sql_flush(style, tables, sequences):
|
|
||||||
"""Return a list of SQL statements required to remove all data from
|
|
||||||
all tables in the database (without actually removing the tables
|
|
||||||
themselves) and put the database in an empty 'initial' state
|
|
||||||
|
|
||||||
"""
|
|
||||||
# NB: The generated SQL below is specific to MySQL
|
|
||||||
# 'TRUNCATE x;', 'TRUNCATE y;', 'TRUNCATE z;'... style SQL statements
|
|
||||||
# to clear all tables of all data
|
|
||||||
if tables:
|
|
||||||
sql = ['SET FOREIGN_KEY_CHECKS = 0;'] + \
|
|
||||||
['%s %s;' % \
|
|
||||||
(style.SQL_KEYWORD('TRUNCATE'),
|
|
||||||
style.SQL_FIELD(quote_name(table))
|
|
||||||
) for table in tables] + \
|
|
||||||
['SET FOREIGN_KEY_CHECKS = 1;']
|
|
||||||
|
|
||||||
# 'ALTER TABLE table AUTO_INCREMENT = 1;'... style SQL statements
|
|
||||||
# to reset sequence indices
|
|
||||||
sql.extend(["%s %s %s %s %s;" % \
|
|
||||||
(style.SQL_KEYWORD('ALTER'),
|
|
||||||
style.SQL_KEYWORD('TABLE'),
|
|
||||||
style.SQL_TABLE(quote_name(sequence['table'])),
|
|
||||||
style.SQL_KEYWORD('AUTO_INCREMENT'),
|
|
||||||
style.SQL_FIELD('= 1'),
|
|
||||||
) for sequence in sequences])
|
|
||||||
return sql
|
|
||||||
else:
|
|
||||||
return []
|
|
||||||
|
|
||||||
def get_sql_sequence_reset(style, model_list):
|
|
||||||
"Returns a list of the SQL statements to reset sequences for the given models."
|
|
||||||
# No sequence reset required
|
|
||||||
return []
|
|
||||||
|
|
||||||
OPERATOR_MAPPING = {
|
|
||||||
'exact': '= %s',
|
|
||||||
'iexact': 'LIKE %s',
|
|
||||||
'contains': 'LIKE BINARY %s',
|
|
||||||
'icontains': 'LIKE %s',
|
|
||||||
'regex': 'REGEXP BINARY %s',
|
|
||||||
'iregex': 'REGEXP %s',
|
|
||||||
'gt': '> %s',
|
|
||||||
'gte': '>= %s',
|
|
||||||
'lt': '< %s',
|
|
||||||
'lte': '<= %s',
|
|
||||||
'startswith': 'LIKE BINARY %s',
|
|
||||||
'endswith': 'LIKE BINARY %s',
|
|
||||||
'istartswith': 'LIKE %s',
|
|
||||||
'iendswith': 'LIKE %s',
|
|
||||||
}
|
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
from django.db.backends.mysql_old.base import quote_name
|
from django.db.backends.mysql_old.base import DatabaseOperations
|
||||||
from MySQLdb import ProgrammingError, OperationalError
|
from MySQLdb import ProgrammingError, OperationalError
|
||||||
from MySQLdb.constants import FIELD_TYPE
|
from MySQLdb.constants import FIELD_TYPE
|
||||||
import re
|
import re
|
||||||
|
|
||||||
|
quote_name = DatabaseOperations().quote_name
|
||||||
foreign_key_re = re.compile(r"\sCONSTRAINT `[^`]*` FOREIGN KEY \(`([^`]*)`\) REFERENCES `([^`]*)` \(`([^`]*)`\)")
|
foreign_key_re = re.compile(r"\sCONSTRAINT `[^`]*` FOREIGN KEY \(`([^`]*)`\) REFERENCES `([^`]*)` \(`([^`]*)`\)")
|
||||||
|
|
||||||
def get_table_list(cursor):
|
def get_table_list(cursor):
|
||||||
|
@ -4,8 +4,7 @@ Oracle database backend for Django.
|
|||||||
Requires cx_Oracle: http://www.python.net/crew/atuining/cx_Oracle/
|
Requires cx_Oracle: http://www.python.net/crew/atuining/cx_Oracle/
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from django.conf import settings
|
from django.db.backends import BaseDatabaseWrapper, BaseDatabaseFeatures, BaseDatabaseOperations, util
|
||||||
from django.db.backends import util
|
|
||||||
from django.utils.datastructures import SortedDict
|
from django.utils.datastructures import SortedDict
|
||||||
from django.utils.encoding import smart_str, force_unicode
|
from django.utils.encoding import smart_str, force_unicode
|
||||||
import datetime
|
import datetime
|
||||||
@ -17,29 +16,392 @@ try:
|
|||||||
import cx_Oracle as Database
|
import cx_Oracle as Database
|
||||||
except ImportError, e:
|
except ImportError, e:
|
||||||
from django.core.exceptions import ImproperlyConfigured
|
from django.core.exceptions import ImproperlyConfigured
|
||||||
raise ImproperlyConfigured, "Error loading cx_Oracle module: %s" % e
|
raise ImproperlyConfigured("Error loading cx_Oracle module: %s" % e)
|
||||||
|
|
||||||
|
|
||||||
DatabaseError = Database.Error
|
DatabaseError = Database.Error
|
||||||
IntegrityError = Database.IntegrityError
|
IntegrityError = Database.IntegrityError
|
||||||
|
|
||||||
try:
|
class DatabaseFeatures(BaseDatabaseFeatures):
|
||||||
# Only exists in Python 2.4+
|
allows_group_by_ordinal = False
|
||||||
from threading import local
|
allows_unique_and_pk = False # Suppress UNIQUE/PK for Oracle (ORA-02259)
|
||||||
except ImportError:
|
needs_datetime_string_cast = False
|
||||||
# Import copy of _thread_local.py from Python 2.4
|
needs_upper_for_iops = True
|
||||||
from django.utils._threading_local import local
|
supports_tablespaces = True
|
||||||
|
uses_case_insensitive_names = True
|
||||||
|
uses_custom_queryset = True
|
||||||
|
|
||||||
class DatabaseWrapper(local):
|
class DatabaseOperations(BaseDatabaseOperations):
|
||||||
def __init__(self, **kwargs):
|
def autoinc_sql(self, table):
|
||||||
self.connection = None
|
# To simulate auto-incrementing primary keys in Oracle, we have to
|
||||||
self.queries = []
|
# create a sequence and a trigger.
|
||||||
self.options = kwargs
|
sq_name = get_sequence_name(table)
|
||||||
|
tr_name = get_trigger_name(table)
|
||||||
|
sequence_sql = 'CREATE SEQUENCE %s;' % sq_name
|
||||||
|
trigger_sql = """
|
||||||
|
CREATE OR REPLACE TRIGGER %s
|
||||||
|
BEFORE INSERT ON %s
|
||||||
|
FOR EACH ROW
|
||||||
|
WHEN (new.id IS NULL)
|
||||||
|
BEGIN
|
||||||
|
SELECT %s.nextval INTO :new.id FROM dual;
|
||||||
|
END;/""" % (tr_name, self.quote_name(table), sq_name)
|
||||||
|
return sequence_sql, trigger_sql
|
||||||
|
|
||||||
|
def date_extract_sql(self, lookup_type, field_name):
|
||||||
|
# http://download-east.oracle.com/docs/cd/B10501_01/server.920/a96540/functions42a.htm#1017163
|
||||||
|
return "EXTRACT(%s FROM %s)" % (lookup_type, field_name)
|
||||||
|
|
||||||
|
def date_trunc_sql(self, lookup_type, field_name):
|
||||||
|
# Oracle uses TRUNC() for both dates and numbers.
|
||||||
|
# http://download-east.oracle.com/docs/cd/B10501_01/server.920/a96540/functions155a.htm#SQLRF06151
|
||||||
|
if lookup_type == 'day':
|
||||||
|
sql = 'TRUNC(%s)' % field_name
|
||||||
|
else:
|
||||||
|
sql = "TRUNC(%s, '%s')" % (field_name, lookup_type)
|
||||||
|
return sql
|
||||||
|
|
||||||
|
def datetime_cast_sql(self):
|
||||||
|
return "TO_TIMESTAMP(%s, 'YYYY-MM-DD HH24:MI:SS.FF')"
|
||||||
|
|
||||||
|
def deferrable_sql(self):
|
||||||
|
return " DEFERRABLE INITIALLY DEFERRED"
|
||||||
|
|
||||||
|
def drop_sequence_sql(self, table):
|
||||||
|
return "DROP SEQUENCE %s;" % self.quote_name(get_sequence_name(table))
|
||||||
|
|
||||||
|
def field_cast_sql(self, db_type):
|
||||||
|
if db_type.endswith('LOB'):
|
||||||
|
return "DBMS_LOB.SUBSTR(%s)"
|
||||||
|
else:
|
||||||
|
return "%s"
|
||||||
|
|
||||||
|
def last_insert_id(self, cursor, table_name, pk_name):
|
||||||
|
sq_name = util.truncate_name(table_name, self.max_name_length() - 3)
|
||||||
|
cursor.execute('SELECT %s_sq.currval FROM dual' % sq_name)
|
||||||
|
return cursor.fetchone()[0]
|
||||||
|
|
||||||
|
def limit_offset_sql(self, limit, offset=None):
|
||||||
|
# Limits and offset are too complicated to be handled here.
|
||||||
|
# Instead, they are handled in django/db/backends/oracle/query.py.
|
||||||
|
return ""
|
||||||
|
|
||||||
|
def max_name_length(self):
|
||||||
|
return 30
|
||||||
|
|
||||||
|
def query_set_class(self, DefaultQuerySet):
|
||||||
|
from django.db import connection
|
||||||
|
from django.db.models.query import EmptyResultSet, GET_ITERATOR_CHUNK_SIZE, quote_only_if_word
|
||||||
|
|
||||||
|
class OracleQuerySet(DefaultQuerySet):
|
||||||
|
|
||||||
|
def iterator(self):
|
||||||
|
"Performs the SELECT database lookup of this QuerySet."
|
||||||
|
|
||||||
|
from django.db.models.query import get_cached_row
|
||||||
|
|
||||||
|
# self._select is a dictionary, and dictionaries' key order is
|
||||||
|
# undefined, so we convert it to a list of tuples.
|
||||||
|
extra_select = self._select.items()
|
||||||
|
|
||||||
|
full_query = None
|
||||||
|
|
||||||
|
try:
|
||||||
|
try:
|
||||||
|
select, sql, params, full_query = self._get_sql_clause(get_full_query=True)
|
||||||
|
except TypeError:
|
||||||
|
select, sql, params = self._get_sql_clause()
|
||||||
|
except EmptyResultSet:
|
||||||
|
raise StopIteration
|
||||||
|
if not full_query:
|
||||||
|
full_query = "SELECT %s%s\n%s" % ((self._distinct and "DISTINCT " or ""), ', '.join(select), sql)
|
||||||
|
|
||||||
|
cursor = connection.cursor()
|
||||||
|
cursor.execute(full_query, params)
|
||||||
|
|
||||||
|
fill_cache = self._select_related
|
||||||
|
fields = self.model._meta.fields
|
||||||
|
index_end = len(fields)
|
||||||
|
|
||||||
|
# so here's the logic;
|
||||||
|
# 1. retrieve each row in turn
|
||||||
|
# 2. convert NCLOBs
|
||||||
|
|
||||||
|
while 1:
|
||||||
|
rows = cursor.fetchmany(GET_ITERATOR_CHUNK_SIZE)
|
||||||
|
if not rows:
|
||||||
|
raise StopIteration
|
||||||
|
for row in rows:
|
||||||
|
row = self.resolve_columns(row, fields)
|
||||||
|
if fill_cache:
|
||||||
|
obj, index_end = get_cached_row(klass=self.model, row=row,
|
||||||
|
index_start=0, max_depth=self._max_related_depth)
|
||||||
|
else:
|
||||||
|
obj = self.model(*row[:index_end])
|
||||||
|
for i, k in enumerate(extra_select):
|
||||||
|
setattr(obj, k[0], row[index_end+i])
|
||||||
|
yield obj
|
||||||
|
|
||||||
|
|
||||||
|
def _get_sql_clause(self, get_full_query=False):
|
||||||
|
from django.db.models.query import fill_table_cache, \
|
||||||
|
handle_legacy_orderlist, orderfield2column
|
||||||
|
|
||||||
|
opts = self.model._meta
|
||||||
|
qn = connection.ops.quote_name
|
||||||
|
|
||||||
|
# Construct the fundamental parts of the query: SELECT X FROM Y WHERE Z.
|
||||||
|
select = ["%s.%s" % (qn(opts.db_table), qn(f.column)) for f in opts.fields]
|
||||||
|
tables = [quote_only_if_word(t) for t in self._tables]
|
||||||
|
joins = SortedDict()
|
||||||
|
where = self._where[:]
|
||||||
|
params = self._params[:]
|
||||||
|
|
||||||
|
# Convert self._filters into SQL.
|
||||||
|
joins2, where2, params2 = self._filters.get_sql(opts)
|
||||||
|
joins.update(joins2)
|
||||||
|
where.extend(where2)
|
||||||
|
params.extend(params2)
|
||||||
|
|
||||||
|
# Add additional tables and WHERE clauses based on select_related.
|
||||||
|
if self._select_related:
|
||||||
|
fill_table_cache(opts, select, tables, where, opts.db_table, [opts.db_table])
|
||||||
|
|
||||||
|
# Add any additional SELECTs.
|
||||||
|
if self._select:
|
||||||
|
select.extend(['(%s) AS %s' % (quote_only_if_word(s[1]), qn(s[0])) for s in self._select.items()])
|
||||||
|
|
||||||
|
# Start composing the body of the SQL statement.
|
||||||
|
sql = [" FROM", qn(opts.db_table)]
|
||||||
|
|
||||||
|
# Compose the join dictionary into SQL describing the joins.
|
||||||
|
if joins:
|
||||||
|
sql.append(" ".join(["%s %s %s ON %s" % (join_type, table, alias, condition)
|
||||||
|
for (alias, (table, join_type, condition)) in joins.items()]))
|
||||||
|
|
||||||
|
# Compose the tables clause into SQL.
|
||||||
|
if tables:
|
||||||
|
sql.append(", " + ", ".join(tables))
|
||||||
|
|
||||||
|
# Compose the where clause into SQL.
|
||||||
|
if where:
|
||||||
|
sql.append(where and "WHERE " + " AND ".join(where))
|
||||||
|
|
||||||
|
# ORDER BY clause
|
||||||
|
order_by = []
|
||||||
|
if self._order_by is not None:
|
||||||
|
ordering_to_use = self._order_by
|
||||||
|
else:
|
||||||
|
ordering_to_use = opts.ordering
|
||||||
|
for f in handle_legacy_orderlist(ordering_to_use):
|
||||||
|
if f == '?': # Special case.
|
||||||
|
order_by.append(DatabaseOperations().random_function_sql())
|
||||||
|
else:
|
||||||
|
if f.startswith('-'):
|
||||||
|
col_name = f[1:]
|
||||||
|
order = "DESC"
|
||||||
|
else:
|
||||||
|
col_name = f
|
||||||
|
order = "ASC"
|
||||||
|
if "." in col_name:
|
||||||
|
table_prefix, col_name = col_name.split('.', 1)
|
||||||
|
table_prefix = qn(table_prefix) + '.'
|
||||||
|
else:
|
||||||
|
# Use the database table as a column prefix if it wasn't given,
|
||||||
|
# and if the requested column isn't a custom SELECT.
|
||||||
|
if "." not in col_name and col_name not in (self._select or ()):
|
||||||
|
table_prefix = qn(opts.db_table) + '.'
|
||||||
|
else:
|
||||||
|
table_prefix = ''
|
||||||
|
order_by.append('%s%s %s' % (table_prefix, qn(orderfield2column(col_name, opts)), order))
|
||||||
|
if order_by:
|
||||||
|
sql.append("ORDER BY " + ", ".join(order_by))
|
||||||
|
|
||||||
|
# Look for column name collisions in the select elements
|
||||||
|
# and fix them with an AS alias. This allows us to do a
|
||||||
|
# SELECT * later in the paging query.
|
||||||
|
cols = [clause.split('.')[-1] for clause in select]
|
||||||
|
for index, col in enumerate(cols):
|
||||||
|
if cols.count(col) > 1:
|
||||||
|
col = '%s%d' % (col.replace('"', ''), index)
|
||||||
|
cols[index] = col
|
||||||
|
select[index] = '%s AS %s' % (select[index], col)
|
||||||
|
|
||||||
|
# LIMIT and OFFSET clauses
|
||||||
|
# To support limits and offsets, Oracle requires some funky rewriting of an otherwise normal looking query.
|
||||||
|
select_clause = ",".join(select)
|
||||||
|
distinct = (self._distinct and "DISTINCT " or "")
|
||||||
|
|
||||||
|
if order_by:
|
||||||
|
order_by_clause = " OVER (ORDER BY %s )" % (", ".join(order_by))
|
||||||
|
else:
|
||||||
|
#Oracle's row_number() function always requires an order-by clause.
|
||||||
|
#So we need to define a default order-by, since none was provided.
|
||||||
|
order_by_clause = " OVER (ORDER BY %s.%s)" % \
|
||||||
|
(qn(opts.db_table), qn(opts.fields[0].db_column or opts.fields[0].column))
|
||||||
|
# limit_and_offset_clause
|
||||||
|
if self._limit is None:
|
||||||
|
assert self._offset is None, "'offset' is not allowed without 'limit'"
|
||||||
|
|
||||||
|
if self._offset is not None:
|
||||||
|
offset = int(self._offset)
|
||||||
|
else:
|
||||||
|
offset = 0
|
||||||
|
if self._limit is not None:
|
||||||
|
limit = int(self._limit)
|
||||||
|
else:
|
||||||
|
limit = None
|
||||||
|
|
||||||
|
limit_and_offset_clause = ''
|
||||||
|
if limit is not None:
|
||||||
|
limit_and_offset_clause = "WHERE rn > %s AND rn <= %s" % (offset, limit+offset)
|
||||||
|
elif offset:
|
||||||
|
limit_and_offset_clause = "WHERE rn > %s" % (offset)
|
||||||
|
|
||||||
|
if len(limit_and_offset_clause) > 0:
|
||||||
|
fmt = \
|
||||||
|
"""SELECT * FROM
|
||||||
|
(SELECT %s%s,
|
||||||
|
ROW_NUMBER()%s AS rn
|
||||||
|
%s)
|
||||||
|
%s"""
|
||||||
|
full_query = fmt % (distinct, select_clause,
|
||||||
|
order_by_clause, ' '.join(sql).strip(),
|
||||||
|
limit_and_offset_clause)
|
||||||
|
else:
|
||||||
|
full_query = None
|
||||||
|
|
||||||
|
if get_full_query:
|
||||||
|
return select, " ".join(sql), params, full_query
|
||||||
|
else:
|
||||||
|
return select, " ".join(sql), params
|
||||||
|
|
||||||
|
def resolve_columns(self, row, fields=()):
|
||||||
|
from django.db.models.fields import DateField, DateTimeField, \
|
||||||
|
TimeField, BooleanField, NullBooleanField, DecimalField, Field
|
||||||
|
values = []
|
||||||
|
for value, field in map(None, row, fields):
|
||||||
|
if isinstance(value, Database.LOB):
|
||||||
|
value = value.read()
|
||||||
|
# Oracle stores empty strings as null. We need to undo this in
|
||||||
|
# order to adhere to the Django convention of using the empty
|
||||||
|
# string instead of null, but only if the field accepts the
|
||||||
|
# empty string.
|
||||||
|
if value is None and isinstance(field, Field) and field.empty_strings_allowed:
|
||||||
|
value = ''
|
||||||
|
# Convert 1 or 0 to True or False
|
||||||
|
elif value in (1, 0) and isinstance(field, (BooleanField, NullBooleanField)):
|
||||||
|
value = bool(value)
|
||||||
|
# Convert floats to decimals
|
||||||
|
elif value is not None and isinstance(field, DecimalField):
|
||||||
|
value = util.typecast_decimal(field.format_number(value))
|
||||||
|
# cx_Oracle always returns datetime.datetime objects for
|
||||||
|
# DATE and TIMESTAMP columns, but Django wants to see a
|
||||||
|
# python datetime.date, .time, or .datetime. We use the type
|
||||||
|
# of the Field to determine which to cast to, but it's not
|
||||||
|
# always available.
|
||||||
|
# As a workaround, we cast to date if all the time-related
|
||||||
|
# values are 0, or to time if the date is 1/1/1900.
|
||||||
|
# This could be cleaned a bit by adding a method to the Field
|
||||||
|
# classes to normalize values from the database (the to_python
|
||||||
|
# method is used for validation and isn't what we want here).
|
||||||
|
elif isinstance(value, Database.Timestamp):
|
||||||
|
# In Python 2.3, the cx_Oracle driver returns its own
|
||||||
|
# Timestamp object that we must convert to a datetime class.
|
||||||
|
if not isinstance(value, datetime.datetime):
|
||||||
|
value = datetime.datetime(value.year, value.month, value.day, value.hour,
|
||||||
|
value.minute, value.second, value.fsecond)
|
||||||
|
if isinstance(field, DateTimeField):
|
||||||
|
pass # DateTimeField subclasses DateField so must be checked first.
|
||||||
|
elif isinstance(field, DateField):
|
||||||
|
value = value.date()
|
||||||
|
elif isinstance(field, TimeField) or (value.year == 1900 and value.month == value.day == 1):
|
||||||
|
value = value.time()
|
||||||
|
elif value.hour == value.minute == value.second == value.microsecond == 0:
|
||||||
|
value = value.date()
|
||||||
|
values.append(value)
|
||||||
|
return values
|
||||||
|
|
||||||
|
return OracleQuerySet
|
||||||
|
|
||||||
|
def quote_name(self, name):
|
||||||
|
# SQL92 requires delimited (quoted) names to be case-sensitive. When
|
||||||
|
# not quoted, Oracle has case-insensitive behavior for identifiers, but
|
||||||
|
# always defaults to uppercase.
|
||||||
|
# We simplify things by making Oracle identifiers always uppercase.
|
||||||
|
if not name.startswith('"') and not name.endswith('"'):
|
||||||
|
name = '"%s"' % util.truncate_name(name.upper(), self.max_name_length())
|
||||||
|
return name.upper()
|
||||||
|
|
||||||
|
def random_function_sql(self):
|
||||||
|
return "DBMS_RANDOM.RANDOM"
|
||||||
|
|
||||||
|
def sql_flush(self, style, tables, sequences):
|
||||||
|
# Return a list of 'TRUNCATE x;', 'TRUNCATE y;',
|
||||||
|
# 'TRUNCATE z;'... style SQL statements
|
||||||
|
if tables:
|
||||||
|
# Oracle does support TRUNCATE, but it seems to get us into
|
||||||
|
# FK referential trouble, whereas DELETE FROM table works.
|
||||||
|
sql = ['%s %s %s;' % \
|
||||||
|
(style.SQL_KEYWORD('DELETE'),
|
||||||
|
style.SQL_KEYWORD('FROM'),
|
||||||
|
style.SQL_FIELD(self.quote_name(table))
|
||||||
|
) for table in tables]
|
||||||
|
# Since we've just deleted all the rows, running our sequence
|
||||||
|
# ALTER code will reset the sequence to 0.
|
||||||
|
for sequence_info in sequences:
|
||||||
|
table_name = sequence_info['table']
|
||||||
|
seq_name = get_sequence_name(table_name)
|
||||||
|
query = _get_sequence_reset_sql() % {'sequence': seq_name, 'table': self.quote_name(table_name)}
|
||||||
|
sql.append(query)
|
||||||
|
return sql
|
||||||
|
else:
|
||||||
|
return []
|
||||||
|
|
||||||
|
def sequence_reset_sql(self, style, model_list):
|
||||||
|
from django.db import models
|
||||||
|
output = []
|
||||||
|
query = _get_sequence_reset_sql()
|
||||||
|
for model in model_list:
|
||||||
|
for f in model._meta.fields:
|
||||||
|
if isinstance(f, models.AutoField):
|
||||||
|
sequence_name = get_sequence_name(model._meta.db_table)
|
||||||
|
output.append(query % {'sequence':sequence_name,
|
||||||
|
'table':model._meta.db_table})
|
||||||
|
break # Only one AutoField is allowed per model, so don't bother continuing.
|
||||||
|
for f in model._meta.many_to_many:
|
||||||
|
sequence_name = get_sequence_name(f.m2m_db_table())
|
||||||
|
output.append(query % {'sequence':sequence_name,
|
||||||
|
'table':f.m2m_db_table()})
|
||||||
|
return output
|
||||||
|
|
||||||
|
def start_transaction_sql(self):
|
||||||
|
return ''
|
||||||
|
|
||||||
|
def tablespace_sql(self, tablespace, inline=False):
|
||||||
|
return "%sTABLESPACE %s" % ((inline and "USING INDEX " or ""), self.quote_name(tablespace))
|
||||||
|
|
||||||
|
class DatabaseWrapper(BaseDatabaseWrapper):
|
||||||
|
features = DatabaseFeatures()
|
||||||
|
ops = DatabaseOperations()
|
||||||
|
operators = {
|
||||||
|
'exact': '= %s',
|
||||||
|
'iexact': '= UPPER(%s)',
|
||||||
|
'contains': "LIKE %s ESCAPE '\\'",
|
||||||
|
'icontains': "LIKE UPPER(%s) ESCAPE '\\'",
|
||||||
|
'gt': '> %s',
|
||||||
|
'gte': '>= %s',
|
||||||
|
'lt': '< %s',
|
||||||
|
'lte': '<= %s',
|
||||||
|
'startswith': "LIKE %s ESCAPE '\\'",
|
||||||
|
'endswith': "LIKE %s ESCAPE '\\'",
|
||||||
|
'istartswith': "LIKE UPPER(%s) ESCAPE '\\'",
|
||||||
|
'iendswith': "LIKE UPPER(%s) ESCAPE '\\'",
|
||||||
|
}
|
||||||
|
|
||||||
def _valid_connection(self):
|
def _valid_connection(self):
|
||||||
return self.connection is not None
|
return self.connection is not None
|
||||||
|
|
||||||
def cursor(self):
|
def _cursor(self, settings):
|
||||||
if not self._valid_connection():
|
if not self._valid_connection():
|
||||||
if len(settings.DATABASE_HOST.strip()) == 0:
|
if len(settings.DATABASE_HOST.strip()) == 0:
|
||||||
settings.DATABASE_HOST = 'localhost'
|
settings.DATABASE_HOST = 'localhost'
|
||||||
@ -55,32 +417,8 @@ class DatabaseWrapper(local):
|
|||||||
# Set oracle date to ansi date format.
|
# Set oracle date to ansi date format.
|
||||||
cursor.execute("ALTER SESSION SET NLS_DATE_FORMAT = 'YYYY-MM-DD'")
|
cursor.execute("ALTER SESSION SET NLS_DATE_FORMAT = 'YYYY-MM-DD'")
|
||||||
cursor.execute("ALTER SESSION SET NLS_TIMESTAMP_FORMAT = 'YYYY-MM-DD HH24:MI:SS.FF'")
|
cursor.execute("ALTER SESSION SET NLS_TIMESTAMP_FORMAT = 'YYYY-MM-DD HH24:MI:SS.FF'")
|
||||||
if settings.DEBUG:
|
|
||||||
return util.CursorDebugWrapper(cursor, self)
|
|
||||||
return cursor
|
return cursor
|
||||||
|
|
||||||
def _commit(self):
|
|
||||||
if self.connection is not None:
|
|
||||||
return self.connection.commit()
|
|
||||||
|
|
||||||
def _rollback(self):
|
|
||||||
if self.connection is not None:
|
|
||||||
return self.connection.rollback()
|
|
||||||
|
|
||||||
def close(self):
|
|
||||||
if self.connection is not None:
|
|
||||||
self.connection.close()
|
|
||||||
self.connection = None
|
|
||||||
|
|
||||||
allows_group_by_ordinal = False
|
|
||||||
allows_unique_and_pk = False # Suppress UNIQUE/PK for Oracle (ORA-02259)
|
|
||||||
autoindexes_primary_keys = True
|
|
||||||
needs_datetime_string_cast = False
|
|
||||||
needs_upper_for_iops = True
|
|
||||||
supports_constraints = True
|
|
||||||
supports_tablespaces = True
|
|
||||||
uses_case_insensitive_names = True
|
|
||||||
|
|
||||||
class FormatStylePlaceholderCursor(Database.Cursor):
|
class FormatStylePlaceholderCursor(Database.Cursor):
|
||||||
"""
|
"""
|
||||||
Django uses "format" (e.g. '%s') style placeholders, but Oracle uses ":var"
|
Django uses "format" (e.g. '%s') style placeholders, but Oracle uses ":var"
|
||||||
@ -145,90 +483,6 @@ def to_unicode(s):
|
|||||||
return force_unicode(s)
|
return force_unicode(s)
|
||||||
return s
|
return s
|
||||||
|
|
||||||
def quote_name(name):
|
|
||||||
# SQL92 requires delimited (quoted) names to be case-sensitive. When
|
|
||||||
# not quoted, Oracle has case-insensitive behavior for identifiers, but
|
|
||||||
# always defaults to uppercase.
|
|
||||||
# We simplify things by making Oracle identifiers always uppercase.
|
|
||||||
if not name.startswith('"') and not name.endswith('"'):
|
|
||||||
name = '"%s"' % util.truncate_name(name.upper(), get_max_name_length())
|
|
||||||
return name.upper()
|
|
||||||
|
|
||||||
dictfetchone = util.dictfetchone
|
|
||||||
dictfetchmany = util.dictfetchmany
|
|
||||||
dictfetchall = util.dictfetchall
|
|
||||||
|
|
||||||
def get_last_insert_id(cursor, table_name, pk_name):
|
|
||||||
sq_name = util.truncate_name(table_name, get_max_name_length()-3)
|
|
||||||
cursor.execute('SELECT %s_sq.currval FROM dual' % sq_name)
|
|
||||||
return cursor.fetchone()[0]
|
|
||||||
|
|
||||||
def get_date_extract_sql(lookup_type, table_name):
|
|
||||||
# lookup_type is 'year', 'month', 'day'
|
|
||||||
# http://download-east.oracle.com/docs/cd/B10501_01/server.920/a96540/functions42a.htm#1017163
|
|
||||||
return "EXTRACT(%s FROM %s)" % (lookup_type, table_name)
|
|
||||||
|
|
||||||
def get_date_trunc_sql(lookup_type, field_name):
|
|
||||||
# lookup_type is 'year', 'month', 'day'
|
|
||||||
# Oracle uses TRUNC() for both dates and numbers.
|
|
||||||
# http://download-east.oracle.com/docs/cd/B10501_01/server.920/a96540/functions155a.htm#SQLRF06151
|
|
||||||
if lookup_type == 'day':
|
|
||||||
sql = 'TRUNC(%s)' % (field_name,)
|
|
||||||
else:
|
|
||||||
sql = "TRUNC(%s, '%s')" % (field_name, lookup_type)
|
|
||||||
return sql
|
|
||||||
|
|
||||||
def get_datetime_cast_sql():
|
|
||||||
return "TO_TIMESTAMP(%s, 'YYYY-MM-DD HH24:MI:SS.FF')"
|
|
||||||
|
|
||||||
def get_limit_offset_sql(limit, offset=None):
|
|
||||||
# Limits and offset are too complicated to be handled here.
|
|
||||||
# Instead, they are handled in django/db/backends/oracle/query.py.
|
|
||||||
return ""
|
|
||||||
|
|
||||||
def get_random_function_sql():
|
|
||||||
return "DBMS_RANDOM.RANDOM"
|
|
||||||
|
|
||||||
def get_deferrable_sql():
|
|
||||||
return " DEFERRABLE INITIALLY DEFERRED"
|
|
||||||
|
|
||||||
def get_fulltext_search_sql(field_name):
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
def get_drop_foreignkey_sql():
|
|
||||||
return "DROP CONSTRAINT"
|
|
||||||
|
|
||||||
def get_pk_default_value():
|
|
||||||
return "DEFAULT"
|
|
||||||
|
|
||||||
def get_max_name_length():
|
|
||||||
return 30
|
|
||||||
|
|
||||||
def get_start_transaction_sql():
|
|
||||||
return None
|
|
||||||
|
|
||||||
def get_tablespace_sql(tablespace, inline=False):
|
|
||||||
return "%sTABLESPACE %s" % ((inline and "USING INDEX " or ""), quote_name(tablespace))
|
|
||||||
|
|
||||||
def get_autoinc_sql(table):
|
|
||||||
# To simulate auto-incrementing primary keys in Oracle, we have to
|
|
||||||
# create a sequence and a trigger.
|
|
||||||
sq_name = get_sequence_name(table)
|
|
||||||
tr_name = get_trigger_name(table)
|
|
||||||
sequence_sql = 'CREATE SEQUENCE %s;' % sq_name
|
|
||||||
trigger_sql = """CREATE OR REPLACE TRIGGER %s
|
|
||||||
BEFORE INSERT ON %s
|
|
||||||
FOR EACH ROW
|
|
||||||
WHEN (new.id IS NULL)
|
|
||||||
BEGIN
|
|
||||||
SELECT %s.nextval INTO :new.id FROM dual;
|
|
||||||
END;
|
|
||||||
/""" % (tr_name, quote_name(table), sq_name)
|
|
||||||
return sequence_sql, trigger_sql
|
|
||||||
|
|
||||||
def get_drop_sequence(table):
|
|
||||||
return "DROP SEQUENCE %s;" % quote_name(get_sequence_name(table))
|
|
||||||
|
|
||||||
def _get_sequence_reset_sql():
|
def _get_sequence_reset_sql():
|
||||||
# TODO: colorize this SQL code with style.SQL_KEYWORD(), etc.
|
# TODO: colorize this SQL code with style.SQL_KEYWORD(), etc.
|
||||||
return """
|
return """
|
||||||
@ -249,310 +503,10 @@ def _get_sequence_reset_sql():
|
|||||||
END;
|
END;
|
||||||
/"""
|
/"""
|
||||||
|
|
||||||
def get_sql_flush(style, tables, sequences):
|
|
||||||
"""Return a list of SQL statements required to remove all data from
|
|
||||||
all tables in the database (without actually removing the tables
|
|
||||||
themselves) and put the database in an empty 'initial' state
|
|
||||||
"""
|
|
||||||
# Return a list of 'TRUNCATE x;', 'TRUNCATE y;',
|
|
||||||
# 'TRUNCATE z;'... style SQL statements
|
|
||||||
if tables:
|
|
||||||
# Oracle does support TRUNCATE, but it seems to get us into
|
|
||||||
# FK referential trouble, whereas DELETE FROM table works.
|
|
||||||
sql = ['%s %s %s;' % \
|
|
||||||
(style.SQL_KEYWORD('DELETE'),
|
|
||||||
style.SQL_KEYWORD('FROM'),
|
|
||||||
style.SQL_FIELD(quote_name(table))
|
|
||||||
) for table in tables]
|
|
||||||
# Since we've just deleted all the rows, running our sequence
|
|
||||||
# ALTER code will reset the sequence to 0.
|
|
||||||
for sequence_info in sequences:
|
|
||||||
table_name = sequence_info['table']
|
|
||||||
seq_name = get_sequence_name(table_name)
|
|
||||||
query = _get_sequence_reset_sql() % {'sequence':seq_name,
|
|
||||||
'table':quote_name(table_name)}
|
|
||||||
sql.append(query)
|
|
||||||
return sql
|
|
||||||
else:
|
|
||||||
return []
|
|
||||||
|
|
||||||
def get_sequence_name(table):
|
def get_sequence_name(table):
|
||||||
name_length = get_max_name_length() - 3
|
name_length = DatabaseOperations().max_name_length() - 3
|
||||||
return '%s_SQ' % util.truncate_name(table, name_length).upper()
|
return '%s_SQ' % util.truncate_name(table, name_length).upper()
|
||||||
|
|
||||||
def get_sql_sequence_reset(style, model_list):
|
|
||||||
"Returns a list of the SQL statements to reset sequences for the given models."
|
|
||||||
from django.db import models
|
|
||||||
output = []
|
|
||||||
query = _get_sequence_reset_sql()
|
|
||||||
for model in model_list:
|
|
||||||
for f in model._meta.fields:
|
|
||||||
if isinstance(f, models.AutoField):
|
|
||||||
sequence_name = get_sequence_name(model._meta.db_table)
|
|
||||||
output.append(query % {'sequence':sequence_name,
|
|
||||||
'table':model._meta.db_table})
|
|
||||||
break # Only one AutoField is allowed per model, so don't bother continuing.
|
|
||||||
for f in model._meta.many_to_many:
|
|
||||||
sequence_name = get_sequence_name(f.m2m_db_table())
|
|
||||||
output.append(query % {'sequence':sequence_name,
|
|
||||||
'table':f.m2m_db_table()})
|
|
||||||
return output
|
|
||||||
|
|
||||||
def get_trigger_name(table):
|
def get_trigger_name(table):
|
||||||
name_length = get_max_name_length() - 3
|
name_length = DatabaseOperations().max_name_length() - 3
|
||||||
return '%s_TR' % util.truncate_name(table, name_length).upper()
|
return '%s_TR' % util.truncate_name(table, name_length).upper()
|
||||||
|
|
||||||
def get_query_set_class(DefaultQuerySet):
|
|
||||||
"Create a custom QuerySet class for Oracle."
|
|
||||||
|
|
||||||
from django.db import backend, connection
|
|
||||||
from django.db.models.query import EmptyResultSet, GET_ITERATOR_CHUNK_SIZE, quote_only_if_word
|
|
||||||
|
|
||||||
class OracleQuerySet(DefaultQuerySet):
|
|
||||||
|
|
||||||
def iterator(self):
|
|
||||||
"Performs the SELECT database lookup of this QuerySet."
|
|
||||||
|
|
||||||
from django.db.models.query import get_cached_row
|
|
||||||
|
|
||||||
# self._select is a dictionary, and dictionaries' key order is
|
|
||||||
# undefined, so we convert it to a list of tuples.
|
|
||||||
extra_select = self._select.items()
|
|
||||||
|
|
||||||
full_query = None
|
|
||||||
|
|
||||||
try:
|
|
||||||
try:
|
|
||||||
select, sql, params, full_query = self._get_sql_clause(get_full_query=True)
|
|
||||||
except TypeError:
|
|
||||||
select, sql, params = self._get_sql_clause()
|
|
||||||
except EmptyResultSet:
|
|
||||||
raise StopIteration
|
|
||||||
if not full_query:
|
|
||||||
full_query = "SELECT %s%s\n%s" % \
|
|
||||||
((self._distinct and "DISTINCT " or ""),
|
|
||||||
', '.join(select), sql)
|
|
||||||
|
|
||||||
cursor = connection.cursor()
|
|
||||||
cursor.execute(full_query, params)
|
|
||||||
|
|
||||||
fill_cache = self._select_related
|
|
||||||
fields = self.model._meta.fields
|
|
||||||
index_end = len(fields)
|
|
||||||
|
|
||||||
# so here's the logic;
|
|
||||||
# 1. retrieve each row in turn
|
|
||||||
# 2. convert NCLOBs
|
|
||||||
|
|
||||||
while 1:
|
|
||||||
rows = cursor.fetchmany(GET_ITERATOR_CHUNK_SIZE)
|
|
||||||
if not rows:
|
|
||||||
raise StopIteration
|
|
||||||
for row in rows:
|
|
||||||
row = self.resolve_columns(row, fields)
|
|
||||||
if fill_cache:
|
|
||||||
obj, index_end = get_cached_row(klass=self.model, row=row,
|
|
||||||
index_start=0, max_depth=self._max_related_depth)
|
|
||||||
else:
|
|
||||||
obj = self.model(*row[:index_end])
|
|
||||||
for i, k in enumerate(extra_select):
|
|
||||||
setattr(obj, k[0], row[index_end+i])
|
|
||||||
yield obj
|
|
||||||
|
|
||||||
|
|
||||||
def _get_sql_clause(self, get_full_query=False):
|
|
||||||
from django.db.models.query import fill_table_cache, \
|
|
||||||
handle_legacy_orderlist, orderfield2column
|
|
||||||
|
|
||||||
opts = self.model._meta
|
|
||||||
|
|
||||||
# Construct the fundamental parts of the query: SELECT X FROM Y WHERE Z.
|
|
||||||
select = ["%s.%s" % (backend.quote_name(opts.db_table), backend.quote_name(f.column)) for f in opts.fields]
|
|
||||||
tables = [quote_only_if_word(t) for t in self._tables]
|
|
||||||
joins = SortedDict()
|
|
||||||
where = self._where[:]
|
|
||||||
params = self._params[:]
|
|
||||||
|
|
||||||
# Convert self._filters into SQL.
|
|
||||||
joins2, where2, params2 = self._filters.get_sql(opts)
|
|
||||||
joins.update(joins2)
|
|
||||||
where.extend(where2)
|
|
||||||
params.extend(params2)
|
|
||||||
|
|
||||||
# Add additional tables and WHERE clauses based on select_related.
|
|
||||||
if self._select_related:
|
|
||||||
fill_table_cache(opts, select, tables, where, opts.db_table, [opts.db_table])
|
|
||||||
|
|
||||||
# Add any additional SELECTs.
|
|
||||||
if self._select:
|
|
||||||
select.extend(['(%s) AS %s' % (quote_only_if_word(s[1]), backend.quote_name(s[0])) for s in self._select.items()])
|
|
||||||
|
|
||||||
# Start composing the body of the SQL statement.
|
|
||||||
sql = [" FROM", backend.quote_name(opts.db_table)]
|
|
||||||
|
|
||||||
# Compose the join dictionary into SQL describing the joins.
|
|
||||||
if joins:
|
|
||||||
sql.append(" ".join(["%s %s %s ON %s" % (join_type, table, alias, condition)
|
|
||||||
for (alias, (table, join_type, condition)) in joins.items()]))
|
|
||||||
|
|
||||||
# Compose the tables clause into SQL.
|
|
||||||
if tables:
|
|
||||||
sql.append(", " + ", ".join(tables))
|
|
||||||
|
|
||||||
# Compose the where clause into SQL.
|
|
||||||
if where:
|
|
||||||
sql.append(where and "WHERE " + " AND ".join(where))
|
|
||||||
|
|
||||||
# ORDER BY clause
|
|
||||||
order_by = []
|
|
||||||
if self._order_by is not None:
|
|
||||||
ordering_to_use = self._order_by
|
|
||||||
else:
|
|
||||||
ordering_to_use = opts.ordering
|
|
||||||
for f in handle_legacy_orderlist(ordering_to_use):
|
|
||||||
if f == '?': # Special case.
|
|
||||||
order_by.append(backend.get_random_function_sql())
|
|
||||||
else:
|
|
||||||
if f.startswith('-'):
|
|
||||||
col_name = f[1:]
|
|
||||||
order = "DESC"
|
|
||||||
else:
|
|
||||||
col_name = f
|
|
||||||
order = "ASC"
|
|
||||||
if "." in col_name:
|
|
||||||
table_prefix, col_name = col_name.split('.', 1)
|
|
||||||
table_prefix = backend.quote_name(table_prefix) + '.'
|
|
||||||
else:
|
|
||||||
# Use the database table as a column prefix if it wasn't given,
|
|
||||||
# and if the requested column isn't a custom SELECT.
|
|
||||||
if "." not in col_name and col_name not in (self._select or ()):
|
|
||||||
table_prefix = backend.quote_name(opts.db_table) + '.'
|
|
||||||
else:
|
|
||||||
table_prefix = ''
|
|
||||||
order_by.append('%s%s %s' % (table_prefix, backend.quote_name(orderfield2column(col_name, opts)), order))
|
|
||||||
if order_by:
|
|
||||||
sql.append("ORDER BY " + ", ".join(order_by))
|
|
||||||
|
|
||||||
# Look for column name collisions in the select elements
|
|
||||||
# and fix them with an AS alias. This allows us to do a
|
|
||||||
# SELECT * later in the paging query.
|
|
||||||
cols = [clause.split('.')[-1] for clause in select]
|
|
||||||
for index, col in enumerate(cols):
|
|
||||||
if cols.count(col) > 1:
|
|
||||||
col = '%s%d' % (col.replace('"', ''), index)
|
|
||||||
cols[index] = col
|
|
||||||
select[index] = '%s AS %s' % (select[index], col)
|
|
||||||
|
|
||||||
# LIMIT and OFFSET clauses
|
|
||||||
# To support limits and offsets, Oracle requires some funky rewriting of an otherwise normal looking query.
|
|
||||||
select_clause = ",".join(select)
|
|
||||||
distinct = (self._distinct and "DISTINCT " or "")
|
|
||||||
|
|
||||||
if order_by:
|
|
||||||
order_by_clause = " OVER (ORDER BY %s )" % (", ".join(order_by))
|
|
||||||
else:
|
|
||||||
#Oracle's row_number() function always requires an order-by clause.
|
|
||||||
#So we need to define a default order-by, since none was provided.
|
|
||||||
order_by_clause = " OVER (ORDER BY %s.%s)" % \
|
|
||||||
(backend.quote_name(opts.db_table),
|
|
||||||
backend.quote_name(opts.fields[0].db_column or opts.fields[0].column))
|
|
||||||
# limit_and_offset_clause
|
|
||||||
if self._limit is None:
|
|
||||||
assert self._offset is None, "'offset' is not allowed without 'limit'"
|
|
||||||
|
|
||||||
if self._offset is not None:
|
|
||||||
offset = int(self._offset)
|
|
||||||
else:
|
|
||||||
offset = 0
|
|
||||||
if self._limit is not None:
|
|
||||||
limit = int(self._limit)
|
|
||||||
else:
|
|
||||||
limit = None
|
|
||||||
|
|
||||||
limit_and_offset_clause = ''
|
|
||||||
if limit is not None:
|
|
||||||
limit_and_offset_clause = "WHERE rn > %s AND rn <= %s" % (offset, limit+offset)
|
|
||||||
elif offset:
|
|
||||||
limit_and_offset_clause = "WHERE rn > %s" % (offset)
|
|
||||||
|
|
||||||
if len(limit_and_offset_clause) > 0:
|
|
||||||
fmt = \
|
|
||||||
"""SELECT * FROM
|
|
||||||
(SELECT %s%s,
|
|
||||||
ROW_NUMBER()%s AS rn
|
|
||||||
%s)
|
|
||||||
%s"""
|
|
||||||
full_query = fmt % (distinct, select_clause,
|
|
||||||
order_by_clause, ' '.join(sql).strip(),
|
|
||||||
limit_and_offset_clause)
|
|
||||||
else:
|
|
||||||
full_query = None
|
|
||||||
|
|
||||||
if get_full_query:
|
|
||||||
return select, " ".join(sql), params, full_query
|
|
||||||
else:
|
|
||||||
return select, " ".join(sql), params
|
|
||||||
|
|
||||||
def resolve_columns(self, row, fields=()):
|
|
||||||
from django.db.models.fields import DateField, DateTimeField, \
|
|
||||||
TimeField, BooleanField, NullBooleanField, DecimalField, Field
|
|
||||||
values = []
|
|
||||||
for value, field in map(None, row, fields):
|
|
||||||
if isinstance(value, Database.LOB):
|
|
||||||
value = value.read()
|
|
||||||
# Oracle stores empty strings as null. We need to undo this in
|
|
||||||
# order to adhere to the Django convention of using the empty
|
|
||||||
# string instead of null, but only if the field accepts the
|
|
||||||
# empty string.
|
|
||||||
if value is None and isinstance(field, Field) and field.empty_strings_allowed:
|
|
||||||
value = ''
|
|
||||||
# Convert 1 or 0 to True or False
|
|
||||||
elif value in (1, 0) and isinstance(field, (BooleanField, NullBooleanField)):
|
|
||||||
value = bool(value)
|
|
||||||
# Convert floats to decimals
|
|
||||||
elif value is not None and isinstance(field, DecimalField):
|
|
||||||
value = util.typecast_decimal(field.format_number(value))
|
|
||||||
# cx_Oracle always returns datetime.datetime objects for
|
|
||||||
# DATE and TIMESTAMP columns, but Django wants to see a
|
|
||||||
# python datetime.date, .time, or .datetime. We use the type
|
|
||||||
# of the Field to determine which to cast to, but it's not
|
|
||||||
# always available.
|
|
||||||
# As a workaround, we cast to date if all the time-related
|
|
||||||
# values are 0, or to time if the date is 1/1/1900.
|
|
||||||
# This could be cleaned a bit by adding a method to the Field
|
|
||||||
# classes to normalize values from the database (the to_python
|
|
||||||
# method is used for validation and isn't what we want here).
|
|
||||||
elif isinstance(value, Database.Timestamp):
|
|
||||||
# In Python 2.3, the cx_Oracle driver returns its own
|
|
||||||
# Timestamp object that we must convert to a datetime class.
|
|
||||||
if not isinstance(value, datetime.datetime):
|
|
||||||
value = datetime.datetime(value.year, value.month, value.day, value.hour,
|
|
||||||
value.minute, value.second, value.fsecond)
|
|
||||||
if isinstance(field, DateTimeField):
|
|
||||||
pass # DateTimeField subclasses DateField so must be checked first.
|
|
||||||
elif isinstance(field, DateField):
|
|
||||||
value = value.date()
|
|
||||||
elif isinstance(field, TimeField) or (value.year == 1900 and value.month == value.day == 1):
|
|
||||||
value = value.time()
|
|
||||||
elif value.hour == value.minute == value.second == value.microsecond == 0:
|
|
||||||
value = value.date()
|
|
||||||
values.append(value)
|
|
||||||
return values
|
|
||||||
|
|
||||||
return OracleQuerySet
|
|
||||||
|
|
||||||
|
|
||||||
OPERATOR_MAPPING = {
|
|
||||||
'exact': '= %s',
|
|
||||||
'iexact': '= UPPER(%s)',
|
|
||||||
'contains': "LIKE %s ESCAPE '\\'",
|
|
||||||
'icontains': "LIKE UPPER(%s) ESCAPE '\\'",
|
|
||||||
'gt': '> %s',
|
|
||||||
'gte': '>= %s',
|
|
||||||
'lt': '< %s',
|
|
||||||
'lte': '<= %s',
|
|
||||||
'startswith': "LIKE %s ESCAPE '\\'",
|
|
||||||
'endswith': "LIKE %s ESCAPE '\\'",
|
|
||||||
'istartswith': "LIKE UPPER(%s) ESCAPE '\\'",
|
|
||||||
'iendswith': "LIKE UPPER(%s) ESCAPE '\\'",
|
|
||||||
}
|
|
||||||
|
@ -36,9 +36,7 @@ TEST_DATABASE_PREFIX = 'test_'
|
|||||||
PASSWORD = 'Im_a_lumberjack'
|
PASSWORD = 'Im_a_lumberjack'
|
||||||
REMEMBER = {}
|
REMEMBER = {}
|
||||||
|
|
||||||
|
def create_test_db(settings, connection, verbosity=1, autoclobber=False):
|
||||||
def create_test_db(settings, connection, backend, verbosity=1, autoclobber=False):
|
|
||||||
|
|
||||||
TEST_DATABASE_NAME = _test_database_name(settings)
|
TEST_DATABASE_NAME = _test_database_name(settings)
|
||||||
TEST_DATABASE_USER = _test_database_user(settings)
|
TEST_DATABASE_USER = _test_database_user(settings)
|
||||||
TEST_DATABASE_PASSWD = _test_database_passwd(settings)
|
TEST_DATABASE_PASSWD = _test_database_passwd(settings)
|
||||||
@ -115,8 +113,7 @@ def create_test_db(settings, connection, backend, verbosity=1, autoclobber=False
|
|||||||
# the side effect of initializing the test database.
|
# the side effect of initializing the test database.
|
||||||
cursor = connection.cursor()
|
cursor = connection.cursor()
|
||||||
|
|
||||||
|
def destroy_test_db(settings, connection, old_database_name, verbosity=1):
|
||||||
def destroy_test_db(settings, connection, backend, old_database_name, verbosity=1):
|
|
||||||
connection.close()
|
connection.close()
|
||||||
|
|
||||||
TEST_DATABASE_NAME = _test_database_name(settings)
|
TEST_DATABASE_NAME = _test_database_name(settings)
|
||||||
@ -152,7 +149,6 @@ def destroy_test_db(settings, connection, backend, old_database_name, verbosity=
|
|||||||
_destroy_test_db(cursor, parameters, verbosity)
|
_destroy_test_db(cursor, parameters, verbosity)
|
||||||
connection.close()
|
connection.close()
|
||||||
|
|
||||||
|
|
||||||
def _create_test_db(cursor, parameters, verbosity):
|
def _create_test_db(cursor, parameters, verbosity):
|
||||||
if verbosity >= 2:
|
if verbosity >= 2:
|
||||||
print "_create_test_db(): dbname = %s" % parameters['dbname']
|
print "_create_test_db(): dbname = %s" % parameters['dbname']
|
||||||
@ -168,7 +164,6 @@ def _create_test_db(cursor, parameters, verbosity):
|
|||||||
]
|
]
|
||||||
_execute_statements(cursor, statements, parameters, verbosity)
|
_execute_statements(cursor, statements, parameters, verbosity)
|
||||||
|
|
||||||
|
|
||||||
def _create_test_user(cursor, parameters, verbosity):
|
def _create_test_user(cursor, parameters, verbosity):
|
||||||
if verbosity >= 2:
|
if verbosity >= 2:
|
||||||
print "_create_test_user(): username = %s" % parameters['user']
|
print "_create_test_user(): username = %s" % parameters['user']
|
||||||
@ -182,7 +177,6 @@ def _create_test_user(cursor, parameters, verbosity):
|
|||||||
]
|
]
|
||||||
_execute_statements(cursor, statements, parameters, verbosity)
|
_execute_statements(cursor, statements, parameters, verbosity)
|
||||||
|
|
||||||
|
|
||||||
def _destroy_test_db(cursor, parameters, verbosity):
|
def _destroy_test_db(cursor, parameters, verbosity):
|
||||||
if verbosity >= 2:
|
if verbosity >= 2:
|
||||||
print "_destroy_test_db(): dbname=%s" % parameters['dbname']
|
print "_destroy_test_db(): dbname=%s" % parameters['dbname']
|
||||||
@ -192,7 +186,6 @@ def _destroy_test_db(cursor, parameters, verbosity):
|
|||||||
]
|
]
|
||||||
_execute_statements(cursor, statements, parameters, verbosity)
|
_execute_statements(cursor, statements, parameters, verbosity)
|
||||||
|
|
||||||
|
|
||||||
def _destroy_test_user(cursor, parameters, verbosity):
|
def _destroy_test_user(cursor, parameters, verbosity):
|
||||||
if verbosity >= 2:
|
if verbosity >= 2:
|
||||||
print "_destroy_test_user(): user=%s" % parameters['user']
|
print "_destroy_test_user(): user=%s" % parameters['user']
|
||||||
@ -202,7 +195,6 @@ def _destroy_test_user(cursor, parameters, verbosity):
|
|||||||
]
|
]
|
||||||
_execute_statements(cursor, statements, parameters, verbosity)
|
_execute_statements(cursor, statements, parameters, verbosity)
|
||||||
|
|
||||||
|
|
||||||
def _execute_statements(cursor, statements, parameters, verbosity):
|
def _execute_statements(cursor, statements, parameters, verbosity):
|
||||||
for template in statements:
|
for template in statements:
|
||||||
stmt = template % parameters
|
stmt = template % parameters
|
||||||
@ -214,7 +206,6 @@ def _execute_statements(cursor, statements, parameters, verbosity):
|
|||||||
sys.stderr.write("Failed (%s)\n" % (err))
|
sys.stderr.write("Failed (%s)\n" % (err))
|
||||||
raise
|
raise
|
||||||
|
|
||||||
|
|
||||||
def _test_database_name(settings):
|
def _test_database_name(settings):
|
||||||
name = TEST_DATABASE_PREFIX + settings.DATABASE_NAME
|
name = TEST_DATABASE_PREFIX + settings.DATABASE_NAME
|
||||||
try:
|
try:
|
||||||
@ -226,7 +217,6 @@ def _test_database_name(settings):
|
|||||||
raise
|
raise
|
||||||
return name
|
return name
|
||||||
|
|
||||||
|
|
||||||
def _test_database_create(settings):
|
def _test_database_create(settings):
|
||||||
name = True
|
name = True
|
||||||
try:
|
try:
|
||||||
@ -240,7 +230,6 @@ def _test_database_create(settings):
|
|||||||
raise
|
raise
|
||||||
return name
|
return name
|
||||||
|
|
||||||
|
|
||||||
def _test_user_create(settings):
|
def _test_user_create(settings):
|
||||||
name = True
|
name = True
|
||||||
try:
|
try:
|
||||||
@ -254,7 +243,6 @@ def _test_user_create(settings):
|
|||||||
raise
|
raise
|
||||||
return name
|
return name
|
||||||
|
|
||||||
|
|
||||||
def _test_database_user(settings):
|
def _test_database_user(settings):
|
||||||
name = TEST_DATABASE_PREFIX + settings.DATABASE_NAME
|
name = TEST_DATABASE_PREFIX + settings.DATABASE_NAME
|
||||||
try:
|
try:
|
||||||
@ -266,7 +254,6 @@ def _test_database_user(settings):
|
|||||||
raise
|
raise
|
||||||
return name
|
return name
|
||||||
|
|
||||||
|
|
||||||
def _test_database_passwd(settings):
|
def _test_database_passwd(settings):
|
||||||
name = PASSWORD
|
name = PASSWORD
|
||||||
try:
|
try:
|
||||||
@ -278,7 +265,6 @@ def _test_database_passwd(settings):
|
|||||||
raise
|
raise
|
||||||
return name
|
return name
|
||||||
|
|
||||||
|
|
||||||
def _test_database_tblspace(settings):
|
def _test_database_tblspace(settings):
|
||||||
name = TEST_DATABASE_PREFIX + settings.DATABASE_NAME
|
name = TEST_DATABASE_PREFIX + settings.DATABASE_NAME
|
||||||
try:
|
try:
|
||||||
@ -290,7 +276,6 @@ def _test_database_tblspace(settings):
|
|||||||
raise
|
raise
|
||||||
return name
|
return name
|
||||||
|
|
||||||
|
|
||||||
def _test_database_tblspace_tmp(settings):
|
def _test_database_tblspace_tmp(settings):
|
||||||
name = TEST_DATABASE_PREFIX + settings.DATABASE_NAME + '_temp'
|
name = TEST_DATABASE_PREFIX + settings.DATABASE_NAME + '_temp'
|
||||||
try:
|
try:
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
from django.db.backends.oracle.base import quote_name
|
from django.db.backends.oracle.base import DatabaseOperations
|
||||||
import re
|
import re
|
||||||
import cx_Oracle
|
import cx_Oracle
|
||||||
|
|
||||||
|
quote_name = DatabaseOperations().quote_name
|
||||||
foreign_key_re = re.compile(r"\sCONSTRAINT `[^`]*` FOREIGN KEY \(`([^`]*)`\) REFERENCES `([^`]*)` \(`([^`]*)`\)")
|
foreign_key_re = re.compile(r"\sCONSTRAINT `[^`]*` FOREIGN KEY \(`([^`]*)`\) REFERENCES `([^`]*)` \(`([^`]*)`\)")
|
||||||
|
|
||||||
def get_table_list(cursor):
|
def get_table_list(cursor):
|
||||||
|
@ -5,23 +5,17 @@ Requires psycopg 1: http://initd.org/projects/psycopg1
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
from django.utils.encoding import smart_str, smart_unicode
|
from django.utils.encoding import smart_str, smart_unicode
|
||||||
from django.db.backends import util
|
from django.db.backends import BaseDatabaseWrapper, BaseDatabaseFeatures, util
|
||||||
|
from django.db.backends.postgresql.operations import DatabaseOperations
|
||||||
try:
|
try:
|
||||||
import psycopg as Database
|
import psycopg as Database
|
||||||
except ImportError, e:
|
except ImportError, e:
|
||||||
from django.core.exceptions import ImproperlyConfigured
|
from django.core.exceptions import ImproperlyConfigured
|
||||||
raise ImproperlyConfigured, "Error loading psycopg module: %s" % e
|
raise ImproperlyConfigured("Error loading psycopg module: %s" % e)
|
||||||
|
|
||||||
DatabaseError = Database.DatabaseError
|
DatabaseError = Database.DatabaseError
|
||||||
IntegrityError = Database.IntegrityError
|
IntegrityError = Database.IntegrityError
|
||||||
|
|
||||||
try:
|
|
||||||
# Only exists in Python 2.4+
|
|
||||||
from threading import local
|
|
||||||
except ImportError:
|
|
||||||
# Import copy of _thread_local.py from Python 2.4
|
|
||||||
from django.utils._threading_local import local
|
|
||||||
|
|
||||||
class UnicodeCursorWrapper(object):
|
class UnicodeCursorWrapper(object):
|
||||||
"""
|
"""
|
||||||
A thin wrapper around psycopg cursors that allows them to accept Unicode
|
A thin wrapper around psycopg cursors that allows them to accept Unicode
|
||||||
@ -62,22 +56,36 @@ class UnicodeCursorWrapper(object):
|
|||||||
else:
|
else:
|
||||||
return getattr(self.cursor, attr)
|
return getattr(self.cursor, attr)
|
||||||
|
|
||||||
postgres_version = None
|
class DatabaseFeatures(BaseDatabaseFeatures):
|
||||||
|
pass # This backend uses all the defaults.
|
||||||
|
|
||||||
class DatabaseWrapper(local):
|
class DatabaseWrapper(BaseDatabaseWrapper):
|
||||||
def __init__(self, **kwargs):
|
features = DatabaseFeatures()
|
||||||
self.connection = None
|
ops = DatabaseOperations()
|
||||||
self.queries = []
|
operators = {
|
||||||
self.options = kwargs
|
'exact': '= %s',
|
||||||
|
'iexact': 'ILIKE %s',
|
||||||
|
'contains': 'LIKE %s',
|
||||||
|
'icontains': 'ILIKE %s',
|
||||||
|
'regex': '~ %s',
|
||||||
|
'iregex': '~* %s',
|
||||||
|
'gt': '> %s',
|
||||||
|
'gte': '>= %s',
|
||||||
|
'lt': '< %s',
|
||||||
|
'lte': '<= %s',
|
||||||
|
'startswith': 'LIKE %s',
|
||||||
|
'endswith': 'LIKE %s',
|
||||||
|
'istartswith': 'ILIKE %s',
|
||||||
|
'iendswith': 'ILIKE %s',
|
||||||
|
}
|
||||||
|
|
||||||
def cursor(self):
|
def _cursor(self, settings):
|
||||||
from django.conf import settings
|
|
||||||
set_tz = False
|
set_tz = False
|
||||||
if self.connection is None:
|
if self.connection is None:
|
||||||
set_tz = True
|
set_tz = True
|
||||||
if settings.DATABASE_NAME == '':
|
if settings.DATABASE_NAME == '':
|
||||||
from django.core.exceptions import ImproperlyConfigured
|
from django.core.exceptions import ImproperlyConfigured
|
||||||
raise ImproperlyConfigured, "You need to specify DATABASE_NAME in your Django settings file."
|
raise ImproperlyConfigured("You need to specify DATABASE_NAME in your Django settings file.")
|
||||||
conn_string = "dbname=%s" % settings.DATABASE_NAME
|
conn_string = "dbname=%s" % settings.DATABASE_NAME
|
||||||
if settings.DATABASE_USER:
|
if settings.DATABASE_USER:
|
||||||
conn_string = "user=%s %s" % (settings.DATABASE_USER, conn_string)
|
conn_string = "user=%s %s" % (settings.DATABASE_USER, conn_string)
|
||||||
@ -94,186 +102,11 @@ class DatabaseWrapper(local):
|
|||||||
cursor.execute("SET TIME ZONE %s", [settings.TIME_ZONE])
|
cursor.execute("SET TIME ZONE %s", [settings.TIME_ZONE])
|
||||||
cursor.execute("SET client_encoding to 'UNICODE'")
|
cursor.execute("SET client_encoding to 'UNICODE'")
|
||||||
cursor = UnicodeCursorWrapper(cursor, 'utf-8')
|
cursor = UnicodeCursorWrapper(cursor, 'utf-8')
|
||||||
global postgres_version
|
if self.ops.postgres_version is None:
|
||||||
if not postgres_version:
|
|
||||||
cursor.execute("SELECT version()")
|
cursor.execute("SELECT version()")
|
||||||
postgres_version = [int(val) for val in cursor.fetchone()[0].split()[1].split('.')]
|
self.ops.postgres_version = [int(val) for val in cursor.fetchone()[0].split()[1].split('.')]
|
||||||
if settings.DEBUG:
|
|
||||||
return util.CursorDebugWrapper(cursor, self)
|
|
||||||
return cursor
|
return cursor
|
||||||
|
|
||||||
def _commit(self):
|
|
||||||
if self.connection is not None:
|
|
||||||
return self.connection.commit()
|
|
||||||
|
|
||||||
def _rollback(self):
|
|
||||||
if self.connection is not None:
|
|
||||||
return self.connection.rollback()
|
|
||||||
|
|
||||||
def close(self):
|
|
||||||
if self.connection is not None:
|
|
||||||
self.connection.close()
|
|
||||||
self.connection = None
|
|
||||||
|
|
||||||
allows_group_by_ordinal = True
|
|
||||||
allows_unique_and_pk = True
|
|
||||||
autoindexes_primary_keys = True
|
|
||||||
needs_datetime_string_cast = True
|
|
||||||
needs_upper_for_iops = False
|
|
||||||
supports_constraints = True
|
|
||||||
supports_tablespaces = False
|
|
||||||
uses_case_insensitive_names = False
|
|
||||||
|
|
||||||
def quote_name(name):
|
|
||||||
if name.startswith('"') and name.endswith('"'):
|
|
||||||
return name # Quoting once is enough.
|
|
||||||
return '"%s"' % name
|
|
||||||
|
|
||||||
def dictfetchone(cursor):
|
|
||||||
"Returns a row from the cursor as a dict"
|
|
||||||
return cursor.dictfetchone()
|
|
||||||
|
|
||||||
def dictfetchmany(cursor, number):
|
|
||||||
"Returns a certain number of rows from a cursor as a dict"
|
|
||||||
return cursor.dictfetchmany(number)
|
|
||||||
|
|
||||||
def dictfetchall(cursor):
|
|
||||||
"Returns all rows from a cursor as a dict"
|
|
||||||
return cursor.dictfetchall()
|
|
||||||
|
|
||||||
def get_last_insert_id(cursor, table_name, pk_name):
|
|
||||||
cursor.execute("SELECT CURRVAL('\"%s_%s_seq\"')" % (table_name, pk_name))
|
|
||||||
return cursor.fetchone()[0]
|
|
||||||
|
|
||||||
def get_date_extract_sql(lookup_type, table_name):
|
|
||||||
# lookup_type is 'year', 'month', 'day'
|
|
||||||
# http://www.postgresql.org/docs/8.0/static/functions-datetime.html#FUNCTIONS-DATETIME-EXTRACT
|
|
||||||
return "EXTRACT('%s' FROM %s)" % (lookup_type, table_name)
|
|
||||||
|
|
||||||
def get_date_trunc_sql(lookup_type, field_name):
|
|
||||||
# lookup_type is 'year', 'month', 'day'
|
|
||||||
# http://www.postgresql.org/docs/8.0/static/functions-datetime.html#FUNCTIONS-DATETIME-TRUNC
|
|
||||||
return "DATE_TRUNC('%s', %s)" % (lookup_type, field_name)
|
|
||||||
|
|
||||||
def get_datetime_cast_sql():
|
|
||||||
return None
|
|
||||||
|
|
||||||
def get_limit_offset_sql(limit, offset=None):
|
|
||||||
sql = "LIMIT %s" % limit
|
|
||||||
if offset and offset != 0:
|
|
||||||
sql += " OFFSET %s" % offset
|
|
||||||
return sql
|
|
||||||
|
|
||||||
def get_random_function_sql():
|
|
||||||
return "RANDOM()"
|
|
||||||
|
|
||||||
def get_deferrable_sql():
|
|
||||||
return " DEFERRABLE INITIALLY DEFERRED"
|
|
||||||
|
|
||||||
def get_fulltext_search_sql(field_name):
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
def get_drop_foreignkey_sql():
|
|
||||||
return "DROP CONSTRAINT"
|
|
||||||
|
|
||||||
def get_pk_default_value():
|
|
||||||
return "DEFAULT"
|
|
||||||
|
|
||||||
def get_max_name_length():
|
|
||||||
return None
|
|
||||||
|
|
||||||
def get_start_transaction_sql():
|
|
||||||
return "BEGIN;"
|
|
||||||
|
|
||||||
def get_autoinc_sql(table):
|
|
||||||
return None
|
|
||||||
|
|
||||||
def get_sql_flush(style, tables, sequences):
|
|
||||||
"""Return a list of SQL statements required to remove all data from
|
|
||||||
all tables in the database (without actually removing the tables
|
|
||||||
themselves) and put the database in an empty 'initial' state
|
|
||||||
|
|
||||||
"""
|
|
||||||
if tables:
|
|
||||||
if postgres_version[0] >= 8 and postgres_version[1] >= 1:
|
|
||||||
# Postgres 8.1+ can do 'TRUNCATE x, y, z...;'. In fact, it *has to*
|
|
||||||
# in order to be able to truncate tables referenced by a foreign
|
|
||||||
# key in any other table. The result is a single SQL TRUNCATE
|
|
||||||
# statement.
|
|
||||||
sql = ['%s %s;' % \
|
|
||||||
(style.SQL_KEYWORD('TRUNCATE'),
|
|
||||||
style.SQL_FIELD(', '.join([quote_name(table) for table in tables]))
|
|
||||||
)]
|
|
||||||
else:
|
|
||||||
# Older versions of Postgres can't do TRUNCATE in a single call, so
|
|
||||||
# they must use a simple delete.
|
|
||||||
sql = ['%s %s %s;' % \
|
|
||||||
(style.SQL_KEYWORD('DELETE'),
|
|
||||||
style.SQL_KEYWORD('FROM'),
|
|
||||||
style.SQL_FIELD(quote_name(table))
|
|
||||||
) for table in tables]
|
|
||||||
|
|
||||||
# 'ALTER SEQUENCE sequence_name RESTART WITH 1;'... style SQL statements
|
|
||||||
# to reset sequence indices
|
|
||||||
for sequence_info in sequences:
|
|
||||||
table_name = sequence_info['table']
|
|
||||||
column_name = sequence_info['column']
|
|
||||||
if column_name and len(column_name)>0:
|
|
||||||
# sequence name in this case will be <table>_<column>_seq
|
|
||||||
sql.append("%s %s %s %s %s %s;" % \
|
|
||||||
(style.SQL_KEYWORD('ALTER'),
|
|
||||||
style.SQL_KEYWORD('SEQUENCE'),
|
|
||||||
style.SQL_FIELD(quote_name('%s_%s_seq' % (table_name, column_name))),
|
|
||||||
style.SQL_KEYWORD('RESTART'),
|
|
||||||
style.SQL_KEYWORD('WITH'),
|
|
||||||
style.SQL_FIELD('1')
|
|
||||||
)
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
# sequence name in this case will be <table>_id_seq
|
|
||||||
sql.append("%s %s %s %s %s %s;" % \
|
|
||||||
(style.SQL_KEYWORD('ALTER'),
|
|
||||||
style.SQL_KEYWORD('SEQUENCE'),
|
|
||||||
style.SQL_FIELD(quote_name('%s_id_seq' % table_name)),
|
|
||||||
style.SQL_KEYWORD('RESTART'),
|
|
||||||
style.SQL_KEYWORD('WITH'),
|
|
||||||
style.SQL_FIELD('1')
|
|
||||||
)
|
|
||||||
)
|
|
||||||
return sql
|
|
||||||
else:
|
|
||||||
return []
|
|
||||||
|
|
||||||
def get_sql_sequence_reset(style, model_list):
|
|
||||||
"Returns a list of the SQL statements to reset sequences for the given models."
|
|
||||||
from django.db import models
|
|
||||||
output = []
|
|
||||||
for model in 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.
|
|
||||||
for f in model._meta.fields:
|
|
||||||
if isinstance(f, models.AutoField):
|
|
||||||
output.append("%s setval('%s', coalesce(max(%s), 1), max(%s) %s null) %s %s;" % \
|
|
||||||
(style.SQL_KEYWORD('SELECT'),
|
|
||||||
style.SQL_FIELD(quote_name('%s_%s_seq' % (model._meta.db_table, f.column))),
|
|
||||||
style.SQL_FIELD(quote_name(f.column)),
|
|
||||||
style.SQL_FIELD(quote_name(f.column)),
|
|
||||||
style.SQL_KEYWORD('IS NOT'),
|
|
||||||
style.SQL_KEYWORD('FROM'),
|
|
||||||
style.SQL_TABLE(quote_name(model._meta.db_table))))
|
|
||||||
break # Only one AutoField is allowed per model, so don't bother continuing.
|
|
||||||
for f in model._meta.many_to_many:
|
|
||||||
output.append("%s setval('%s', coalesce(max(%s), 1), max(%s) %s null) %s %s;" % \
|
|
||||||
(style.SQL_KEYWORD('SELECT'),
|
|
||||||
style.SQL_FIELD(quote_name('%s_id_seq' % f.m2m_db_table())),
|
|
||||||
style.SQL_FIELD(quote_name('id')),
|
|
||||||
style.SQL_FIELD(quote_name('id')),
|
|
||||||
style.SQL_KEYWORD('IS NOT'),
|
|
||||||
style.SQL_KEYWORD('FROM'),
|
|
||||||
style.SQL_TABLE(f.m2m_db_table())))
|
|
||||||
return output
|
|
||||||
|
|
||||||
def typecast_string(s):
|
def typecast_string(s):
|
||||||
"""
|
"""
|
||||||
Cast all returned strings to unicode strings.
|
Cast all returned strings to unicode strings.
|
||||||
@ -288,26 +121,9 @@ def typecast_string(s):
|
|||||||
try:
|
try:
|
||||||
Database.register_type(Database.new_type((1082,), "DATE", util.typecast_date))
|
Database.register_type(Database.new_type((1082,), "DATE", util.typecast_date))
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
raise Exception, "You appear to be using psycopg version 2. Set your DATABASE_ENGINE to 'postgresql_psycopg2' instead of 'postgresql'."
|
raise Exception("You appear to be using psycopg version 2. Set your DATABASE_ENGINE to 'postgresql_psycopg2' instead of 'postgresql'.")
|
||||||
Database.register_type(Database.new_type((1083,1266), "TIME", util.typecast_time))
|
Database.register_type(Database.new_type((1083,1266), "TIME", util.typecast_time))
|
||||||
Database.register_type(Database.new_type((1114,1184), "TIMESTAMP", util.typecast_timestamp))
|
Database.register_type(Database.new_type((1114,1184), "TIMESTAMP", util.typecast_timestamp))
|
||||||
Database.register_type(Database.new_type((16,), "BOOLEAN", util.typecast_boolean))
|
Database.register_type(Database.new_type((16,), "BOOLEAN", util.typecast_boolean))
|
||||||
Database.register_type(Database.new_type((1700,), "NUMERIC", util.typecast_decimal))
|
Database.register_type(Database.new_type((1700,), "NUMERIC", util.typecast_decimal))
|
||||||
Database.register_type(Database.new_type(Database.types[1043].values, 'STRING', typecast_string))
|
Database.register_type(Database.new_type(Database.types[1043].values, 'STRING', typecast_string))
|
||||||
|
|
||||||
OPERATOR_MAPPING = {
|
|
||||||
'exact': '= %s',
|
|
||||||
'iexact': 'ILIKE %s',
|
|
||||||
'contains': 'LIKE %s',
|
|
||||||
'icontains': 'ILIKE %s',
|
|
||||||
'regex': '~ %s',
|
|
||||||
'iregex': '~* %s',
|
|
||||||
'gt': '> %s',
|
|
||||||
'gte': '>= %s',
|
|
||||||
'lt': '< %s',
|
|
||||||
'lte': '<= %s',
|
|
||||||
'startswith': 'LIKE %s',
|
|
||||||
'endswith': 'LIKE %s',
|
|
||||||
'istartswith': 'ILIKE %s',
|
|
||||||
'iendswith': 'ILIKE %s',
|
|
||||||
}
|
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
from django.db.backends.postgresql.base import quote_name
|
from django.db.backends.postgresql.base import DatabaseOperations
|
||||||
|
|
||||||
|
quote_name = DatabaseOperations().quote_name
|
||||||
|
|
||||||
def get_table_list(cursor):
|
def get_table_list(cursor):
|
||||||
"Returns a list of table names in the current database."
|
"Returns a list of table names in the current database."
|
||||||
|
109
django/db/backends/postgresql/operations.py
Normal file
109
django/db/backends/postgresql/operations.py
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
from django.db.backends import BaseDatabaseOperations
|
||||||
|
|
||||||
|
# This DatabaseOperations class lives in here instead of base.py because it's
|
||||||
|
# used by both the 'postgresql' and 'postgresql_psycopg2' backends.
|
||||||
|
|
||||||
|
class DatabaseOperations(BaseDatabaseOperations):
|
||||||
|
def __init__(self, postgres_version=None):
|
||||||
|
self.postgres_version = postgres_version
|
||||||
|
|
||||||
|
def date_extract_sql(self, lookup_type, field_name):
|
||||||
|
# http://www.postgresql.org/docs/8.0/static/functions-datetime.html#FUNCTIONS-DATETIME-EXTRACT
|
||||||
|
return "EXTRACT('%s' FROM %s)" % (lookup_type, field_name)
|
||||||
|
|
||||||
|
def date_trunc_sql(self, lookup_type, field_name):
|
||||||
|
# http://www.postgresql.org/docs/8.0/static/functions-datetime.html#FUNCTIONS-DATETIME-TRUNC
|
||||||
|
return "DATE_TRUNC('%s', %s)" % (lookup_type, field_name)
|
||||||
|
|
||||||
|
def deferrable_sql(self):
|
||||||
|
return " DEFERRABLE INITIALLY DEFERRED"
|
||||||
|
|
||||||
|
def last_insert_id(self, cursor, table_name, pk_name):
|
||||||
|
cursor.execute("SELECT CURRVAL('\"%s_%s_seq\"')" % (table_name, pk_name))
|
||||||
|
return cursor.fetchone()[0]
|
||||||
|
|
||||||
|
def quote_name(self, name):
|
||||||
|
if name.startswith('"') and name.endswith('"'):
|
||||||
|
return name # Quoting once is enough.
|
||||||
|
return '"%s"' % name
|
||||||
|
|
||||||
|
def sql_flush(self, style, tables, sequences):
|
||||||
|
if tables:
|
||||||
|
if self.postgres_version[0] >= 8 and self.postgres_version[1] >= 1:
|
||||||
|
# Postgres 8.1+ can do 'TRUNCATE x, y, z...;'. In fact, it *has to*
|
||||||
|
# in order to be able to truncate tables referenced by a foreign
|
||||||
|
# key in any other table. The result is a single SQL TRUNCATE
|
||||||
|
# statement.
|
||||||
|
sql = ['%s %s;' % \
|
||||||
|
(style.SQL_KEYWORD('TRUNCATE'),
|
||||||
|
style.SQL_FIELD(', '.join([self.quote_name(table) for table in tables]))
|
||||||
|
)]
|
||||||
|
else:
|
||||||
|
# Older versions of Postgres can't do TRUNCATE in a single call, so
|
||||||
|
# they must use a simple delete.
|
||||||
|
sql = ['%s %s %s;' % \
|
||||||
|
(style.SQL_KEYWORD('DELETE'),
|
||||||
|
style.SQL_KEYWORD('FROM'),
|
||||||
|
style.SQL_FIELD(self.quote_name(table))
|
||||||
|
) for table in tables]
|
||||||
|
|
||||||
|
# 'ALTER SEQUENCE sequence_name RESTART WITH 1;'... style SQL statements
|
||||||
|
# to reset sequence indices
|
||||||
|
for sequence_info in sequences:
|
||||||
|
table_name = sequence_info['table']
|
||||||
|
column_name = sequence_info['column']
|
||||||
|
if column_name and len(column_name)>0:
|
||||||
|
# sequence name in this case will be <table>_<column>_seq
|
||||||
|
sql.append("%s %s %s %s %s %s;" % \
|
||||||
|
(style.SQL_KEYWORD('ALTER'),
|
||||||
|
style.SQL_KEYWORD('SEQUENCE'),
|
||||||
|
style.SQL_FIELD(self.quote_name('%s_%s_seq' % (table_name, column_name))),
|
||||||
|
style.SQL_KEYWORD('RESTART'),
|
||||||
|
style.SQL_KEYWORD('WITH'),
|
||||||
|
style.SQL_FIELD('1')
|
||||||
|
)
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
# sequence name in this case will be <table>_id_seq
|
||||||
|
sql.append("%s %s %s %s %s %s;" % \
|
||||||
|
(style.SQL_KEYWORD('ALTER'),
|
||||||
|
style.SQL_KEYWORD('SEQUENCE'),
|
||||||
|
style.SQL_FIELD(self.quote_name('%s_id_seq' % table_name)),
|
||||||
|
style.SQL_KEYWORD('RESTART'),
|
||||||
|
style.SQL_KEYWORD('WITH'),
|
||||||
|
style.SQL_FIELD('1')
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return sql
|
||||||
|
else:
|
||||||
|
return []
|
||||||
|
|
||||||
|
def sequence_reset_sql(self, style, model_list):
|
||||||
|
from django.db import models
|
||||||
|
output = []
|
||||||
|
qn = self.quote_name
|
||||||
|
for model in 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.
|
||||||
|
for f in model._meta.fields:
|
||||||
|
if isinstance(f, models.AutoField):
|
||||||
|
output.append("%s setval('%s', coalesce(max(%s), 1), max(%s) %s null) %s %s;" % \
|
||||||
|
(style.SQL_KEYWORD('SELECT'),
|
||||||
|
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'),
|
||||||
|
style.SQL_KEYWORD('FROM'),
|
||||||
|
style.SQL_TABLE(qn(model._meta.db_table))))
|
||||||
|
break # Only one AutoField is allowed per model, so don't bother continuing.
|
||||||
|
for f in model._meta.many_to_many:
|
||||||
|
output.append("%s setval('%s', coalesce(max(%s), 1), max(%s) %s null) %s %s;" % \
|
||||||
|
(style.SQL_KEYWORD('SELECT'),
|
||||||
|
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'),
|
||||||
|
style.SQL_KEYWORD('FROM'),
|
||||||
|
style.SQL_TABLE(f.m2m_db_table())))
|
||||||
|
return output
|
@ -4,42 +4,50 @@ PostgreSQL database backend for Django.
|
|||||||
Requires psycopg 2: http://initd.org/projects/psycopg2
|
Requires psycopg 2: http://initd.org/projects/psycopg2
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from django.db.backends import util
|
from django.db.backends import BaseDatabaseWrapper, BaseDatabaseFeatures
|
||||||
|
from django.db.backends.postgresql.operations import DatabaseOperations
|
||||||
try:
|
try:
|
||||||
import psycopg2 as Database
|
import psycopg2 as Database
|
||||||
import psycopg2.extensions
|
import psycopg2.extensions
|
||||||
except ImportError, e:
|
except ImportError, e:
|
||||||
from django.core.exceptions import ImproperlyConfigured
|
from django.core.exceptions import ImproperlyConfigured
|
||||||
raise ImproperlyConfigured, "Error loading psycopg2 module: %s" % e
|
raise ImproperlyConfigured("Error loading psycopg2 module: %s" % e)
|
||||||
|
|
||||||
DatabaseError = Database.DatabaseError
|
DatabaseError = Database.DatabaseError
|
||||||
IntegrityError = Database.IntegrityError
|
IntegrityError = Database.IntegrityError
|
||||||
|
|
||||||
try:
|
|
||||||
# Only exists in Python 2.4+
|
|
||||||
from threading import local
|
|
||||||
except ImportError:
|
|
||||||
# Import copy of _thread_local.py from Python 2.4
|
|
||||||
from django.utils._threading_local import local
|
|
||||||
|
|
||||||
psycopg2.extensions.register_type(psycopg2.extensions.UNICODE)
|
psycopg2.extensions.register_type(psycopg2.extensions.UNICODE)
|
||||||
|
|
||||||
postgres_version = None
|
class DatabaseFeatures(BaseDatabaseFeatures):
|
||||||
|
needs_datetime_string_cast = False
|
||||||
|
|
||||||
class DatabaseWrapper(local):
|
class DatabaseWrapper(BaseDatabaseWrapper):
|
||||||
def __init__(self, **kwargs):
|
features = DatabaseFeatures()
|
||||||
self.connection = None
|
ops = DatabaseOperations()
|
||||||
self.queries = []
|
operators = {
|
||||||
self.options = kwargs
|
'exact': '= %s',
|
||||||
|
'iexact': 'ILIKE %s',
|
||||||
|
'contains': 'LIKE %s',
|
||||||
|
'icontains': 'ILIKE %s',
|
||||||
|
'regex': '~ %s',
|
||||||
|
'iregex': '~* %s',
|
||||||
|
'gt': '> %s',
|
||||||
|
'gte': '>= %s',
|
||||||
|
'lt': '< %s',
|
||||||
|
'lte': '<= %s',
|
||||||
|
'startswith': 'LIKE %s',
|
||||||
|
'endswith': 'LIKE %s',
|
||||||
|
'istartswith': 'ILIKE %s',
|
||||||
|
'iendswith': 'ILIKE %s',
|
||||||
|
}
|
||||||
|
|
||||||
def cursor(self):
|
def _cursor(self, settings):
|
||||||
from django.conf import settings
|
|
||||||
set_tz = False
|
set_tz = False
|
||||||
if self.connection is None:
|
if self.connection is None:
|
||||||
set_tz = True
|
set_tz = True
|
||||||
if settings.DATABASE_NAME == '':
|
if settings.DATABASE_NAME == '':
|
||||||
from django.core.exceptions import ImproperlyConfigured
|
from django.core.exceptions import ImproperlyConfigured
|
||||||
raise ImproperlyConfigured, "You need to specify DATABASE_NAME in your Django settings file."
|
raise ImproperlyConfigured("You need to specify DATABASE_NAME in your Django settings file.")
|
||||||
conn_string = "dbname=%s" % settings.DATABASE_NAME
|
conn_string = "dbname=%s" % settings.DATABASE_NAME
|
||||||
if settings.DATABASE_USER:
|
if settings.DATABASE_USER:
|
||||||
conn_string = "user=%s %s" % (settings.DATABASE_USER, conn_string)
|
conn_string = "user=%s %s" % (settings.DATABASE_USER, conn_string)
|
||||||
@ -56,187 +64,7 @@ class DatabaseWrapper(local):
|
|||||||
cursor.tzinfo_factory = None
|
cursor.tzinfo_factory = None
|
||||||
if set_tz:
|
if set_tz:
|
||||||
cursor.execute("SET TIME ZONE %s", [settings.TIME_ZONE])
|
cursor.execute("SET TIME ZONE %s", [settings.TIME_ZONE])
|
||||||
global postgres_version
|
if self.ops.postgres_version is None:
|
||||||
if not postgres_version:
|
|
||||||
cursor.execute("SELECT version()")
|
cursor.execute("SELECT version()")
|
||||||
postgres_version = [int(val) for val in cursor.fetchone()[0].split()[1].split('.')]
|
self.ops.postgres_version = [int(val) for val in cursor.fetchone()[0].split()[1].split('.')]
|
||||||
if settings.DEBUG:
|
|
||||||
return util.CursorDebugWrapper(cursor, self)
|
|
||||||
return cursor
|
return cursor
|
||||||
|
|
||||||
def _commit(self):
|
|
||||||
if self.connection is not None:
|
|
||||||
return self.connection.commit()
|
|
||||||
|
|
||||||
def _rollback(self):
|
|
||||||
if self.connection is not None:
|
|
||||||
return self.connection.rollback()
|
|
||||||
|
|
||||||
def close(self):
|
|
||||||
if self.connection is not None:
|
|
||||||
self.connection.close()
|
|
||||||
self.connection = None
|
|
||||||
|
|
||||||
allows_group_by_ordinal = True
|
|
||||||
allows_unique_and_pk = True
|
|
||||||
autoindexes_primary_keys = True
|
|
||||||
needs_datetime_string_cast = False
|
|
||||||
needs_upper_for_iops = False
|
|
||||||
supports_constraints = True
|
|
||||||
supports_tablespaces = False
|
|
||||||
uses_case_insensitive_names = False
|
|
||||||
|
|
||||||
def quote_name(name):
|
|
||||||
if name.startswith('"') and name.endswith('"'):
|
|
||||||
return name # Quoting once is enough.
|
|
||||||
return '"%s"' % name
|
|
||||||
|
|
||||||
dictfetchone = util.dictfetchone
|
|
||||||
dictfetchmany = util.dictfetchmany
|
|
||||||
dictfetchall = util.dictfetchall
|
|
||||||
|
|
||||||
def get_last_insert_id(cursor, table_name, pk_name):
|
|
||||||
cursor.execute("SELECT CURRVAL('\"%s_%s_seq\"')" % (table_name, pk_name))
|
|
||||||
return cursor.fetchone()[0]
|
|
||||||
|
|
||||||
def get_date_extract_sql(lookup_type, table_name):
|
|
||||||
# lookup_type is 'year', 'month', 'day'
|
|
||||||
# http://www.postgresql.org/docs/8.0/static/functions-datetime.html#FUNCTIONS-DATETIME-EXTRACT
|
|
||||||
return "EXTRACT('%s' FROM %s)" % (lookup_type, table_name)
|
|
||||||
|
|
||||||
def get_date_trunc_sql(lookup_type, field_name):
|
|
||||||
# lookup_type is 'year', 'month', 'day'
|
|
||||||
# http://www.postgresql.org/docs/8.0/static/functions-datetime.html#FUNCTIONS-DATETIME-TRUNC
|
|
||||||
return "DATE_TRUNC('%s', %s)" % (lookup_type, field_name)
|
|
||||||
|
|
||||||
def get_datetime_cast_sql():
|
|
||||||
return None
|
|
||||||
|
|
||||||
def get_limit_offset_sql(limit, offset=None):
|
|
||||||
sql = "LIMIT %s" % limit
|
|
||||||
if offset and offset != 0:
|
|
||||||
sql += " OFFSET %s" % offset
|
|
||||||
return sql
|
|
||||||
|
|
||||||
def get_random_function_sql():
|
|
||||||
return "RANDOM()"
|
|
||||||
|
|
||||||
def get_deferrable_sql():
|
|
||||||
return " DEFERRABLE INITIALLY DEFERRED"
|
|
||||||
|
|
||||||
def get_fulltext_search_sql(field_name):
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
def get_drop_foreignkey_sql():
|
|
||||||
return "DROP CONSTRAINT"
|
|
||||||
|
|
||||||
def get_pk_default_value():
|
|
||||||
return "DEFAULT"
|
|
||||||
|
|
||||||
def get_max_name_length():
|
|
||||||
return None
|
|
||||||
|
|
||||||
def get_start_transaction_sql():
|
|
||||||
return "BEGIN;"
|
|
||||||
|
|
||||||
def get_autoinc_sql(table):
|
|
||||||
return None
|
|
||||||
|
|
||||||
def get_sql_flush(style, tables, sequences):
|
|
||||||
"""Return a list of SQL statements required to remove all data from
|
|
||||||
all tables in the database (without actually removing the tables
|
|
||||||
themselves) and put the database in an empty 'initial' state
|
|
||||||
"""
|
|
||||||
if tables:
|
|
||||||
if postgres_version[0] >= 8 and postgres_version[1] >= 1:
|
|
||||||
# Postgres 8.1+ can do 'TRUNCATE x, y, z...;'. In fact, it *has to* in order to be able to
|
|
||||||
# truncate tables referenced by a foreign key in any other table. The result is a
|
|
||||||
# single SQL TRUNCATE statement
|
|
||||||
sql = ['%s %s;' % \
|
|
||||||
(style.SQL_KEYWORD('TRUNCATE'),
|
|
||||||
style.SQL_FIELD(', '.join([quote_name(table) for table in tables]))
|
|
||||||
)]
|
|
||||||
else:
|
|
||||||
sql = ['%s %s %s;' % \
|
|
||||||
(style.SQL_KEYWORD('DELETE'),
|
|
||||||
style.SQL_KEYWORD('FROM'),
|
|
||||||
style.SQL_FIELD(quote_name(table))
|
|
||||||
) for table in tables]
|
|
||||||
|
|
||||||
# 'ALTER SEQUENCE sequence_name RESTART WITH 1;'... style SQL statements
|
|
||||||
# to reset sequence indices
|
|
||||||
for sequence in sequences:
|
|
||||||
table_name = sequence['table']
|
|
||||||
column_name = sequence['column']
|
|
||||||
if column_name and len(column_name) > 0:
|
|
||||||
# sequence name in this case will be <table>_<column>_seq
|
|
||||||
sql.append("%s %s %s %s %s %s;" % \
|
|
||||||
(style.SQL_KEYWORD('ALTER'),
|
|
||||||
style.SQL_KEYWORD('SEQUENCE'),
|
|
||||||
style.SQL_FIELD(quote_name('%s_%s_seq' % (table_name, column_name))),
|
|
||||||
style.SQL_KEYWORD('RESTART'),
|
|
||||||
style.SQL_KEYWORD('WITH'),
|
|
||||||
style.SQL_FIELD('1')
|
|
||||||
)
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
# sequence name in this case will be <table>_id_seq
|
|
||||||
sql.append("%s %s %s %s %s %s;" % \
|
|
||||||
(style.SQL_KEYWORD('ALTER'),
|
|
||||||
style.SQL_KEYWORD('SEQUENCE'),
|
|
||||||
style.SQL_FIELD(quote_name('%s_id_seq' % table_name)),
|
|
||||||
style.SQL_KEYWORD('RESTART'),
|
|
||||||
style.SQL_KEYWORD('WITH'),
|
|
||||||
style.SQL_FIELD('1')
|
|
||||||
)
|
|
||||||
)
|
|
||||||
return sql
|
|
||||||
else:
|
|
||||||
return []
|
|
||||||
|
|
||||||
def get_sql_sequence_reset(style, model_list):
|
|
||||||
"Returns a list of the SQL statements to reset sequences for the given models."
|
|
||||||
from django.db import models
|
|
||||||
output = []
|
|
||||||
for model in 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.
|
|
||||||
for f in model._meta.fields:
|
|
||||||
if isinstance(f, models.AutoField):
|
|
||||||
output.append("%s setval('%s', coalesce(max(%s), 1), max(%s) %s null) %s %s;" % \
|
|
||||||
(style.SQL_KEYWORD('SELECT'),
|
|
||||||
style.SQL_FIELD(quote_name('%s_%s_seq' % (model._meta.db_table, f.column))),
|
|
||||||
style.SQL_FIELD(quote_name(f.column)),
|
|
||||||
style.SQL_FIELD(quote_name(f.column)),
|
|
||||||
style.SQL_KEYWORD('IS NOT'),
|
|
||||||
style.SQL_KEYWORD('FROM'),
|
|
||||||
style.SQL_TABLE(quote_name(model._meta.db_table))))
|
|
||||||
break # Only one AutoField is allowed per model, so don't bother continuing.
|
|
||||||
for f in model._meta.many_to_many:
|
|
||||||
output.append("%s setval('%s', coalesce(max(%s), 1), max(%s) %s null) %s %s;" % \
|
|
||||||
(style.SQL_KEYWORD('SELECT'),
|
|
||||||
style.SQL_FIELD(quote_name('%s_id_seq' % f.m2m_db_table())),
|
|
||||||
style.SQL_FIELD(quote_name('id')),
|
|
||||||
style.SQL_FIELD(quote_name('id')),
|
|
||||||
style.SQL_KEYWORD('IS NOT'),
|
|
||||||
style.SQL_KEYWORD('FROM'),
|
|
||||||
style.SQL_TABLE(f.m2m_db_table())))
|
|
||||||
return output
|
|
||||||
|
|
||||||
OPERATOR_MAPPING = {
|
|
||||||
'exact': '= %s',
|
|
||||||
'iexact': 'ILIKE %s',
|
|
||||||
'contains': 'LIKE %s',
|
|
||||||
'icontains': 'ILIKE %s',
|
|
||||||
'regex': '~ %s',
|
|
||||||
'iregex': '~* %s',
|
|
||||||
'gt': '> %s',
|
|
||||||
'gte': '>= %s',
|
|
||||||
'lt': '< %s',
|
|
||||||
'lte': '<= %s',
|
|
||||||
'startswith': 'LIKE %s',
|
|
||||||
'endswith': 'LIKE %s',
|
|
||||||
'istartswith': 'ILIKE %s',
|
|
||||||
'iendswith': 'ILIKE %s',
|
|
||||||
}
|
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
from django.db.backends.postgresql_psycopg2.base import quote_name
|
from django.db.backends.postgresql_psycopg2.base import DatabaseOperations
|
||||||
|
|
||||||
|
quote_name = DatabaseOperations().quote_name
|
||||||
|
|
||||||
def get_table_list(cursor):
|
def get_table_list(cursor):
|
||||||
"Returns a list of table names in the current database."
|
"Returns a list of table names in the current database."
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
SQLite3 backend for django. Requires pysqlite2 (http://pysqlite.org/).
|
SQLite3 backend for django. Requires pysqlite2 (http://pysqlite.org/).
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from django.db.backends import util
|
from django.db.backends import BaseDatabaseWrapper, BaseDatabaseFeatures, BaseDatabaseOperations, util
|
||||||
try:
|
try:
|
||||||
try:
|
try:
|
||||||
from sqlite3 import dbapi2 as Database
|
from sqlite3 import dbapi2 as Database
|
||||||
@ -34,21 +34,69 @@ Database.register_converter("TIMESTAMP", util.typecast_timestamp)
|
|||||||
Database.register_converter("decimal", util.typecast_decimal)
|
Database.register_converter("decimal", util.typecast_decimal)
|
||||||
Database.register_adapter(decimal.Decimal, util.rev_typecast_decimal)
|
Database.register_adapter(decimal.Decimal, util.rev_typecast_decimal)
|
||||||
|
|
||||||
try:
|
class DatabaseFeatures(BaseDatabaseFeatures):
|
||||||
# Only exists in Python 2.4+
|
supports_constraints = False
|
||||||
from threading import local
|
|
||||||
except ImportError:
|
|
||||||
# Import copy of _thread_local.py from Python 2.4
|
|
||||||
from django.utils._threading_local import local
|
|
||||||
|
|
||||||
class DatabaseWrapper(local):
|
class DatabaseOperations(BaseDatabaseOperations):
|
||||||
def __init__(self, **kwargs):
|
def date_extract_sql(self, lookup_type, field_name):
|
||||||
self.connection = None
|
# sqlite doesn't support extract, so we fake it with the user-defined
|
||||||
self.queries = []
|
# function django_extract that's registered in connect().
|
||||||
self.options = kwargs
|
return 'django_extract("%s", %s)' % (lookup_type.lower(), field_name)
|
||||||
|
|
||||||
def cursor(self):
|
def date_trunc_sql(self, lookup_type, field_name):
|
||||||
from django.conf import settings
|
# sqlite doesn't support DATE_TRUNC, so we fake it with a user-defined
|
||||||
|
# function django_date_trunc that's registered in connect().
|
||||||
|
return 'django_date_trunc("%s", %s)' % (lookup_type.lower(), field_name)
|
||||||
|
|
||||||
|
def drop_foreignkey_sql(self):
|
||||||
|
return ""
|
||||||
|
|
||||||
|
def pk_default_value(self):
|
||||||
|
return 'NULL'
|
||||||
|
|
||||||
|
def quote_name(self, name):
|
||||||
|
if name.startswith('"') and name.endswith('"'):
|
||||||
|
return name # Quoting once is enough.
|
||||||
|
return '"%s"' % name
|
||||||
|
|
||||||
|
def sql_flush(self, style, tables, sequences):
|
||||||
|
# NB: The generated SQL below is specific to SQLite
|
||||||
|
# Note: The DELETE FROM... SQL generated below works for SQLite databases
|
||||||
|
# because constraints don't exist
|
||||||
|
sql = ['%s %s %s;' % \
|
||||||
|
(style.SQL_KEYWORD('DELETE'),
|
||||||
|
style.SQL_KEYWORD('FROM'),
|
||||||
|
style.SQL_FIELD(self.quote_name(table))
|
||||||
|
) for table in tables]
|
||||||
|
# Note: No requirement for reset of auto-incremented indices (cf. other
|
||||||
|
# sql_flush() implementations). Just return SQL at this point
|
||||||
|
return sql
|
||||||
|
|
||||||
|
class DatabaseWrapper(BaseDatabaseWrapper):
|
||||||
|
features = DatabaseFeatures()
|
||||||
|
ops = DatabaseOperations()
|
||||||
|
|
||||||
|
# SQLite requires LIKE statements to include an ESCAPE clause if the value
|
||||||
|
# being escaped has a percent or underscore in it.
|
||||||
|
# See http://www.sqlite.org/lang_expr.html for an explanation.
|
||||||
|
operators = {
|
||||||
|
'exact': '= %s',
|
||||||
|
'iexact': "LIKE %s ESCAPE '\\'",
|
||||||
|
'contains': "LIKE %s ESCAPE '\\'",
|
||||||
|
'icontains': "LIKE %s ESCAPE '\\'",
|
||||||
|
'regex': 'REGEXP %s',
|
||||||
|
'iregex': "REGEXP '(?i)' || %s",
|
||||||
|
'gt': '> %s',
|
||||||
|
'gte': '>= %s',
|
||||||
|
'lt': '< %s',
|
||||||
|
'lte': '<= %s',
|
||||||
|
'startswith': "LIKE %s ESCAPE '\\'",
|
||||||
|
'endswith': "LIKE %s ESCAPE '\\'",
|
||||||
|
'istartswith': "LIKE %s ESCAPE '\\'",
|
||||||
|
'iendswith': "LIKE %s ESCAPE '\\'",
|
||||||
|
}
|
||||||
|
|
||||||
|
def _cursor(self, settings):
|
||||||
if self.connection is None:
|
if self.connection is None:
|
||||||
kwargs = {
|
kwargs = {
|
||||||
'database': settings.DATABASE_NAME,
|
'database': settings.DATABASE_NAME,
|
||||||
@ -60,28 +108,15 @@ class DatabaseWrapper(local):
|
|||||||
self.connection.create_function("django_extract", 2, _sqlite_extract)
|
self.connection.create_function("django_extract", 2, _sqlite_extract)
|
||||||
self.connection.create_function("django_date_trunc", 2, _sqlite_date_trunc)
|
self.connection.create_function("django_date_trunc", 2, _sqlite_date_trunc)
|
||||||
self.connection.create_function("regexp", 2, _sqlite_regexp)
|
self.connection.create_function("regexp", 2, _sqlite_regexp)
|
||||||
cursor = self.connection.cursor(factory=SQLiteCursorWrapper)
|
return self.connection.cursor(factory=SQLiteCursorWrapper)
|
||||||
if settings.DEBUG:
|
|
||||||
return util.CursorDebugWrapper(cursor, self)
|
|
||||||
else:
|
|
||||||
return cursor
|
|
||||||
|
|
||||||
def _commit(self):
|
|
||||||
if self.connection is not None:
|
|
||||||
self.connection.commit()
|
|
||||||
|
|
||||||
def _rollback(self):
|
|
||||||
if self.connection is not None:
|
|
||||||
self.connection.rollback()
|
|
||||||
|
|
||||||
def close(self):
|
def close(self):
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
# If database is in memory, closing the connection destroys the
|
# If database is in memory, closing the connection destroys the
|
||||||
# database. To prevent accidental data loss, ignore close requests on
|
# database. To prevent accidental data loss, ignore close requests on
|
||||||
# an in-memory db.
|
# an in-memory db.
|
||||||
if self.connection is not None and settings.DATABASE_NAME != ":memory:":
|
if settings.DATABASE_NAME != ":memory:":
|
||||||
self.connection.close()
|
BaseDatabaseWrapper.close(self)
|
||||||
self.connection = None
|
|
||||||
|
|
||||||
class SQLiteCursorWrapper(Database.Cursor):
|
class SQLiteCursorWrapper(Database.Cursor):
|
||||||
"""
|
"""
|
||||||
@ -100,33 +135,6 @@ class SQLiteCursorWrapper(Database.Cursor):
|
|||||||
def convert_query(self, query, num_params):
|
def convert_query(self, query, num_params):
|
||||||
return query % tuple("?" * num_params)
|
return query % tuple("?" * num_params)
|
||||||
|
|
||||||
allows_group_by_ordinal = True
|
|
||||||
allows_unique_and_pk = True
|
|
||||||
autoindexes_primary_keys = True
|
|
||||||
needs_datetime_string_cast = True
|
|
||||||
needs_upper_for_iops = False
|
|
||||||
supports_constraints = False
|
|
||||||
supports_tablespaces = False
|
|
||||||
uses_case_insensitive_names = False
|
|
||||||
|
|
||||||
def quote_name(name):
|
|
||||||
if name.startswith('"') and name.endswith('"'):
|
|
||||||
return name # Quoting once is enough.
|
|
||||||
return '"%s"' % name
|
|
||||||
|
|
||||||
dictfetchone = util.dictfetchone
|
|
||||||
dictfetchmany = util.dictfetchmany
|
|
||||||
dictfetchall = util.dictfetchall
|
|
||||||
|
|
||||||
def get_last_insert_id(cursor, table_name, pk_name):
|
|
||||||
return cursor.lastrowid
|
|
||||||
|
|
||||||
def get_date_extract_sql(lookup_type, table_name):
|
|
||||||
# lookup_type is 'year', 'month', 'day'
|
|
||||||
# sqlite doesn't support extract, so we fake it with the user-defined
|
|
||||||
# function _sqlite_extract that's registered in connect(), above.
|
|
||||||
return 'django_extract("%s", %s)' % (lookup_type.lower(), table_name)
|
|
||||||
|
|
||||||
def _sqlite_extract(lookup_type, dt):
|
def _sqlite_extract(lookup_type, dt):
|
||||||
try:
|
try:
|
||||||
dt = util.typecast_timestamp(dt)
|
dt = util.typecast_timestamp(dt)
|
||||||
@ -134,67 +142,6 @@ def _sqlite_extract(lookup_type, dt):
|
|||||||
return None
|
return None
|
||||||
return str(getattr(dt, lookup_type))
|
return str(getattr(dt, lookup_type))
|
||||||
|
|
||||||
def get_date_trunc_sql(lookup_type, field_name):
|
|
||||||
# lookup_type is 'year', 'month', 'day'
|
|
||||||
# sqlite doesn't support DATE_TRUNC, so we fake it as above.
|
|
||||||
return 'django_date_trunc("%s", %s)' % (lookup_type.lower(), field_name)
|
|
||||||
|
|
||||||
def get_datetime_cast_sql():
|
|
||||||
return None
|
|
||||||
|
|
||||||
def get_limit_offset_sql(limit, offset=None):
|
|
||||||
sql = "LIMIT %s" % limit
|
|
||||||
if offset and offset != 0:
|
|
||||||
sql += " OFFSET %s" % offset
|
|
||||||
return sql
|
|
||||||
|
|
||||||
def get_random_function_sql():
|
|
||||||
return "RANDOM()"
|
|
||||||
|
|
||||||
def get_deferrable_sql():
|
|
||||||
return ""
|
|
||||||
|
|
||||||
def get_fulltext_search_sql(field_name):
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
def get_drop_foreignkey_sql():
|
|
||||||
return ""
|
|
||||||
|
|
||||||
def get_pk_default_value():
|
|
||||||
return "NULL"
|
|
||||||
|
|
||||||
def get_max_name_length():
|
|
||||||
return None
|
|
||||||
|
|
||||||
def get_start_transaction_sql():
|
|
||||||
return "BEGIN;"
|
|
||||||
|
|
||||||
def get_autoinc_sql(table):
|
|
||||||
return None
|
|
||||||
|
|
||||||
def get_sql_flush(style, tables, sequences):
|
|
||||||
"""
|
|
||||||
Return a list of SQL statements required to remove all data from
|
|
||||||
all tables in the database (without actually removing the tables
|
|
||||||
themselves) and put the database in an empty 'initial' state
|
|
||||||
"""
|
|
||||||
# NB: The generated SQL below is specific to SQLite
|
|
||||||
# Note: The DELETE FROM... SQL generated below works for SQLite databases
|
|
||||||
# because constraints don't exist
|
|
||||||
sql = ['%s %s %s;' % \
|
|
||||||
(style.SQL_KEYWORD('DELETE'),
|
|
||||||
style.SQL_KEYWORD('FROM'),
|
|
||||||
style.SQL_FIELD(quote_name(table))
|
|
||||||
) for table in tables]
|
|
||||||
# Note: No requirement for reset of auto-incremented indices (cf. other
|
|
||||||
# get_sql_flush() implementations). Just return SQL at this point
|
|
||||||
return sql
|
|
||||||
|
|
||||||
def get_sql_sequence_reset(style, model_list):
|
|
||||||
"Returns a list of the SQL statements to reset sequences for the given models."
|
|
||||||
# No sequence reset required
|
|
||||||
return []
|
|
||||||
|
|
||||||
def _sqlite_date_trunc(lookup_type, dt):
|
def _sqlite_date_trunc(lookup_type, dt):
|
||||||
try:
|
try:
|
||||||
dt = util.typecast_timestamp(dt)
|
dt = util.typecast_timestamp(dt)
|
||||||
@ -213,24 +160,3 @@ def _sqlite_regexp(re_pattern, re_string):
|
|||||||
return bool(re.search(re_pattern, re_string))
|
return bool(re.search(re_pattern, re_string))
|
||||||
except:
|
except:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# SQLite requires LIKE statements to include an ESCAPE clause if the value
|
|
||||||
# being escaped has a percent or underscore in it.
|
|
||||||
# See http://www.sqlite.org/lang_expr.html for an explanation.
|
|
||||||
OPERATOR_MAPPING = {
|
|
||||||
'exact': '= %s',
|
|
||||||
'iexact': "LIKE %s ESCAPE '\\'",
|
|
||||||
'contains': "LIKE %s ESCAPE '\\'",
|
|
||||||
'icontains': "LIKE %s ESCAPE '\\'",
|
|
||||||
'regex': 'REGEXP %s',
|
|
||||||
'iregex': "REGEXP '(?i)' || %s",
|
|
||||||
'gt': '> %s',
|
|
||||||
'gte': '>= %s',
|
|
||||||
'lt': '< %s',
|
|
||||||
'lte': '<= %s',
|
|
||||||
'startswith': "LIKE %s ESCAPE '\\'",
|
|
||||||
'endswith': "LIKE %s ESCAPE '\\'",
|
|
||||||
'istartswith': "LIKE %s ESCAPE '\\'",
|
|
||||||
'iendswith': "LIKE %s ESCAPE '\\'",
|
|
||||||
}
|
|
||||||
|
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
from django.db.backends.sqlite3.base import quote_name
|
from django.db.backends.sqlite3.base import DatabaseOperations
|
||||||
|
|
||||||
|
quote_name = DatabaseOperations().quote_name
|
||||||
|
|
||||||
def get_table_list(cursor):
|
def get_table_list(cursor):
|
||||||
"Returns a list of table names in the current database."
|
"Returns a list of table names in the current database."
|
||||||
|
@ -124,30 +124,3 @@ def truncate_name(name, length=None):
|
|||||||
hash = md5.md5(name).hexdigest()[:4]
|
hash = md5.md5(name).hexdigest()[:4]
|
||||||
|
|
||||||
return '%s%s' % (name[:length-4], hash)
|
return '%s%s' % (name[:length-4], hash)
|
||||||
|
|
||||||
##################################################################################
|
|
||||||
# Helper functions for dictfetch* for databases that don't natively support them #
|
|
||||||
##################################################################################
|
|
||||||
|
|
||||||
def _dict_helper(desc, row):
|
|
||||||
"Returns a dictionary for the given cursor.description and result row."
|
|
||||||
return dict(zip([col[0] for col in desc], row))
|
|
||||||
|
|
||||||
def dictfetchone(cursor):
|
|
||||||
"Returns a row from the cursor as a dict"
|
|
||||||
row = cursor.fetchone()
|
|
||||||
if not row:
|
|
||||||
return None
|
|
||||||
return _dict_helper(cursor.description, row)
|
|
||||||
|
|
||||||
def dictfetchmany(cursor, number):
|
|
||||||
"Returns a certain number of rows from a cursor as a dict"
|
|
||||||
desc = cursor.description
|
|
||||||
for row in cursor.fetchmany(number):
|
|
||||||
yield _dict_helper(desc, row)
|
|
||||||
|
|
||||||
def dictfetchall(cursor):
|
|
||||||
"Returns all rows from a cursor as a dict"
|
|
||||||
desc = cursor.description
|
|
||||||
for row in cursor.fetchall():
|
|
||||||
yield _dict_helper(desc, row)
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.core.exceptions import ObjectDoesNotExist, ImproperlyConfigured
|
from django.core.exceptions import ObjectDoesNotExist, ImproperlyConfigured
|
||||||
from django.core import validators
|
from django.core import validators
|
||||||
from django.db import backend, connection
|
from django.db import connection
|
||||||
from django.db.models.loading import get_apps, get_app, get_models, get_model, register_models
|
from django.db.models.loading import get_apps, get_app, get_models, get_model, register_models
|
||||||
from django.db.models.query import Q
|
from django.db.models.query import Q
|
||||||
from django.db.models.manager import Manager
|
from django.db.models.manager import Manager
|
||||||
|
@ -6,7 +6,7 @@ from django.db.models.fields import AutoField, ImageField, FieldDoesNotExist
|
|||||||
from django.db.models.fields.related import OneToOneRel, ManyToOneRel
|
from django.db.models.fields.related import OneToOneRel, ManyToOneRel
|
||||||
from django.db.models.query import delete_objects
|
from django.db.models.query import delete_objects
|
||||||
from django.db.models.options import Options
|
from django.db.models.options import Options
|
||||||
from django.db import connection, backend, transaction
|
from django.db import connection, transaction
|
||||||
from django.db.models import signals
|
from django.db.models import signals
|
||||||
from django.db.models.loading import register_models, get_model
|
from django.db.models.loading import register_models, get_model
|
||||||
from django.dispatch import dispatcher
|
from django.dispatch import dispatcher
|
||||||
@ -212,28 +212,32 @@ class Model(object):
|
|||||||
non_pks = [f for f in self._meta.fields if not f.primary_key]
|
non_pks = [f for f in self._meta.fields if not f.primary_key]
|
||||||
cursor = connection.cursor()
|
cursor = connection.cursor()
|
||||||
|
|
||||||
|
qn = connection.ops.quote_name
|
||||||
|
|
||||||
# First, try an UPDATE. If that doesn't update anything, do an INSERT.
|
# First, try an UPDATE. If that doesn't update anything, do an INSERT.
|
||||||
pk_val = self._get_pk_val()
|
pk_val = self._get_pk_val()
|
||||||
pk_set = bool(pk_val)
|
# Note: the comparison with '' is required for compatibility with
|
||||||
|
# oldforms-style model creation.
|
||||||
|
pk_set = pk_val is not None and pk_val != u''
|
||||||
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" % \
|
cursor.execute("SELECT 1 FROM %s WHERE %s=%%s" % \
|
||||||
(backend.quote_name(self._meta.db_table), backend.quote_name(self._meta.pk.column)),
|
(qn(self._meta.db_table), qn(self._meta.pk.column)),
|
||||||
self._meta.pk.get_db_prep_lookup('exact', pk_val))
|
self._meta.pk.get_db_prep_lookup('exact', 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(raw and getattr(self, f.attname) or f.pre_save(self, False)) for f in non_pks]
|
db_values = [f.get_db_prep_save(raw and getattr(self, f.attname) or f.pre_save(self, False)) for f in non_pks]
|
||||||
if db_values:
|
if db_values:
|
||||||
cursor.execute("UPDATE %s SET %s WHERE %s=%%s" % \
|
cursor.execute("UPDATE %s SET %s WHERE %s=%%s" % \
|
||||||
(backend.quote_name(self._meta.db_table),
|
(qn(self._meta.db_table),
|
||||||
','.join(['%s=%%s' % backend.quote_name(f.column) for f in non_pks]),
|
','.join(['%s=%%s' % qn(f.column) for f in non_pks]),
|
||||||
backend.quote_name(self._meta.pk.column)),
|
qn(self._meta.pk.column)),
|
||||||
db_values + self._meta.pk.get_db_prep_lookup('exact', pk_val))
|
db_values + self._meta.pk.get_db_prep_lookup('exact', 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 = [backend.quote_name(f.column) for f in self._meta.fields if not isinstance(f, AutoField)]
|
field_names = [qn(f.column) for f in self._meta.fields if not isinstance(f, AutoField)]
|
||||||
db_values = [f.get_db_prep_save(raw and getattr(self, f.attname) or f.pre_save(self, True)) for f in self._meta.fields if not isinstance(f, AutoField)]
|
db_values = [f.get_db_prep_save(raw and getattr(self, f.attname) or f.pre_save(self, True)) for f in self._meta.fields if not isinstance(f, AutoField)]
|
||||||
# If the PK has been manually set, respect that.
|
# If the PK has been manually set, respect that.
|
||||||
if pk_set:
|
if pk_set:
|
||||||
@ -241,23 +245,22 @@ class Model(object):
|
|||||||
db_values += [f.get_db_prep_save(raw and getattr(self, f.attname) or f.pre_save(self, True)) for f in self._meta.fields if isinstance(f, AutoField)]
|
db_values += [f.get_db_prep_save(raw and getattr(self, f.attname) or f.pre_save(self, True)) for f in self._meta.fields if isinstance(f, AutoField)]
|
||||||
placeholders = ['%s'] * len(field_names)
|
placeholders = ['%s'] * len(field_names)
|
||||||
if self._meta.order_with_respect_to:
|
if self._meta.order_with_respect_to:
|
||||||
field_names.append(backend.quote_name('_order'))
|
field_names.append(qn('_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)' % \
|
||||||
(backend.quote_name(self._meta.db_table), backend.quote_name(self._meta.order_with_respect_to.column)))
|
(qn(self._meta.db_table), qn(self._meta.order_with_respect_to.column)))
|
||||||
db_values.append(getattr(self, self._meta.order_with_respect_to.attname))
|
db_values.append(getattr(self, self._meta.order_with_respect_to.attname))
|
||||||
if db_values:
|
if db_values:
|
||||||
cursor.execute("INSERT INTO %s (%s) VALUES (%s)" % \
|
cursor.execute("INSERT INTO %s (%s) VALUES (%s)" % \
|
||||||
(backend.quote_name(self._meta.db_table), ','.join(field_names),
|
(qn(self._meta.db_table), ','.join(field_names),
|
||||||
','.join(placeholders)), db_values)
|
','.join(placeholders)), db_values)
|
||||||
else:
|
else:
|
||||||
# Create a new record with defaults for everything.
|
# Create a new record with defaults for everything.
|
||||||
cursor.execute("INSERT INTO %s (%s) VALUES (%s)" %
|
cursor.execute("INSERT INTO %s (%s) VALUES (%s)" %
|
||||||
(backend.quote_name(self._meta.db_table),
|
(qn(self._meta.db_table), qn(self._meta.pk.column),
|
||||||
backend.quote_name(self._meta.pk.column),
|
connection.ops.pk_default_value()))
|
||||||
backend.get_pk_default_value()))
|
|
||||||
if self._meta.has_auto_field and not pk_set:
|
if self._meta.has_auto_field and not pk_set:
|
||||||
setattr(self, self._meta.pk.attname, backend.get_last_insert_id(cursor, self._meta.db_table, self._meta.pk.column))
|
setattr(self, self._meta.pk.attname, connection.ops.last_insert_id(cursor, self._meta.db_table, self._meta.pk.column))
|
||||||
transaction.commit_unless_managed()
|
transaction.commit_unless_managed()
|
||||||
|
|
||||||
# Run any post-save hooks.
|
# Run any post-save hooks.
|
||||||
@ -329,10 +332,11 @@ class Model(object):
|
|||||||
return force_unicode(dict(field.choices).get(value, value), strings_only=True)
|
return force_unicode(dict(field.choices).get(value, value), strings_only=True)
|
||||||
|
|
||||||
def _get_next_or_previous_by_FIELD(self, field, is_next, **kwargs):
|
def _get_next_or_previous_by_FIELD(self, field, is_next, **kwargs):
|
||||||
|
qn = connection.ops.quote_name
|
||||||
op = is_next and '>' or '<'
|
op = is_next and '>' or '<'
|
||||||
where = '(%s %s %%s OR (%s = %%s AND %s.%s %s %%s))' % \
|
where = '(%s %s %%s OR (%s = %%s AND %s.%s %s %%s))' % \
|
||||||
(backend.quote_name(field.column), op, backend.quote_name(field.column),
|
(qn(field.column), op, qn(field.column),
|
||||||
backend.quote_name(self._meta.db_table), backend.quote_name(self._meta.pk.column), op)
|
qn(self._meta.db_table), qn(self._meta.pk.column), op)
|
||||||
param = smart_str(getattr(self, field.attname))
|
param = smart_str(getattr(self, field.attname))
|
||||||
q = self.__class__._default_manager.filter(**kwargs).order_by((not is_next and '-' or '') + field.name, (not is_next and '-' or '') + self._meta.pk.name)
|
q = self.__class__._default_manager.filter(**kwargs).order_by((not is_next and '-' or '') + field.name, (not is_next and '-' or '') + self._meta.pk.name)
|
||||||
q._where.append(where)
|
q._where.append(where)
|
||||||
@ -343,14 +347,15 @@ class Model(object):
|
|||||||
raise self.DoesNotExist, "%s matching query does not exist." % self.__class__._meta.object_name
|
raise self.DoesNotExist, "%s matching query does not exist." % self.__class__._meta.object_name
|
||||||
|
|
||||||
def _get_next_or_previous_in_order(self, is_next):
|
def _get_next_or_previous_in_order(self, is_next):
|
||||||
|
qn = connection.ops.quote_name
|
||||||
cachename = "__%s_order_cache" % is_next
|
cachename = "__%s_order_cache" % is_next
|
||||||
if not hasattr(self, cachename):
|
if not hasattr(self, cachename):
|
||||||
op = is_next and '>' or '<'
|
op = is_next and '>' or '<'
|
||||||
order_field = self._meta.order_with_respect_to
|
order_field = self._meta.order_with_respect_to
|
||||||
where = ['%s %s (SELECT %s FROM %s WHERE %s=%%s)' % \
|
where = ['%s %s (SELECT %s FROM %s WHERE %s=%%s)' % \
|
||||||
(backend.quote_name('_order'), op, backend.quote_name('_order'),
|
(qn('_order'), op, qn('_order'),
|
||||||
backend.quote_name(self._meta.db_table), backend.quote_name(self._meta.pk.column)),
|
qn(self._meta.db_table), qn(self._meta.pk.column)),
|
||||||
'%s=%%s' % backend.quote_name(order_field.column)]
|
'%s=%%s' % qn(order_field.column)]
|
||||||
params = [self._get_pk_val(), getattr(self, order_field.attname)]
|
params = [self._get_pk_val(), getattr(self, order_field.attname)]
|
||||||
obj = self._default_manager.order_by('_order').extra(where=where, params=params)[:1].get()
|
obj = self._default_manager.order_by('_order').extra(where=where, params=params)[:1].get()
|
||||||
setattr(self, cachename, obj)
|
setattr(self, cachename, obj)
|
||||||
@ -432,24 +437,26 @@ class Model(object):
|
|||||||
# ORDERING METHODS #########################
|
# ORDERING METHODS #########################
|
||||||
|
|
||||||
def method_set_order(ordered_obj, self, id_list):
|
def method_set_order(ordered_obj, self, id_list):
|
||||||
|
qn = connection.ops.quote_name
|
||||||
cursor = connection.cursor()
|
cursor = connection.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 %s = %%s WHERE %s = %%s AND %s = %%s" % \
|
sql = "UPDATE %s SET %s = %%s WHERE %s = %%s AND %s = %%s" % \
|
||||||
(backend.quote_name(ordered_obj._meta.db_table), backend.quote_name('_order'),
|
(qn(ordered_obj._meta.db_table), qn('_order'),
|
||||||
backend.quote_name(ordered_obj._meta.order_with_respect_to.column),
|
qn(ordered_obj._meta.order_with_respect_to.column),
|
||||||
backend.quote_name(ordered_obj._meta.pk.column))
|
qn(ordered_obj._meta.pk.column))
|
||||||
rel_val = getattr(self, ordered_obj._meta.order_with_respect_to.rel.field_name)
|
rel_val = getattr(self, ordered_obj._meta.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)])
|
||||||
transaction.commit_unless_managed()
|
transaction.commit_unless_managed()
|
||||||
|
|
||||||
def method_get_order(ordered_obj, self):
|
def method_get_order(ordered_obj, self):
|
||||||
|
qn = connection.ops.quote_name
|
||||||
cursor = connection.cursor()
|
cursor = connection.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 %s" % \
|
sql = "SELECT %s FROM %s WHERE %s = %%s ORDER BY %s" % \
|
||||||
(backend.quote_name(ordered_obj._meta.pk.column),
|
(qn(ordered_obj._meta.pk.column),
|
||||||
backend.quote_name(ordered_obj._meta.db_table),
|
qn(ordered_obj._meta.db_table),
|
||||||
backend.quote_name(ordered_obj._meta.order_with_respect_to.column),
|
qn(ordered_obj._meta.order_with_respect_to.column),
|
||||||
backend.quote_name('_order'))
|
qn('_order'))
|
||||||
rel_val = getattr(self, ordered_obj._meta.order_with_respect_to.rel.field_name)
|
rel_val = getattr(self, ordered_obj._meta.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()]
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
from django.db import backend, transaction
|
from django.db import connection, transaction
|
||||||
from django.db.models import signals, get_model
|
from django.db.models import signals, get_model
|
||||||
from django.db.models.fields import AutoField, Field, IntegerField, PositiveIntegerField, PositiveSmallIntegerField, get_ul_class
|
from django.db.models.fields import AutoField, Field, IntegerField, PositiveIntegerField, PositiveSmallIntegerField, get_ul_class
|
||||||
from django.db.models.related import RelatedObject
|
from django.db.models.related import RelatedObject
|
||||||
@ -319,7 +319,6 @@ def create_many_related_manager(superclass):
|
|||||||
# source_col_name: the PK colname in join_table for the source object
|
# source_col_name: the PK colname in join_table for the source object
|
||||||
# target_col_name: the PK colname in join_table for the target object
|
# target_col_name: the PK colname in join_table for the target object
|
||||||
# *objs - objects to add. Either object instances, or primary keys of object instances.
|
# *objs - objects to add. Either object instances, or primary keys of object instances.
|
||||||
from django.db import connection
|
|
||||||
|
|
||||||
# If there aren't any objects, there is nothing to do.
|
# If there aren't any objects, there is nothing to do.
|
||||||
if objs:
|
if objs:
|
||||||
@ -350,7 +349,6 @@ def create_many_related_manager(superclass):
|
|||||||
# source_col_name: the PK colname in join_table for the source object
|
# source_col_name: the PK colname in join_table for the source object
|
||||||
# target_col_name: the PK colname in join_table for the target object
|
# target_col_name: the PK colname in join_table for the target object
|
||||||
# *objs - objects to remove
|
# *objs - objects to remove
|
||||||
from django.db import connection
|
|
||||||
|
|
||||||
# If there aren't any objects, there is nothing to do.
|
# If there aren't any objects, there is nothing to do.
|
||||||
if objs:
|
if objs:
|
||||||
@ -371,7 +369,6 @@ def create_many_related_manager(superclass):
|
|||||||
|
|
||||||
def _clear_items(self, source_col_name):
|
def _clear_items(self, source_col_name):
|
||||||
# source_col_name: the PK colname in join_table for the source object
|
# source_col_name: the PK colname in join_table for the source object
|
||||||
from django.db import connection
|
|
||||||
cursor = connection.cursor()
|
cursor = connection.cursor()
|
||||||
cursor.execute("DELETE FROM %s WHERE %s = %%s" % \
|
cursor.execute("DELETE FROM %s WHERE %s = %%s" % \
|
||||||
(self.join_table, source_col_name),
|
(self.join_table, source_col_name),
|
||||||
@ -400,7 +397,7 @@ class ManyRelatedObjectsDescriptor(object):
|
|||||||
superclass = rel_model._default_manager.__class__
|
superclass = rel_model._default_manager.__class__
|
||||||
RelatedManager = create_many_related_manager(superclass)
|
RelatedManager = create_many_related_manager(superclass)
|
||||||
|
|
||||||
qn = backend.quote_name
|
qn = connection.ops.quote_name
|
||||||
manager = RelatedManager(
|
manager = RelatedManager(
|
||||||
model=rel_model,
|
model=rel_model,
|
||||||
core_filters={'%s__pk' % self.related.field.name: instance._get_pk_val()},
|
core_filters={'%s__pk' % self.related.field.name: instance._get_pk_val()},
|
||||||
@ -441,7 +438,7 @@ class ReverseManyRelatedObjectsDescriptor(object):
|
|||||||
superclass = rel_model._default_manager.__class__
|
superclass = rel_model._default_manager.__class__
|
||||||
RelatedManager = create_many_related_manager(superclass)
|
RelatedManager = create_many_related_manager(superclass)
|
||||||
|
|
||||||
qn = backend.quote_name
|
qn = connection.ops.quote_name
|
||||||
manager = RelatedManager(
|
manager = RelatedManager(
|
||||||
model=rel_model,
|
model=rel_model,
|
||||||
core_filters={'%s__pk' % self.field.related_query_name(): instance._get_pk_val()},
|
core_filters={'%s__pk' % self.field.related_query_name(): instance._get_pk_val()},
|
||||||
|
@ -4,78 +4,147 @@ from django.conf import settings
|
|||||||
from django.core.exceptions import ImproperlyConfigured
|
from django.core.exceptions import ImproperlyConfigured
|
||||||
import sys
|
import sys
|
||||||
import os
|
import os
|
||||||
|
import threading
|
||||||
|
|
||||||
__all__ = ('get_apps', 'get_app', 'get_models', 'get_model', 'register_models')
|
__all__ = ('get_apps', 'get_app', 'get_models', 'get_model', 'register_models',
|
||||||
|
'load_app', 'app_cache_ready')
|
||||||
|
|
||||||
_app_list = [] # Cache of installed apps.
|
class AppCache(object):
|
||||||
# Entry is not placed in app_list cache until entire app is loaded.
|
"""
|
||||||
_app_models = {} # Dictionary of models against app label
|
A cache that stores installed applications and their models. Used to
|
||||||
# Each value is a dictionary of model name: model class
|
provide reverse-relations and for app introspection (e.g. admin).
|
||||||
# Applabel and Model entry exists in cache when individual model is loaded.
|
"""
|
||||||
_app_errors = {} # Dictionary of errors that were experienced when loading the INSTALLED_APPS
|
# Use the Borg pattern to share state between all instances. Details at
|
||||||
# Key is the app_name of the model, value is the exception that was raised
|
# http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/66531.
|
||||||
# during model loading.
|
__shared_state = dict(
|
||||||
_loaded = False # Has the contents of settings.INSTALLED_APPS been loaded?
|
# Keys of app_store are the model modules for each application.
|
||||||
# i.e., has get_apps() been called?
|
app_store = {},
|
||||||
|
|
||||||
def get_apps():
|
# Mapping of app_labels to a dictionary of model names to model code.
|
||||||
"Returns a list of all installed modules that contain models."
|
app_models = {},
|
||||||
global _app_list
|
|
||||||
global _loaded
|
# Mapping of app_labels to errors raised when trying to import the app.
|
||||||
if not _loaded:
|
app_errors = {},
|
||||||
_loaded = True
|
|
||||||
for app_name in settings.INSTALLED_APPS:
|
# -- Everything below here is only used when populating the cache --
|
||||||
|
loaded = False,
|
||||||
|
handled = {},
|
||||||
|
postponed = [],
|
||||||
|
nesting_level = 0,
|
||||||
|
write_lock = threading.RLock(),
|
||||||
|
)
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.__dict__ = self.__shared_state
|
||||||
|
|
||||||
|
def _populate(self):
|
||||||
|
"""
|
||||||
|
Fill in all the cache information. This method is threadsafe, in the
|
||||||
|
sense that every caller will see the same state upon return, and if the
|
||||||
|
cache is already initialised, it does no work.
|
||||||
|
"""
|
||||||
|
if self.loaded:
|
||||||
|
return
|
||||||
|
self.write_lock.acquire()
|
||||||
try:
|
try:
|
||||||
load_app(app_name)
|
if self.loaded:
|
||||||
|
return
|
||||||
|
for app_name in settings.INSTALLED_APPS:
|
||||||
|
if app_name in self.handled:
|
||||||
|
continue
|
||||||
|
try:
|
||||||
|
self.load_app(app_name, True)
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
# Problem importing the app
|
# Problem importing the app
|
||||||
_app_errors[app_name] = e
|
self.app_errors[app_name] = e
|
||||||
return _app_list
|
if not self.nesting_level:
|
||||||
|
for app_name in self.postponed:
|
||||||
|
self.load_app(app_name)
|
||||||
|
self.loaded = True
|
||||||
|
finally:
|
||||||
|
self.write_lock.release()
|
||||||
|
|
||||||
def get_app(app_label, emptyOK=False):
|
def load_app(self, app_name, can_postpone=False):
|
||||||
"Returns the module containing the models for the given app_label. If the app has no models in it and 'emptyOK' is True, returns None."
|
"""
|
||||||
get_apps() # Run get_apps() to populate the _app_list cache. Slightly hackish.
|
Loads the app with the provided fully qualified name, and returns the
|
||||||
|
model module.
|
||||||
|
"""
|
||||||
|
self.handled[app_name] = None
|
||||||
|
self.nesting_level += 1
|
||||||
|
mod = __import__(app_name, {}, {}, ['models'])
|
||||||
|
self.nesting_level -= 1
|
||||||
|
if not hasattr(mod, 'models'):
|
||||||
|
if can_postpone:
|
||||||
|
# Either the app has no models, or the package is still being
|
||||||
|
# imported by Python and the model module isn't available yet.
|
||||||
|
# We will check again once all the recursion has finished (in
|
||||||
|
# populate).
|
||||||
|
self.postponed.append(app_name)
|
||||||
|
return None
|
||||||
|
if mod.models not in self.app_store:
|
||||||
|
self.app_store[mod.models] = len(self.app_store)
|
||||||
|
return mod.models
|
||||||
|
|
||||||
|
def app_cache_ready(self):
|
||||||
|
"""
|
||||||
|
Returns true if the model cache is fully populated.
|
||||||
|
|
||||||
|
Useful for code that wants to cache the results of get_models() for
|
||||||
|
themselves once it is safe to do so.
|
||||||
|
"""
|
||||||
|
return self.loaded
|
||||||
|
|
||||||
|
def get_apps(self):
|
||||||
|
"Returns a list of all installed modules that contain models."
|
||||||
|
self._populate()
|
||||||
|
|
||||||
|
# Ensure the returned list is always in the same order (with new apps
|
||||||
|
# added at the end). This avoids unstable ordering on the admin app
|
||||||
|
# list page, for example.
|
||||||
|
apps = [(v, k) for k, v in self.app_store.items()]
|
||||||
|
apps.sort()
|
||||||
|
return [elt[1] for elt in apps]
|
||||||
|
|
||||||
|
def get_app(self, app_label, emptyOK=False):
|
||||||
|
"""
|
||||||
|
Returns the module containing the models for the given app_label. If
|
||||||
|
the app has no models in it and 'emptyOK' is True, returns None.
|
||||||
|
"""
|
||||||
|
self._populate()
|
||||||
|
self.write_lock.acquire()
|
||||||
|
try:
|
||||||
for app_name in settings.INSTALLED_APPS:
|
for app_name in settings.INSTALLED_APPS:
|
||||||
if app_label == app_name.split('.')[-1]:
|
if app_label == app_name.split('.')[-1]:
|
||||||
mod = load_app(app_name)
|
mod = self.load_app(app_name, False)
|
||||||
if mod is None:
|
if mod is None:
|
||||||
if emptyOK:
|
if emptyOK:
|
||||||
return None
|
return None
|
||||||
else:
|
else:
|
||||||
return mod
|
return mod
|
||||||
raise ImproperlyConfigured, "App with label %s could not be found" % app_label
|
raise ImproperlyConfigured, "App with label %s could not be found" % app_label
|
||||||
|
finally:
|
||||||
|
self.write_lock.release()
|
||||||
|
|
||||||
def load_app(app_name):
|
def get_app_errors(self):
|
||||||
"Loads the app with the provided fully qualified name, and returns the model module."
|
"Returns the map of known problems with the INSTALLED_APPS."
|
||||||
global _app_list
|
self._populate()
|
||||||
mod = __import__(app_name, {}, {}, ['models'])
|
return self.app_errors
|
||||||
if not hasattr(mod, 'models'):
|
|
||||||
return None
|
|
||||||
if mod.models not in _app_list:
|
|
||||||
_app_list.append(mod.models)
|
|
||||||
return mod.models
|
|
||||||
|
|
||||||
def get_app_errors():
|
def get_models(self, app_mod=None):
|
||||||
"Returns the map of known problems with the INSTALLED_APPS"
|
|
||||||
global _app_errors
|
|
||||||
get_apps() # Run get_apps() to populate the _app_list cache. Slightly hackish.
|
|
||||||
return _app_errors
|
|
||||||
|
|
||||||
def get_models(app_mod=None):
|
|
||||||
"""
|
"""
|
||||||
Given a module containing models, returns a list of the models. Otherwise
|
Given a module containing models, returns a list of the models.
|
||||||
returns a list of all installed models.
|
Otherwise returns a list of all installed models.
|
||||||
"""
|
"""
|
||||||
app_list = get_apps() # Run get_apps() to populate the _app_list cache. Slightly hackish.
|
self._populate()
|
||||||
if app_mod:
|
if app_mod:
|
||||||
return _app_models.get(app_mod.__name__.split('.')[-2], {}).values()
|
return self.app_models.get(app_mod.__name__.split('.')[-2], {}).values()
|
||||||
else:
|
else:
|
||||||
model_list = []
|
model_list = []
|
||||||
for app_mod in app_list:
|
for app_entry in self.app_models.itervalues():
|
||||||
model_list.extend(get_models(app_mod))
|
model_list.extend(app_entry.values())
|
||||||
return model_list
|
return model_list
|
||||||
|
|
||||||
def get_model(app_label, model_name, seed_cache=True):
|
def get_model(self, app_label, model_name, seed_cache=True):
|
||||||
"""
|
"""
|
||||||
Returns the model matching the given app_label and case-insensitive
|
Returns the model matching the given app_label and case-insensitive
|
||||||
model_name.
|
model_name.
|
||||||
@ -83,18 +152,10 @@ def get_model(app_label, model_name, seed_cache=True):
|
|||||||
Returns None if no model is found.
|
Returns None if no model is found.
|
||||||
"""
|
"""
|
||||||
if seed_cache:
|
if seed_cache:
|
||||||
get_apps()
|
self._populate()
|
||||||
try:
|
return self.app_models.get(app_label, {}).get(model_name.lower())
|
||||||
model_dict = _app_models[app_label]
|
|
||||||
except KeyError:
|
|
||||||
return None
|
|
||||||
|
|
||||||
try:
|
def register_models(self, app_label, *models):
|
||||||
return model_dict[model_name.lower()]
|
|
||||||
except KeyError:
|
|
||||||
return None
|
|
||||||
|
|
||||||
def register_models(app_label, *models):
|
|
||||||
"""
|
"""
|
||||||
Register a set of models as belonging to an app.
|
Register a set of models as belonging to an app.
|
||||||
"""
|
"""
|
||||||
@ -102,15 +163,29 @@ def register_models(app_label, *models):
|
|||||||
# Store as 'name: model' pair in a dictionary
|
# Store as 'name: model' pair in a dictionary
|
||||||
# in the _app_models dictionary
|
# in the _app_models dictionary
|
||||||
model_name = model._meta.object_name.lower()
|
model_name = model._meta.object_name.lower()
|
||||||
model_dict = _app_models.setdefault(app_label, {})
|
model_dict = self.app_models.setdefault(app_label, {})
|
||||||
if model_name in model_dict:
|
if model_name in model_dict:
|
||||||
# The same model may be imported via different paths (e.g.
|
# The same model may be imported via different paths (e.g.
|
||||||
# appname.models and project.appname.models). We use the source
|
# appname.models and project.appname.models). We use the source
|
||||||
# filename as a means to detect identity.
|
# filename as a means to detect identity.
|
||||||
fname1 = os.path.abspath(sys.modules[model.__module__].__file__)
|
fname1 = os.path.abspath(sys.modules[model.__module__].__file__)
|
||||||
fname2 = os.path.abspath(sys.modules[model_dict[model_name].__module__].__file__)
|
fname2 = os.path.abspath(sys.modules[model_dict[model_name].__module__].__file__)
|
||||||
# Since the filename extension could be .py the first time and .pyc
|
# Since the filename extension could be .py the first time and
|
||||||
# or .pyo the second time, ignore the extension when comparing.
|
# .pyc or .pyo the second time, ignore the extension when
|
||||||
|
# comparing.
|
||||||
if os.path.splitext(fname1)[0] == os.path.splitext(fname2)[0]:
|
if os.path.splitext(fname1)[0] == os.path.splitext(fname2)[0]:
|
||||||
continue
|
continue
|
||||||
model_dict[model_name] = model
|
model_dict[model_name] = model
|
||||||
|
|
||||||
|
cache = AppCache()
|
||||||
|
|
||||||
|
# These methods were always module level, so are kept that way for backwards
|
||||||
|
# compatibility.
|
||||||
|
get_apps = cache.get_apps
|
||||||
|
get_app = cache.get_app
|
||||||
|
get_app_errors = cache.get_app_errors
|
||||||
|
get_models = cache.get_models
|
||||||
|
get_model = cache.get_model
|
||||||
|
register_models = cache.register_models
|
||||||
|
load_app = cache.load_app
|
||||||
|
app_cache_ready = cache.app_cache_ready
|
||||||
|
@ -2,7 +2,7 @@ from django.conf import settings
|
|||||||
from django.db.models.related import RelatedObject
|
from django.db.models.related import RelatedObject
|
||||||
from django.db.models.fields.related import ManyToManyRel
|
from django.db.models.fields.related import ManyToManyRel
|
||||||
from django.db.models.fields import AutoField, FieldDoesNotExist
|
from django.db.models.fields import AutoField, FieldDoesNotExist
|
||||||
from django.db.models.loading import get_models
|
from django.db.models.loading import get_models, app_cache_ready
|
||||||
from django.db.models.query import orderlist2sql
|
from django.db.models.query import orderlist2sql
|
||||||
from django.utils.translation import activate, deactivate_all, get_language, string_concat
|
from django.utils.translation import activate, deactivate_all, get_language, string_concat
|
||||||
from django.utils.encoding import force_unicode, smart_str
|
from django.utils.encoding import force_unicode, smart_str
|
||||||
@ -62,7 +62,7 @@ class Options(object):
|
|||||||
del self.meta
|
del self.meta
|
||||||
|
|
||||||
def _prepare(self, model):
|
def _prepare(self, model):
|
||||||
from django.db import backend
|
from django.db import connection
|
||||||
from django.db.backends.util import truncate_name
|
from django.db.backends.util import truncate_name
|
||||||
if self.order_with_respect_to:
|
if self.order_with_respect_to:
|
||||||
self.order_with_respect_to = self.get_field(self.order_with_respect_to)
|
self.order_with_respect_to = self.get_field(self.order_with_respect_to)
|
||||||
@ -78,8 +78,7 @@ class Options(object):
|
|||||||
# If the db_table wasn't provided, use the app_label + module_name.
|
# If the db_table wasn't provided, use the app_label + module_name.
|
||||||
if not self.db_table:
|
if not self.db_table:
|
||||||
self.db_table = "%s_%s" % (self.app_label, self.module_name)
|
self.db_table = "%s_%s" % (self.app_label, self.module_name)
|
||||||
self.db_table = truncate_name(self.db_table,
|
self.db_table = truncate_name(self.db_table, connection.ops.max_name_length())
|
||||||
backend.get_max_name_length())
|
|
||||||
|
|
||||||
def add_field(self, field):
|
def add_field(self, field):
|
||||||
# Insert the given field in the order in which it was created, using
|
# Insert the given field in the order in which it was created, using
|
||||||
@ -178,6 +177,7 @@ class Options(object):
|
|||||||
for f in klass._meta.many_to_many:
|
for f in klass._meta.many_to_many:
|
||||||
if f.rel and self == f.rel.to._meta:
|
if f.rel and self == f.rel.to._meta:
|
||||||
rel_objs.append(RelatedObject(f.rel.to, klass, f))
|
rel_objs.append(RelatedObject(f.rel.to, klass, f))
|
||||||
|
if app_cache_ready():
|
||||||
self._all_related_many_to_many_objects = rel_objs
|
self._all_related_many_to_many_objects = rel_objs
|
||||||
return rel_objs
|
return rel_objs
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.db import backend, connection, transaction
|
from django.db import connection, transaction
|
||||||
from django.db.models.fields import DateField, FieldDoesNotExist
|
from django.db.models.fields import DateField, FieldDoesNotExist
|
||||||
from django.db.models import signals, loading
|
from django.db.models import signals, loading
|
||||||
from django.dispatch import dispatcher
|
from django.dispatch import dispatcher
|
||||||
@ -64,23 +64,24 @@ def orderfield2column(f, opts):
|
|||||||
return f
|
return f
|
||||||
|
|
||||||
def orderlist2sql(order_list, opts, prefix=''):
|
def orderlist2sql(order_list, opts, prefix=''):
|
||||||
|
qn = connection.ops.quote_name
|
||||||
if prefix.endswith('.'):
|
if prefix.endswith('.'):
|
||||||
prefix = backend.quote_name(prefix[:-1]) + '.'
|
prefix = qn(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, backend.quote_name(orderfield2column(f[1:], opts))))
|
output.append('%s%s DESC' % (prefix, qn(orderfield2column(f[1:], opts))))
|
||||||
elif f == '?':
|
elif f == '?':
|
||||||
output.append(backend.get_random_function_sql())
|
output.append(connection.ops.random_function_sql())
|
||||||
else:
|
else:
|
||||||
output.append('%s%s ASC' % (prefix, backend.quote_name(orderfield2column(f, opts))))
|
output.append('%s%s ASC' % (prefix, qn(orderfield2column(f, opts))))
|
||||||
return ', '.join(output)
|
return ', '.join(output)
|
||||||
|
|
||||||
def quote_only_if_word(word):
|
def quote_only_if_word(word):
|
||||||
if re.search('\W', word): # Don't quote if there are spaces or non-word chars.
|
if re.search('\W', word): # Don't quote if there are spaces or non-word chars.
|
||||||
return word
|
return word
|
||||||
else:
|
else:
|
||||||
return backend.quote_name(word)
|
return connection.ops.quote_name(word)
|
||||||
|
|
||||||
class _QuerySet(object):
|
class _QuerySet(object):
|
||||||
"Represents a lazy database lookup for a set of objects"
|
"Represents a lazy database lookup for a set of objects"
|
||||||
@ -235,8 +236,8 @@ class _QuerySet(object):
|
|||||||
|
|
||||||
cursor = connection.cursor()
|
cursor = connection.cursor()
|
||||||
if self._distinct:
|
if self._distinct:
|
||||||
id_col = "%s.%s" % (backend.quote_name(self.model._meta.db_table),
|
id_col = "%s.%s" % (connection.ops.quote_name(self.model._meta.db_table),
|
||||||
backend.quote_name(self.model._meta.pk.column))
|
connection.ops.quote_name(self.model._meta.pk.column))
|
||||||
cursor.execute("SELECT COUNT(DISTINCT(%s))" % id_col + sql, params)
|
cursor.execute("SELECT COUNT(DISTINCT(%s))" % id_col + sql, params)
|
||||||
else:
|
else:
|
||||||
cursor.execute("SELECT COUNT(*)" + sql, params)
|
cursor.execute("SELECT COUNT(*)" + sql, params)
|
||||||
@ -308,11 +309,12 @@ class _QuerySet(object):
|
|||||||
assert self._limit is None and self._offset is None, \
|
assert self._limit is None and self._offset is None, \
|
||||||
"Cannot use 'limit' or 'offset' with in_bulk"
|
"Cannot use 'limit' or 'offset' with in_bulk"
|
||||||
assert isinstance(id_list, (tuple, list)), "in_bulk() must be provided with a list of IDs."
|
assert isinstance(id_list, (tuple, list)), "in_bulk() must be provided with a list of IDs."
|
||||||
|
qn = connection.ops.quote_name
|
||||||
id_list = list(id_list)
|
id_list = list(id_list)
|
||||||
if id_list == []:
|
if id_list == []:
|
||||||
return {}
|
return {}
|
||||||
qs = self._clone()
|
qs = self._clone()
|
||||||
qs._where.append("%s.%s IN (%s)" % (backend.quote_name(self.model._meta.db_table), backend.quote_name(self.model._meta.pk.column), ",".join(['%s'] * len(id_list))))
|
qs._where.append("%s.%s IN (%s)" % (qn(self.model._meta.db_table), qn(self.model._meta.pk.column), ",".join(['%s'] * len(id_list))))
|
||||||
qs._params.extend(id_list)
|
qs._params.extend(id_list)
|
||||||
return dict([(obj._get_pk_val(), obj) for obj in qs.iterator()])
|
return dict([(obj._get_pk_val(), obj) for obj in qs.iterator()])
|
||||||
|
|
||||||
@ -481,10 +483,11 @@ class _QuerySet(object):
|
|||||||
return self._result_cache
|
return self._result_cache
|
||||||
|
|
||||||
def _get_sql_clause(self):
|
def _get_sql_clause(self):
|
||||||
|
qn = connection.ops.quote_name
|
||||||
opts = self.model._meta
|
opts = self.model._meta
|
||||||
|
|
||||||
# Construct the fundamental parts of the query: SELECT X FROM Y WHERE Z.
|
# Construct the fundamental parts of the query: SELECT X FROM Y WHERE Z.
|
||||||
select = ["%s.%s" % (backend.quote_name(opts.db_table), backend.quote_name(f.column)) for f in opts.fields]
|
select = ["%s.%s" % (qn(opts.db_table), qn(f.column)) for f in opts.fields]
|
||||||
tables = [quote_only_if_word(t) for t in self._tables]
|
tables = [quote_only_if_word(t) for t in self._tables]
|
||||||
joins = SortedDict()
|
joins = SortedDict()
|
||||||
where = self._where[:]
|
where = self._where[:]
|
||||||
@ -505,10 +508,10 @@ class _QuerySet(object):
|
|||||||
|
|
||||||
# Add any additional SELECTs.
|
# Add any additional SELECTs.
|
||||||
if self._select:
|
if self._select:
|
||||||
select.extend(['(%s) AS %s' % (quote_only_if_word(s[1]), backend.quote_name(s[0])) for s in self._select.items()])
|
select.extend(['(%s) AS %s' % (quote_only_if_word(s[1]), qn(s[0])) for s in self._select.items()])
|
||||||
|
|
||||||
# Start composing the body of the SQL statement.
|
# Start composing the body of the SQL statement.
|
||||||
sql = [" FROM", backend.quote_name(opts.db_table)]
|
sql = [" FROM", qn(opts.db_table)]
|
||||||
|
|
||||||
# Compose the join dictionary into SQL describing the joins.
|
# Compose the join dictionary into SQL describing the joins.
|
||||||
if joins:
|
if joins:
|
||||||
@ -531,7 +534,7 @@ class _QuerySet(object):
|
|||||||
ordering_to_use = opts.ordering
|
ordering_to_use = opts.ordering
|
||||||
for f in handle_legacy_orderlist(ordering_to_use):
|
for f in handle_legacy_orderlist(ordering_to_use):
|
||||||
if f == '?': # Special case.
|
if f == '?': # Special case.
|
||||||
order_by.append(backend.get_random_function_sql())
|
order_by.append(connection.ops.random_function_sql())
|
||||||
else:
|
else:
|
||||||
if f.startswith('-'):
|
if f.startswith('-'):
|
||||||
col_name = f[1:]
|
col_name = f[1:]
|
||||||
@ -541,29 +544,29 @@ class _QuerySet(object):
|
|||||||
order = "ASC"
|
order = "ASC"
|
||||||
if "." in col_name:
|
if "." in col_name:
|
||||||
table_prefix, col_name = col_name.split('.', 1)
|
table_prefix, col_name = col_name.split('.', 1)
|
||||||
table_prefix = backend.quote_name(table_prefix) + '.'
|
table_prefix = qn(table_prefix) + '.'
|
||||||
else:
|
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 (self._select or ()):
|
if "." not in col_name and col_name not in (self._select or ()):
|
||||||
table_prefix = backend.quote_name(opts.db_table) + '.'
|
table_prefix = qn(opts.db_table) + '.'
|
||||||
else:
|
else:
|
||||||
table_prefix = ''
|
table_prefix = ''
|
||||||
order_by.append('%s%s %s' % (table_prefix, backend.quote_name(orderfield2column(col_name, opts)), order))
|
order_by.append('%s%s %s' % (table_prefix, qn(orderfield2column(col_name, opts)), order))
|
||||||
if order_by:
|
if order_by:
|
||||||
sql.append("ORDER BY " + ", ".join(order_by))
|
sql.append("ORDER BY " + ", ".join(order_by))
|
||||||
|
|
||||||
# LIMIT and OFFSET clauses
|
# LIMIT and OFFSET clauses
|
||||||
if self._limit is not None:
|
if self._limit is not None:
|
||||||
sql.append("%s " % backend.get_limit_offset_sql(self._limit, self._offset))
|
sql.append("%s " % connection.ops.limit_offset_sql(self._limit, self._offset))
|
||||||
else:
|
else:
|
||||||
assert self._offset is None, "'offset' is not allowed without 'limit'"
|
assert self._offset is None, "'offset' is not allowed without 'limit'"
|
||||||
|
|
||||||
return select, " ".join(sql), params
|
return select, " ".join(sql), params
|
||||||
|
|
||||||
# Use the backend's QuerySet class if it defines one, otherwise use _QuerySet.
|
# Use the backend's QuerySet class if it defines one. Otherwise, use _QuerySet.
|
||||||
if hasattr(backend, 'get_query_set_class'):
|
if connection.features.uses_custom_queryset:
|
||||||
QuerySet = backend.get_query_set_class(_QuerySet)
|
QuerySet = connection.ops.query_set_class(_QuerySet)
|
||||||
else:
|
else:
|
||||||
QuerySet = _QuerySet
|
QuerySet = _QuerySet
|
||||||
|
|
||||||
@ -579,6 +582,8 @@ class ValuesQuerySet(QuerySet):
|
|||||||
except EmptyResultSet:
|
except EmptyResultSet:
|
||||||
raise StopIteration
|
raise StopIteration
|
||||||
|
|
||||||
|
qn = connection.ops.quote_name
|
||||||
|
|
||||||
# self._select is a dictionary, and dictionaries' key order is
|
# self._select is a dictionary, and dictionaries' key order is
|
||||||
# undefined, so we convert it to a list of tuples.
|
# undefined, so we convert it to a list of tuples.
|
||||||
extra_select = self._select.items()
|
extra_select = self._select.items()
|
||||||
@ -605,9 +610,9 @@ class ValuesQuerySet(QuerySet):
|
|||||||
field_names = [f.attname for f in fields]
|
field_names = [f.attname for f in fields]
|
||||||
|
|
||||||
columns = [f.column for f in fields]
|
columns = [f.column for f in fields]
|
||||||
select = ['%s.%s' % (backend.quote_name(self.model._meta.db_table), backend.quote_name(c)) for c in columns]
|
select = ['%s.%s' % (qn(self.model._meta.db_table), qn(c)) for c in columns]
|
||||||
if extra_select:
|
if extra_select:
|
||||||
select.extend(['(%s) AS %s' % (quote_only_if_word(s[1]), backend.quote_name(s[0])) for s in extra_select])
|
select.extend(['(%s) AS %s' % (quote_only_if_word(s[1]), qn(s[0])) for s in extra_select])
|
||||||
field_names.extend([f[0] for f in extra_select])
|
field_names.extend([f[0] for f in extra_select])
|
||||||
|
|
||||||
cursor = connection.cursor()
|
cursor = connection.cursor()
|
||||||
@ -632,32 +637,33 @@ class DateQuerySet(QuerySet):
|
|||||||
def iterator(self):
|
def iterator(self):
|
||||||
from django.db.backends.util import typecast_timestamp
|
from django.db.backends.util import typecast_timestamp
|
||||||
from django.db.models.fields import DateTimeField
|
from django.db.models.fields import DateTimeField
|
||||||
|
|
||||||
|
qn = connection.ops.quote_name
|
||||||
self._order_by = () # Clear this because it'll mess things up otherwise.
|
self._order_by = () # Clear this because it'll mess things up otherwise.
|
||||||
if self._field.null:
|
if self._field.null:
|
||||||
self._where.append('%s.%s IS NOT NULL' % \
|
self._where.append('%s.%s IS NOT NULL' % \
|
||||||
(backend.quote_name(self.model._meta.db_table), backend.quote_name(self._field.column)))
|
(qn(self.model._meta.db_table), qn(self._field.column)))
|
||||||
try:
|
try:
|
||||||
select, sql, params = self._get_sql_clause()
|
select, sql, params = self._get_sql_clause()
|
||||||
except EmptyResultSet:
|
except EmptyResultSet:
|
||||||
raise StopIteration
|
raise StopIteration
|
||||||
|
|
||||||
table_name = backend.quote_name(self.model._meta.db_table)
|
table_name = qn(self.model._meta.db_table)
|
||||||
field_name = backend.quote_name(self._field.column)
|
field_name = qn(self._field.column)
|
||||||
|
|
||||||
if backend.allows_group_by_ordinal:
|
if connection.features.allows_group_by_ordinal:
|
||||||
group_by = '1'
|
group_by = '1'
|
||||||
else:
|
else:
|
||||||
group_by = backend.get_date_trunc_sql(self._kind,
|
group_by = connection.ops.date_trunc_sql(self._kind, '%s.%s' % (table_name, field_name))
|
||||||
'%s.%s' % (table_name, field_name))
|
|
||||||
|
|
||||||
sql = 'SELECT %s %s GROUP BY %s ORDER BY 1 %s' % \
|
sql = 'SELECT %s %s GROUP BY %s ORDER BY 1 %s' % \
|
||||||
(backend.get_date_trunc_sql(self._kind, '%s.%s' % (backend.quote_name(self.model._meta.db_table),
|
(connection.ops.date_trunc_sql(self._kind, '%s.%s' % (qn(self.model._meta.db_table),
|
||||||
backend.quote_name(self._field.column))), sql, group_by, self._order)
|
qn(self._field.column))), sql, group_by, self._order)
|
||||||
cursor = connection.cursor()
|
cursor = connection.cursor()
|
||||||
cursor.execute(sql, params)
|
cursor.execute(sql, params)
|
||||||
|
|
||||||
has_resolve_columns = hasattr(self, 'resolve_columns')
|
has_resolve_columns = hasattr(self, 'resolve_columns')
|
||||||
needs_datetime_string_cast = backend.needs_datetime_string_cast
|
needs_datetime_string_cast = connection.features.needs_datetime_string_cast
|
||||||
dates = []
|
dates = []
|
||||||
# It would be better to use self._field here instead of DateTimeField(),
|
# It would be better to use self._field here instead of DateTimeField(),
|
||||||
# but in Oracle that will result in a list of datetime.date instead of
|
# but in Oracle that will result in a list of datetime.date instead of
|
||||||
@ -777,44 +783,44 @@ class QNot(Q):
|
|||||||
return SortedDict(), [], []
|
return SortedDict(), [], []
|
||||||
return joins, where2, params
|
return joins, where2, params
|
||||||
|
|
||||||
def get_where_clause(lookup_type, table_prefix, field_name, value):
|
def get_where_clause(lookup_type, table_prefix, field_name, value, db_type):
|
||||||
if table_prefix.endswith('.'):
|
if table_prefix.endswith('.'):
|
||||||
table_prefix = backend.quote_name(table_prefix[:-1])+'.'
|
table_prefix = connection.ops.quote_name(table_prefix[:-1])+'.'
|
||||||
field_name = backend.quote_name(field_name)
|
field_name = connection.ops.quote_name(field_name)
|
||||||
if type(value) == datetime.datetime and backend.get_datetime_cast_sql():
|
if type(value) == datetime.datetime and connection.ops.datetime_cast_sql():
|
||||||
cast_sql = backend.get_datetime_cast_sql()
|
cast_sql = connection.ops.datetime_cast_sql()
|
||||||
else:
|
else:
|
||||||
cast_sql = '%s'
|
cast_sql = '%s'
|
||||||
if lookup_type in ('iexact', 'icontains', 'istartswith', 'iendswith') and backend.needs_upper_for_iops:
|
field_sql = connection.ops.field_cast_sql(db_type) % (table_prefix + field_name)
|
||||||
format = 'UPPER(%s%s) %s'
|
if lookup_type in ('iexact', 'icontains', 'istartswith', 'iendswith') and connection.features.needs_upper_for_iops:
|
||||||
|
format = 'UPPER(%s) %s'
|
||||||
else:
|
else:
|
||||||
format = '%s%s %s'
|
format = '%s %s'
|
||||||
try:
|
try:
|
||||||
return format % (table_prefix, field_name,
|
return format % (field_sql, connection.operators[lookup_type] % cast_sql)
|
||||||
backend.OPERATOR_MAPPING[lookup_type] % cast_sql)
|
|
||||||
except KeyError:
|
except KeyError:
|
||||||
pass
|
pass
|
||||||
if lookup_type == 'in':
|
if lookup_type == 'in':
|
||||||
in_string = ','.join(['%s' for id in value])
|
in_string = ','.join(['%s' for id in value])
|
||||||
if in_string:
|
if in_string:
|
||||||
return '%s%s IN (%s)' % (table_prefix, field_name, in_string)
|
return '%s IN (%s)' % (field_sql, in_string)
|
||||||
else:
|
else:
|
||||||
raise EmptyResultSet
|
raise EmptyResultSet
|
||||||
elif lookup_type in ('range', 'year'):
|
elif lookup_type in ('range', 'year'):
|
||||||
return '%s%s BETWEEN %%s AND %%s' % (table_prefix, field_name)
|
return '%s BETWEEN %%s AND %%s' % field_sql
|
||||||
elif lookup_type in ('month', 'day'):
|
elif lookup_type in ('month', 'day'):
|
||||||
return "%s = %%s" % backend.get_date_extract_sql(lookup_type, table_prefix + field_name)
|
return "%s = %%s" % connection.ops.date_extract_sql(lookup_type, field_sql)
|
||||||
elif lookup_type == 'isnull':
|
elif lookup_type == 'isnull':
|
||||||
return "%s%s IS %sNULL" % (table_prefix, field_name, (not value and 'NOT ' or ''))
|
return "%s IS %sNULL" % (field_sql, (not value and 'NOT ' or ''))
|
||||||
elif lookup_type == 'search':
|
elif lookup_type == 'search':
|
||||||
return backend.get_fulltext_search_sql(table_prefix + field_name)
|
return connection.ops.fulltext_search_sql(field_sql)
|
||||||
elif lookup_type in ('regex', 'iregex'):
|
elif lookup_type in ('regex', 'iregex'):
|
||||||
if settings.DATABASE_ENGINE == 'oracle':
|
if settings.DATABASE_ENGINE == 'oracle':
|
||||||
if lookup_type == 'regex':
|
if lookup_type == 'regex':
|
||||||
match_option = 'c'
|
match_option = 'c'
|
||||||
else:
|
else:
|
||||||
match_option = 'i'
|
match_option = 'i'
|
||||||
return "REGEXP_LIKE(%s%s, %s, '%s')" % (table_prefix, field_name, cast_sql, match_option)
|
return "REGEXP_LIKE(%s, %s, '%s')" % (field_sql, cast_sql, match_option)
|
||||||
else:
|
else:
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
raise TypeError, "Got invalid lookup_type: %s" % repr(lookup_type)
|
raise TypeError, "Got invalid lookup_type: %s" % repr(lookup_type)
|
||||||
@ -846,7 +852,7 @@ def fill_table_cache(opts, select, tables, where, old_prefix, cache_tables_seen,
|
|||||||
if max_depth and cur_depth > max_depth:
|
if max_depth and cur_depth > max_depth:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
qn = backend.quote_name
|
qn = connection.ops.quote_name
|
||||||
for f in opts.fields:
|
for f in opts.fields:
|
||||||
if f.rel and not f.null:
|
if f.rel and not f.null:
|
||||||
db_table = f.rel.to._meta.db_table
|
db_table = f.rel.to._meta.db_table
|
||||||
@ -944,7 +950,7 @@ def field_choices(field_list, related_query):
|
|||||||
return choices
|
return choices
|
||||||
|
|
||||||
def lookup_inner(path, lookup_type, value, opts, table, column):
|
def lookup_inner(path, lookup_type, value, opts, table, column):
|
||||||
qn = backend.quote_name
|
qn = connection.ops.quote_name
|
||||||
joins, where, params = SortedDict(), [], []
|
joins, where, params = SortedDict(), [], []
|
||||||
current_opts = opts
|
current_opts = opts
|
||||||
current_table = table
|
current_table = table
|
||||||
@ -1071,6 +1077,7 @@ def lookup_inner(path, lookup_type, value, opts, table, column):
|
|||||||
else:
|
else:
|
||||||
# No elements left in path. Current element is the element on which
|
# No elements left in path. Current element is the element on which
|
||||||
# the search is being performed.
|
# the search is being performed.
|
||||||
|
db_type = None
|
||||||
|
|
||||||
if join_required:
|
if join_required:
|
||||||
# Last query term is a RelatedObject
|
# Last query term is a RelatedObject
|
||||||
@ -1100,15 +1107,16 @@ def lookup_inner(path, lookup_type, value, opts, table, column):
|
|||||||
else:
|
else:
|
||||||
# Last query term was a normal field.
|
# Last query term was a normal field.
|
||||||
column = field.column
|
column = field.column
|
||||||
|
db_type = field.db_type()
|
||||||
|
|
||||||
where.append(get_where_clause(lookup_type, current_table + '.', column, value))
|
where.append(get_where_clause(lookup_type, current_table + '.', column, value, db_type))
|
||||||
params.extend(field.get_db_prep_lookup(lookup_type, value))
|
params.extend(field.get_db_prep_lookup(lookup_type, value))
|
||||||
|
|
||||||
return joins, where, params
|
return joins, where, params
|
||||||
|
|
||||||
def delete_objects(seen_objs):
|
def delete_objects(seen_objs):
|
||||||
"Iterate through a list of seen classes, and remove any instances that are referred to"
|
"Iterate through a list of seen classes, and remove any instances that are referred to"
|
||||||
qn = backend.quote_name
|
qn = connection.ops.quote_name
|
||||||
ordered_classes = seen_objs.keys()
|
ordered_classes = seen_objs.keys()
|
||||||
ordered_classes.reverse()
|
ordered_classes.reverse()
|
||||||
|
|
||||||
|
@ -678,7 +678,7 @@ def do_if(parser, token):
|
|||||||
|
|
||||||
{% if athlete_list and coach_list or cheerleader_list %}
|
{% if athlete_list and coach_list or cheerleader_list %}
|
||||||
|
|
||||||
If you need to combine and and or to do advanced logic, just use
|
If you need to combine ``and`` and ``or`` to do advanced logic, just use
|
||||||
nested if tags. For example:
|
nested if tags. For example:
|
||||||
|
|
||||||
{% if athlete_list %}
|
{% if athlete_list %}
|
||||||
|
@ -140,7 +140,7 @@ def do_extends(parser, token):
|
|||||||
This tag may be used in two ways: ``{% extends "base" %}`` (with quotes)
|
This tag may be used in two ways: ``{% extends "base" %}`` (with quotes)
|
||||||
uses the literal value "base" as the name of the parent template to extend,
|
uses the literal value "base" as the name of the parent template to extend,
|
||||||
or ``{% extends variable %}`` uses the value of ``variable`` as either the
|
or ``{% extends variable %}`` uses the value of ``variable`` as either the
|
||||||
name of the parent template to extend (if it evaluates to a string,) or as
|
name of the parent template to extend (if it evaluates to a string) or as
|
||||||
the parent tempate itelf (if it evaluates to a Template object).
|
the parent tempate itelf (if it evaluates to a Template object).
|
||||||
"""
|
"""
|
||||||
bits = token.contents.split()
|
bits = token.contents.split()
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import sys, time
|
import sys, time
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.db import connection, backend, get_creation_module
|
from django.db import connection, get_creation_module
|
||||||
from django.core import mail
|
from django.core import mail
|
||||||
from django.core.management import call_command
|
from django.core.management import call_command
|
||||||
from django.dispatch import dispatcher
|
from django.dispatch import dispatcher
|
||||||
@ -97,7 +97,7 @@ def create_test_db(verbosity=1, autoclobber=False):
|
|||||||
# If the database backend wants to create the test DB itself, let it
|
# If the database backend wants to create the test DB itself, let it
|
||||||
creation_module = get_creation_module()
|
creation_module = get_creation_module()
|
||||||
if hasattr(creation_module, "create_test_db"):
|
if hasattr(creation_module, "create_test_db"):
|
||||||
creation_module.create_test_db(settings, connection, backend, verbosity, autoclobber)
|
creation_module.create_test_db(settings, connection, verbosity, autoclobber)
|
||||||
return
|
return
|
||||||
|
|
||||||
if verbosity >= 1:
|
if verbosity >= 1:
|
||||||
@ -118,13 +118,15 @@ def create_test_db(verbosity=1, autoclobber=False):
|
|||||||
else:
|
else:
|
||||||
TEST_DATABASE_NAME = TEST_DATABASE_PREFIX + settings.DATABASE_NAME
|
TEST_DATABASE_NAME = TEST_DATABASE_PREFIX + settings.DATABASE_NAME
|
||||||
|
|
||||||
|
qn = connection.ops.quote_name
|
||||||
|
|
||||||
# Create the test database and connect to it. We need to autocommit
|
# Create the test database and connect to it. We need to autocommit
|
||||||
# if the database supports it because PostgreSQL doesn't allow
|
# if the database supports it because PostgreSQL doesn't allow
|
||||||
# CREATE/DROP DATABASE statements within transactions.
|
# CREATE/DROP DATABASE statements within transactions.
|
||||||
cursor = connection.cursor()
|
cursor = connection.cursor()
|
||||||
_set_autocommit(connection)
|
_set_autocommit(connection)
|
||||||
try:
|
try:
|
||||||
cursor.execute("CREATE DATABASE %s %s" % (backend.quote_name(TEST_DATABASE_NAME), suffix))
|
cursor.execute("CREATE DATABASE %s %s" % (qn(TEST_DATABASE_NAME), suffix))
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
sys.stderr.write("Got an error creating the test database: %s\n" % e)
|
sys.stderr.write("Got an error creating the test database: %s\n" % e)
|
||||||
if not autoclobber:
|
if not autoclobber:
|
||||||
@ -133,10 +135,10 @@ def create_test_db(verbosity=1, autoclobber=False):
|
|||||||
try:
|
try:
|
||||||
if verbosity >= 1:
|
if verbosity >= 1:
|
||||||
print "Destroying old test database..."
|
print "Destroying old test database..."
|
||||||
cursor.execute("DROP DATABASE %s" % backend.quote_name(TEST_DATABASE_NAME))
|
cursor.execute("DROP DATABASE %s" % qn(TEST_DATABASE_NAME))
|
||||||
if verbosity >= 1:
|
if verbosity >= 1:
|
||||||
print "Creating test database..."
|
print "Creating test database..."
|
||||||
cursor.execute("CREATE DATABASE %s %s" % (backend.quote_name(TEST_DATABASE_NAME), suffix))
|
cursor.execute("CREATE DATABASE %s %s" % (qn(TEST_DATABASE_NAME), suffix))
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
sys.stderr.write("Got an error recreating the test database: %s\n" % e)
|
sys.stderr.write("Got an error recreating the test database: %s\n" % e)
|
||||||
sys.exit(2)
|
sys.exit(2)
|
||||||
@ -163,7 +165,7 @@ def destroy_test_db(old_database_name, verbosity=1):
|
|||||||
# If the database wants to drop the test DB itself, let it
|
# If the database wants to drop the test DB itself, let it
|
||||||
creation_module = get_creation_module()
|
creation_module = get_creation_module()
|
||||||
if hasattr(creation_module, "destroy_test_db"):
|
if hasattr(creation_module, "destroy_test_db"):
|
||||||
creation_module.destroy_test_db(settings, connection, backend, old_database_name, verbosity)
|
creation_module.destroy_test_db(settings, connection, old_database_name, verbosity)
|
||||||
return
|
return
|
||||||
|
|
||||||
# Unless we're using SQLite, remove the test database to clean up after
|
# Unless we're using SQLite, remove the test database to clean up after
|
||||||
@ -180,5 +182,5 @@ def destroy_test_db(old_database_name, verbosity=1):
|
|||||||
cursor = connection.cursor()
|
cursor = connection.cursor()
|
||||||
_set_autocommit(connection)
|
_set_autocommit(connection)
|
||||||
time.sleep(1) # To avoid "database is being accessed by other users" errors.
|
time.sleep(1) # To avoid "database is being accessed by other users" errors.
|
||||||
cursor.execute("DROP DATABASE %s" % backend.quote_name(TEST_DATABASE_NAME))
|
cursor.execute("DROP DATABASE %s" % connection.ops.quote_name(TEST_DATABASE_NAME))
|
||||||
connection.close()
|
connection.close()
|
||||||
|
@ -286,7 +286,7 @@ Please follow these coding standards when writing code for inclusion in Django:
|
|||||||
* Mark all strings for internationalization; see the `i18n documentation`_
|
* Mark all strings for internationalization; see the `i18n documentation`_
|
||||||
for details.
|
for details.
|
||||||
|
|
||||||
* In docstrings, use "action words," like so::
|
* In docstrings, use "action words" such as::
|
||||||
|
|
||||||
def foo():
|
def foo():
|
||||||
"""
|
"""
|
||||||
|
@ -48,8 +48,8 @@ Get your database running
|
|||||||
|
|
||||||
If you plan to use Django's database API functionality, you'll need to
|
If you plan to use Django's database API functionality, you'll need to
|
||||||
make sure a database server is running. Django works with PostgreSQL_,
|
make sure a database server is running. Django works with PostgreSQL_,
|
||||||
MySQL_, Oracle_ and SQLite_ (the latter doesn't require a separate server to
|
MySQL_, Oracle_ and SQLite_ (although SQLite doesn't require a separate server
|
||||||
be running).
|
to be running).
|
||||||
|
|
||||||
Additionally, you'll need to make sure your Python database bindings are
|
Additionally, you'll need to make sure your Python database bindings are
|
||||||
installed.
|
installed.
|
||||||
|
40
docs/man/compile-messages.1
Normal file
40
docs/man/compile-messages.1
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
.TH "compile-messages.py" "1" "August 2007" "Django Project" ""
|
||||||
|
.SH "NAME"
|
||||||
|
compile-messages.py \- Internationalization utility for the Django
|
||||||
|
web framework
|
||||||
|
.SH "SYNOPSIS"
|
||||||
|
.B compile-messages.py \fR[-l <locale>]
|
||||||
|
|
||||||
|
.SH "DESCRIPTION"
|
||||||
|
A Django-customised wrapper around gettext's \fBmsgfmt\fR command. Generates
|
||||||
|
binary message catalogs (.mo files) from textual translation descriptions (.po
|
||||||
|
files).
|
||||||
|
.sp
|
||||||
|
The script should be invoked after running
|
||||||
|
.BI make-messages.py,
|
||||||
|
in the same directory from which
|
||||||
|
.BI make-messages.py
|
||||||
|
was invoked.
|
||||||
|
|
||||||
|
.SH "OPTIONS"
|
||||||
|
.TP
|
||||||
|
.I \-l <locale>
|
||||||
|
Compile the message catalogs for a specific locale. If this option is omitted,
|
||||||
|
all message catalogs are (re-)compiled.
|
||||||
|
|
||||||
|
.SH "SEE ALSO"
|
||||||
|
The man page for
|
||||||
|
.BI msgfmt
|
||||||
|
from the GNU gettext utilities, and the internationalization documentation
|
||||||
|
for Django:
|
||||||
|
.sp
|
||||||
|
.I http://www.djangoproject.com/documentation/i18n/
|
||||||
|
|
||||||
|
.SH "AUTHORS/CREDITS"
|
||||||
|
Originally developed at World Online in Lawrence, Kansas, USA. Refer to the
|
||||||
|
AUTHORS file in the Django distribution for contributors.
|
||||||
|
|
||||||
|
.SH "LICENSE"
|
||||||
|
New BSD license. For the full license text refer to the LICENSE file in the
|
||||||
|
Django distribution.
|
||||||
|
|
34
docs/man/daily_cleanup.1
Normal file
34
docs/man/daily_cleanup.1
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
.TH "daily_cleanup.py" "1" "August 2007" "Django Project" ""
|
||||||
|
.SH "NAME"
|
||||||
|
daily_cleanup.py \- Database clean-up for the Django web framework
|
||||||
|
.SH "SYNOPSIS"
|
||||||
|
.B daily_cleanup.py
|
||||||
|
|
||||||
|
.SH "DESCRIPTION"
|
||||||
|
Removes stale session data from a Django database. This means, any session data
|
||||||
|
which has an expiry date prior to the date the script is run.
|
||||||
|
.sp
|
||||||
|
The script can be run manually or can be scheduled to run at regular
|
||||||
|
intervals as a
|
||||||
|
.BI cron
|
||||||
|
job.
|
||||||
|
|
||||||
|
.SH "ENVIRONMENT"
|
||||||
|
.TP
|
||||||
|
.I DJANGO_SETTINGS_MODULE
|
||||||
|
This environment variable defines the settings module to be read.
|
||||||
|
It should be in Python-import form, e.g. "myproject.settings".
|
||||||
|
|
||||||
|
.SH "SEE ALSO"
|
||||||
|
The sessions documentation:
|
||||||
|
.sp
|
||||||
|
.I http://www.djangoproject.com/documentation/sessions/
|
||||||
|
|
||||||
|
.SH "AUTHORS/CREDITS"
|
||||||
|
Originally developed at World Online in Lawrence, Kansas, USA. Refer to the
|
||||||
|
AUTHORS file in the Django distribution for contributors.
|
||||||
|
|
||||||
|
.SH "LICENSE"
|
||||||
|
New BSD license. For the full license text refer to the LICENSE file in the
|
||||||
|
Django distribution.
|
||||||
|
|
26
docs/man/gather_profile_stats.1
Normal file
26
docs/man/gather_profile_stats.1
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
.TH "gather_profile_stats.py" "1" "August 2007" "Django Project" ""
|
||||||
|
.SH "NAME"
|
||||||
|
gather_profile_stats.py \- Performance analysis tool for the Django web
|
||||||
|
framework
|
||||||
|
.SH "SYNOPSIS"
|
||||||
|
.B python gather_profile_stats.py
|
||||||
|
.I <path>
|
||||||
|
|
||||||
|
.SH "DESCRIPTION"
|
||||||
|
This utility script aggregates profiling logs generated using Python's
|
||||||
|
hotshot profiler. The sole command-line argument is the full path to the
|
||||||
|
directory containing the profiling logfiles.
|
||||||
|
|
||||||
|
.SH "SEE ALSO"
|
||||||
|
Discussion of profiling Django applications on the Django project's wiki:
|
||||||
|
.sp
|
||||||
|
.I http://www.djangoproject.com/wiki/ProfilingDjango
|
||||||
|
|
||||||
|
.SH "AUTHORS/CREDITS"
|
||||||
|
Originally developed at World Online in Lawrence, Kansas, USA. Refer to the
|
||||||
|
AUTHORS file in the Django distribution for contributors.
|
||||||
|
|
||||||
|
.SH "LICENSE"
|
||||||
|
New BSD license. For the full license text refer to the LICENSE file in the
|
||||||
|
Django distribution.
|
||||||
|
|
62
docs/man/make-messages.1
Normal file
62
docs/man/make-messages.1
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
.TH "make-messages.py" "1" "August 2007" "Django Project" ""
|
||||||
|
.SH "NAME"
|
||||||
|
make-messages.py \- Internationalization utility for the Django
|
||||||
|
web framework
|
||||||
|
.SH "SYNOPSIS"
|
||||||
|
.B make-messages.py\fR [\-a] [\-v] [\-l <locale>] [\-d <domain>]
|
||||||
|
|
||||||
|
.SH "DESCRIPTION"
|
||||||
|
This script creates or updates one or more message files for a Django app,
|
||||||
|
a Django project or the Django framework itself. It should be run from one
|
||||||
|
of three places: the root directory of a Django app; the root directory
|
||||||
|
of a Django project; or the root django directory (the one in your PYTHONPATH,
|
||||||
|
not the root of a Subversion checkout).
|
||||||
|
.sp
|
||||||
|
The script will run over the source tree of an application, project or Django
|
||||||
|
itself (depending on where it is invoked), pulling out all strings marked for
|
||||||
|
translation and creating or updating a standard PO-format message file for the
|
||||||
|
specified language. Refer to Django's internationalization documentation for
|
||||||
|
details of where this file is created.
|
||||||
|
.sp
|
||||||
|
The \fI\-a\fR and \fI\-l\fR options are used to control whether message
|
||||||
|
catalogs are created for all locales, or just a single one.
|
||||||
|
|
||||||
|
.SH "OPTIONS"
|
||||||
|
.TP
|
||||||
|
.I \-a
|
||||||
|
Run make-messages for all locales specified in the Django settings file. Cannot
|
||||||
|
be used in conjuntion with \fI\-l\fR.
|
||||||
|
.TP
|
||||||
|
.I \-d <domain>
|
||||||
|
Specifies the translation domain to use. Valid domains are \fIdjango\fR or
|
||||||
|
\fIdjangojs\fR, depending on whether you wish to generate translation strings
|
||||||
|
for the Python or JavaScript components of your app, your project or the
|
||||||
|
framework itself. The default domain is \fIdjango\fR.
|
||||||
|
.TP
|
||||||
|
.I \-l <locale>
|
||||||
|
Extract messages for a particular locale.
|
||||||
|
.TP
|
||||||
|
.I \-v
|
||||||
|
Run verbosely.
|
||||||
|
|
||||||
|
.SH "ENVIRONMENT"
|
||||||
|
.TP
|
||||||
|
.I DJANGO_SETTINGS_MODULE
|
||||||
|
This environment variable defines the settings module to be read.
|
||||||
|
It should be in Python-import form, e.g. "myproject.settings".
|
||||||
|
|
||||||
|
.SH "SEE ALSO"
|
||||||
|
The Django internationalization documentation:
|
||||||
|
.sp
|
||||||
|
.I http://www.djangoproject.com/documentation/i18n/
|
||||||
|
.sp
|
||||||
|
The PO file format is documented in the GNU gettext documentation.
|
||||||
|
|
||||||
|
.SH "AUTHORS/CREDITS"
|
||||||
|
Originally developed at World Online in Lawrence, Kansas, USA. Refer to the
|
||||||
|
AUTHORS file in the Django distribution for contributors.
|
||||||
|
|
||||||
|
.SH "LICENSE"
|
||||||
|
New BSD license. For the full license text refer to the LICENSE file in the
|
||||||
|
Django distribution.
|
||||||
|
|
@ -1422,12 +1422,12 @@ keep it simple and assume e-mail validation is contained in a function called
|
|||||||
|
|
||||||
class MultiEmailField(forms.Field):
|
class MultiEmailField(forms.Field):
|
||||||
def clean(self, value):
|
def clean(self, value):
|
||||||
|
if not value:
|
||||||
|
raise forms.ValidationError('Enter at least one e-mail address.')
|
||||||
emails = value.split(',')
|
emails = value.split(',')
|
||||||
for email in emails:
|
for email in emails:
|
||||||
if not is_valid_email(email):
|
if not is_valid_email(email):
|
||||||
raise forms.ValidationError('%s is not a valid e-mail address.' % email)
|
raise forms.ValidationError('%s is not a valid e-mail address.' % email)
|
||||||
if not emails:
|
|
||||||
raise forms.ValidationError('Enter at least one e-mail address.')
|
|
||||||
return emails
|
return emails
|
||||||
|
|
||||||
Let's alter the ongoing ``ContactForm`` example to demonstrate how you'd use
|
Let's alter the ongoing ``ContactForm`` example to demonstrate how you'd use
|
||||||
|
@ -937,6 +937,12 @@ such as this::
|
|||||||
|
|
||||||
The template tag will output the string ``/clients/client/123/``.
|
The template tag will output the string ``/clients/client/123/``.
|
||||||
|
|
||||||
|
**New in development version:** If you're using `named URL patterns`_,
|
||||||
|
you can refer to the name of the pattern in the ``url`` tag instead of
|
||||||
|
using the path to the view.
|
||||||
|
|
||||||
|
.. _named URL patterns: ../url_dispatch/#naming-url-patterns
|
||||||
|
|
||||||
widthratio
|
widthratio
|
||||||
~~~~~~~~~~
|
~~~~~~~~~~
|
||||||
|
|
||||||
@ -1326,12 +1332,17 @@ urlize
|
|||||||
|
|
||||||
Converts URLs in plain text into clickable links.
|
Converts URLs in plain text into clickable links.
|
||||||
|
|
||||||
|
Note that if ``urlize`` is applied to text that already contains HTML markup,
|
||||||
|
things won't work as expected. Apply this filter only to *plain* text.
|
||||||
|
|
||||||
urlizetrunc
|
urlizetrunc
|
||||||
~~~~~~~~~~~
|
~~~~~~~~~~~
|
||||||
|
|
||||||
Converts URLs into clickable links, truncating URLs longer than the given
|
Converts URLs into clickable links, truncating URLs longer than the given
|
||||||
character limit.
|
character limit.
|
||||||
|
|
||||||
|
As with urlize_, this filter should only be applied to *plain* text.
|
||||||
|
|
||||||
**Argument:** Length to truncate URLs to
|
**Argument:** Length to truncate URLs to
|
||||||
|
|
||||||
wordcount
|
wordcount
|
||||||
|
@ -277,7 +277,7 @@ Subclassing Context: RequestContext
|
|||||||
|
|
||||||
Django comes with a special ``Context`` class,
|
Django comes with a special ``Context`` class,
|
||||||
``django.template.RequestContext``, that acts slightly differently than
|
``django.template.RequestContext``, that acts slightly differently than
|
||||||
the normal ``django.template.Context``. The first difference is that takes
|
the normal ``django.template.Context``. The first difference is that it takes
|
||||||
an `HttpRequest object`_ as its first argument. For example::
|
an `HttpRequest object`_ as its first argument. For example::
|
||||||
|
|
||||||
c = RequestContext(request, {
|
c = RequestContext(request, {
|
||||||
|
@ -193,7 +193,7 @@ Change it like so::
|
|||||||
urlpatterns = patterns('',
|
urlpatterns = patterns('',
|
||||||
(r'^$', 'django.views.generic.list_detail.object_list', info_dict),
|
(r'^$', 'django.views.generic.list_detail.object_list', info_dict),
|
||||||
(r'^(?P<object_id>\d+)/$', 'django.views.generic.list_detail.object_detail', info_dict),
|
(r'^(?P<object_id>\d+)/$', 'django.views.generic.list_detail.object_detail', info_dict),
|
||||||
(r'^(?P<object_id>\d+)/results/$', 'django.views.generic.list_detail.object_detail', dict(info_dict, template_name='polls/results.html'), 'poll_results'),
|
url(r'^(?P<object_id>\d+)/results/$', 'django.views.generic.list_detail.object_detail', dict(info_dict, template_name='polls/results.html'), 'poll_results'),
|
||||||
(r'^(?P<poll_id>\d+)/vote/$', 'mysite.polls.views.vote'),
|
(r'^(?P<poll_id>\d+)/vote/$', 'mysite.polls.views.vote'),
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -209,11 +209,14 @@ objects" and "display a detail page for a particular type of object."
|
|||||||
from the URL to be called ``"object_id"``, so we've changed ``poll_id`` to
|
from the URL to be called ``"object_id"``, so we've changed ``poll_id`` to
|
||||||
``object_id`` for the generic views.
|
``object_id`` for the generic views.
|
||||||
|
|
||||||
* We've added a name, ``poll_results``, to the results view so that we have
|
* We've added a name, ``poll_results``, to the results view so that we
|
||||||
a way to refer to its URL later on (see `naming URL patterns`_ for more on
|
have a way to refer to its URL later on (see the documentation about
|
||||||
named patterns).
|
`naming URL patterns`_ for information). We're also using the `url()`_
|
||||||
|
function from ``django.conf.urls.defaults`` here. It's a good habit to
|
||||||
|
use ``url()`` when you are providing a pattern name like this.
|
||||||
|
|
||||||
.. _naming URL patterns: ../url_dispatch/#naming-url-patterns
|
.. _naming URL patterns: ../url_dispatch/#naming-url-patterns
|
||||||
|
.. _url(): ../url_dispatch/#url
|
||||||
|
|
||||||
By default, the ``object_detail`` generic view uses a template called
|
By default, the ``object_detail`` generic view uses a template called
|
||||||
``<app name>/<model name>_detail.html``. In our case, it'll use the template
|
``<app name>/<model name>_detail.html``. In our case, it'll use the template
|
||||||
|
@ -204,8 +204,16 @@ optional extra arguments dictionary. For example::
|
|||||||
...
|
...
|
||||||
)
|
)
|
||||||
|
|
||||||
|
This function takes five arguments, most of which are optional::
|
||||||
|
|
||||||
|
url(regex, view, kwargs=None, name=None, prefix='')
|
||||||
|
|
||||||
See `Naming URL patterns`_ for why the ``name`` parameter is useful.
|
See `Naming URL patterns`_ for why the ``name`` parameter is useful.
|
||||||
|
|
||||||
|
The ``prefix`` parameter has the same meaning as the first argument to
|
||||||
|
``patterns()`` and is only relevant when you're passing a string as the
|
||||||
|
``view`` parameter.
|
||||||
|
|
||||||
handler404
|
handler404
|
||||||
----------
|
----------
|
||||||
|
|
||||||
@ -512,7 +520,7 @@ view::
|
|||||||
|
|
||||||
This is completely valid, but it leads to problems when you try to do reverse
|
This is completely valid, but it leads to problems when you try to do reverse
|
||||||
URL matching (through the ``permalink()`` decorator or the ``{% url %}``
|
URL matching (through the ``permalink()`` decorator or the ``{% url %}``
|
||||||
template tag). Continuing this example, if you wanted to retrieve the URL for
|
`template tag`_). Continuing this example, if you wanted to retrieve the URL for
|
||||||
the ``archive`` view, Django's reverse URL matcher would get confused, because
|
the ``archive`` view, Django's reverse URL matcher would get confused, because
|
||||||
*two* URLpatterns point at that view.
|
*two* URLpatterns point at that view.
|
||||||
|
|
||||||
@ -552,14 +560,16 @@ not restricted to valid Python names.
|
|||||||
name, will decrease the chances of collision. We recommend something like
|
name, will decrease the chances of collision. We recommend something like
|
||||||
``myapp-comment`` instead of ``comment``.
|
``myapp-comment`` instead of ``comment``.
|
||||||
|
|
||||||
|
.. _template tag: ../templates/#url
|
||||||
|
|
||||||
Utility methods
|
Utility methods
|
||||||
===============
|
===============
|
||||||
|
|
||||||
reverse()
|
reverse()
|
||||||
---------
|
---------
|
||||||
|
|
||||||
If you need to use something similar to the ``{% url %}`` template tag in your
|
If you need to use something similar to the ``{% url %}`` `template tag`_ in
|
||||||
code, Django provides the ``django.core.urlresolvers.reverse()``. The
|
your code, Django provides the ``django.core.urlresolvers.reverse()``. The
|
||||||
``reverse()`` function has the following signature::
|
``reverse()`` function has the following signature::
|
||||||
|
|
||||||
reverse(viewname, urlconf=None, args=None, kwargs=None)
|
reverse(viewname, urlconf=None, args=None, kwargs=None)
|
||||||
|
@ -247,6 +247,7 @@ class ClientTest(TestCase):
|
|||||||
self.failIf(login)
|
self.failIf(login)
|
||||||
|
|
||||||
def test_logout(self):
|
def test_logout(self):
|
||||||
|
"Request a logout after logging in"
|
||||||
# Log in
|
# Log in
|
||||||
self.client.login(username='testclient', password='password')
|
self.client.login(username='testclient', password='password')
|
||||||
|
|
||||||
|
File diff suppressed because one or more lines are too long
@ -36,6 +36,13 @@ class Base(models.Model):
|
|||||||
def __unicode__(self):
|
def __unicode__(self):
|
||||||
return "Base %s" % self.name
|
return "Base %s" % self.name
|
||||||
|
|
||||||
|
class Article(models.Model):
|
||||||
|
name = models.CharField(maxlength = 50)
|
||||||
|
text = models.TextField()
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return "Article %s" % self.name
|
||||||
|
|
||||||
__test__ = {'API_TESTS': ur"""
|
__test__ = {'API_TESTS': ur"""
|
||||||
# Regression test for #1661 and #1662: Check that string form referencing of
|
# Regression test for #1661 and #1662: Check that string form referencing of
|
||||||
# models works, both as pre and post reference, on all RelatedField types.
|
# models works, both as pre and post reference, on all RelatedField types.
|
||||||
@ -82,4 +89,13 @@ __test__ = {'API_TESTS': ur"""
|
|||||||
# We can also do the above query using UTF-8 strings.
|
# We can also do the above query using UTF-8 strings.
|
||||||
>>> Foo.objects.get(friend__contains='\xc3\xa7')
|
>>> Foo.objects.get(friend__contains='\xc3\xa7')
|
||||||
<Foo: Foo Bjorn>
|
<Foo: Foo Bjorn>
|
||||||
|
|
||||||
|
# Regression tests for #5087: make sure we can perform queries on TextFields.
|
||||||
|
>>> a = Article(name='Test', text='The quick brown fox jumps over the lazy dog.')
|
||||||
|
>>> a.save()
|
||||||
|
>>> Article.objects.get(text__exact='The quick brown fox jumps over the lazy dog.')
|
||||||
|
<Article: Article Test>
|
||||||
|
|
||||||
|
>>> Article.objects.get(text__contains='quick brown fox')
|
||||||
|
<Article: Article Test>
|
||||||
"""}
|
"""}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user