mirror of
https://github.com/django/django.git
synced 2025-07-04 17:59: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
|
||||
Andy Dustman <farcepest@gmail.com>
|
||||
Clint Ecker
|
||||
Nick Efford <nick@efford.org>
|
||||
eibaan@gmail.com
|
||||
enlight
|
||||
Enrico <rico.bl@gmail.com>
|
||||
@ -197,6 +198,7 @@ answer newbie questions, and generally made Django that much better:
|
||||
mccutchen@gmail.com
|
||||
michael.mcewan@gmail.com
|
||||
mikko@sorl.net
|
||||
Slawek Mikula <slawek dot mikula at gmail dot com>
|
||||
mitakummaa@gmail.com
|
||||
mmarshall
|
||||
Andreas Mock <andreas.mock@web.de>
|
||||
|
@ -25,7 +25,8 @@ ADMINS = ()
|
||||
INTERNAL_IPS = ()
|
||||
|
||||
# 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'
|
||||
|
||||
# Language code for this installation. All choices can be found here:
|
||||
|
Binary file not shown.
@ -6,7 +6,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: django\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"
|
||||
"Last-Translator: Mario Gonzalez <gonzalemario @t gmail.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
|
||||
#: contrib/admin/views/main.py:344 contrib/admin/views/main.py:346
|
||||
#: core/validators.py:275
|
||||
msgid "and"
|
||||
msgstr "y"
|
||||
|
||||
@ -510,6 +511,10 @@ msgstr "%(number)d %(type)s"
|
||||
msgid ", %(number)d %(type)s"
|
||||
msgstr ", %(number)d %(type)s"
|
||||
|
||||
#: utils/text.py:127
|
||||
msgid "or"
|
||||
msgstr "o"
|
||||
|
||||
#: utils/dateformat.py:41
|
||||
msgid "p.m."
|
||||
msgstr "p.m"
|
||||
@ -765,7 +770,7 @@ msgstr "ocho"
|
||||
msgid "nine"
|
||||
msgstr "nueve"
|
||||
|
||||
#: contrib/auth/views.py:41
|
||||
#: contrib/auth/views.py:47
|
||||
msgid "Logged out"
|
||||
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.
|
||||
|
||||
# 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
|
||||
# although not all variations may be possible on all operating systems.
|
||||
# http://en.wikipedia.org/wiki/List_of_tz_zones_by_name
|
||||
# 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
|
||||
# system time zone.
|
||||
TIME_ZONE = 'America/Chicago'
|
||||
|
@ -59,7 +59,7 @@
|
||||
{% else %}
|
||||
<ul class="actionlist">
|
||||
{% 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 %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
|
@ -1,6 +1,6 @@
|
||||
from django.core import validators
|
||||
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.utils.encoding import smart_str
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
@ -188,6 +188,7 @@ class User(models.Model):
|
||||
# AND gp."group_id" = ug."group_id"
|
||||
# AND ct."id" = p."content_type_id"
|
||||
# AND ug."user_id" = %s, [self.id])
|
||||
qn = connection.ops.quote_name
|
||||
sql = """
|
||||
SELECT ct.%s, p.%s
|
||||
FROM %s p, %s gp, %s ug, %s ct
|
||||
@ -195,13 +196,13 @@ class User(models.Model):
|
||||
AND gp.%s = ug.%s
|
||||
AND ct.%s = p.%s
|
||||
AND ug.%s = %%s""" % (
|
||||
backend.quote_name('app_label'), backend.quote_name('codename'),
|
||||
backend.quote_name('auth_permission'), backend.quote_name('auth_group_permissions'),
|
||||
backend.quote_name('auth_user_groups'), backend.quote_name('django_content_type'),
|
||||
backend.quote_name('id'), backend.quote_name('permission_id'),
|
||||
backend.quote_name('group_id'), backend.quote_name('group_id'),
|
||||
backend.quote_name('id'), backend.quote_name('content_type_id'),
|
||||
backend.quote_name('user_id'),)
|
||||
qn('app_label'), qn('codename'),
|
||||
qn('auth_permission'), qn('auth_group_permissions'),
|
||||
qn('auth_user_groups'), qn('django_content_type'),
|
||||
qn('id'), qn('permission_id'),
|
||||
qn('group_id'), qn('group_id'),
|
||||
qn('id'), qn('content_type_id'),
|
||||
qn('user_id'),)
|
||||
cursor.execute(sql, [self.id])
|
||||
self._group_perm_cache = set(["%s.%s" % (row[0], row[1]) for row in cursor.fetchall()])
|
||||
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.core.exceptions import ObjectDoesNotExist
|
||||
from django.db import backend
|
||||
from django.db import connection
|
||||
from django.db.models import signals
|
||||
from django.db.models.fields.related import RelatedField, Field, ManyToManyRel
|
||||
from django.db.models.loading import get_model
|
||||
@ -163,13 +163,15 @@ class ReverseGenericRelatedObjectsDescriptor(object):
|
||||
superclass = rel_model._default_manager.__class__
|
||||
RelatedManager = create_generic_related_manager(superclass)
|
||||
|
||||
qn = connection.ops.quote_name
|
||||
|
||||
manager = RelatedManager(
|
||||
model = rel_model,
|
||||
instance = instance,
|
||||
symmetrical = (self.field.rel.symmetrical and instance.__class__ == rel_model),
|
||||
join_table = backend.quote_name(self.field.m2m_db_table()),
|
||||
source_col_name = backend.quote_name(self.field.m2m_column_name()),
|
||||
target_col_name = backend.quote_name(self.field.m2m_reverse_name()),
|
||||
join_table = qn(self.field.m2m_db_table()),
|
||||
source_col_name = qn(self.field.m2m_column_name()),
|
||||
target_col_name = qn(self.field.m2m_reverse_name()),
|
||||
content_type = ContentType.objects.get_for_model(self.field.model),
|
||||
content_type_field_name = self.field.content_type_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.translation import get_date_formats
|
||||
from django.utils.encoding import smart_unicode, smart_str, iri_to_uri
|
||||
from django.db.models.query import QuerySet
|
||||
|
||||
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)
|
||||
|
||||
def objects(self, **kwargs):
|
||||
for obj in self.model._default_manager.filter(**kwargs):
|
||||
yield EasyInstance(self, obj)
|
||||
return self.get_query_set().filter(**kwargs)
|
||||
|
||||
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):
|
||||
return EasyInstance(self, self.model._default_manager.get(pk=pk))
|
||||
@ -194,3 +199,17 @@ class EasyInstanceField(object):
|
||||
else:
|
||||
lst = [(self.values()[0], None)]
|
||||
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):
|
||||
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}
|
||||
if day is not None:
|
||||
# TODO: The objects in this template should be EasyInstances
|
||||
return date_based.archive_day(request, year, month, day, self.model.objects.all(), field.name,
|
||||
return date_based.archive_day(request, year, month, day, queryset, field.name,
|
||||
template_name='databrowse/calendar_day.html', allow_empty=False, allow_future=True,
|
||||
extra_context=extra_context)
|
||||
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,
|
||||
extra_context=extra_context)
|
||||
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,
|
||||
extra_context=extra_context)
|
||||
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,
|
||||
extra_context=extra_context)
|
||||
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 %}>
|
||||
<head>
|
||||
<title>{% block title %}{% endblock %}</title>
|
||||
{% block style %}
|
||||
<style type="text/css">
|
||||
* { margin:0; padding:0; }
|
||||
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 { background:#fff; border-bottom:1px solid #ddd; padding:0 20px; }
|
||||
</style>
|
||||
{% endblock %}
|
||||
{% block extrahead %}{% endblock %}
|
||||
</head>
|
||||
<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">
|
||||
{% block content %}{% endblock %}
|
||||
</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 %}
|
||||
|
||||
@ -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>
|
||||
|
||||
<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">
|
||||
{% for object in object_list %}
|
||||
|
@ -1,4 +1,4 @@
|
||||
{% extends "databrowse/base.html" %}
|
||||
{% extends "databrowse/base_site.html" %}
|
||||
|
||||
{% 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 %}
|
||||
|
||||
|
@ -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 %}
|
||||
|
||||
@ -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>
|
||||
|
||||
<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">
|
||||
{% 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 %}
|
||||
|
||||
|
@ -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 %}
|
||||
|
||||
|
@ -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 %}
|
||||
|
||||
|
@ -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 %}
|
||||
|
||||
@ -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>
|
||||
|
||||
<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">
|
||||
{% 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 %}
|
||||
|
||||
|
@ -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 %}
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
{% extends "databrowse/base.html" %}
|
||||
{% extends "databrowse/base_site.html" %}
|
||||
|
||||
{% 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 %}
|
||||
|
||||
@ -6,7 +6,7 @@
|
||||
|
||||
<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 }}
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
{% extends "databrowse/base.html" %}
|
||||
{% extends "databrowse/base_site.html" %}
|
||||
|
||||
{% 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 self.output_transaction:
|
||||
# This needs to be imported here, because it relies on settings.
|
||||
from django.db import backend
|
||||
if backend.get_start_transaction_sql():
|
||||
print self.style.SQL_KEYWORD(backend.get_start_transaction_sql())
|
||||
from django.db import connection
|
||||
if connection.ops.start_transaction_sql():
|
||||
print self.style.SQL_KEYWORD(connection.ops.start_transaction_sql())
|
||||
print output
|
||||
if self.output_transaction:
|
||||
print self.style.SQL_KEYWORD("COMMIT;")
|
||||
|
@ -8,7 +8,7 @@ class Command(LabelCommand):
|
||||
requires_model_validation = False
|
||||
|
||||
def handle_label(self, tablename, **options):
|
||||
from django.db import backend, connection, transaction, models
|
||||
from django.db import connection, transaction, models
|
||||
fields = (
|
||||
# "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),
|
||||
@ -17,8 +17,9 @@ class Command(LabelCommand):
|
||||
)
|
||||
table_output = []
|
||||
index_output = []
|
||||
qn = connection.ops.quote_name
|
||||
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 ""))
|
||||
if f.unique:
|
||||
field_output.append("UNIQUE")
|
||||
@ -27,10 +28,10 @@ class Command(LabelCommand):
|
||||
if f.db_index:
|
||||
unique = f.unique and "UNIQUE " or ""
|
||||
index_output.append("CREATE %sINDEX %s_%s ON %s (%s);" % \
|
||||
(unique, tablename, f.name, backend.quote_name(tablename),
|
||||
backend.quote_name(f.name)))
|
||||
(unique, tablename, f.name, qn(tablename),
|
||||
qn(f.name)))
|
||||
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):
|
||||
full_statement.append(' %s%s' % (line, i < len(table_output)-1 and ',' or ''))
|
||||
full_statement.append(');')
|
||||
|
@ -15,7 +15,7 @@ class Command(BaseCommand):
|
||||
def handle(self, *fixture_labels, **options):
|
||||
from django.db.models import get_apps
|
||||
from django.core import serializers
|
||||
from django.db import connection, transaction, backend
|
||||
from django.db import connection, transaction
|
||||
from django.conf import settings
|
||||
|
||||
self.style = no_style()
|
||||
@ -105,7 +105,7 @@ class Command(BaseCommand):
|
||||
(format, fixture_name, humanize(fixture_dir))
|
||||
|
||||
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 verbosity > 1:
|
||||
print "Resetting sequences"
|
||||
|
@ -5,5 +5,5 @@ class Command(AppCommand):
|
||||
output_transaction = True
|
||||
|
||||
def handle_app(self, app, **options):
|
||||
from django.db import backend, models
|
||||
return '\n'.join(backend.get_sql_sequence_reset(self.style, models.get_models(app)))
|
||||
from django.db import connection, models
|
||||
return '\n'.join(connection.ops.sequence_reset_sql(self.style, models.get_models(app)))
|
||||
|
@ -12,7 +12,7 @@ class Command(NoArgsCommand):
|
||||
args = '[--verbosity] [--noinput]'
|
||||
|
||||
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.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,
|
||||
# so we know what needs to be added.
|
||||
table_list = table_list()
|
||||
if backend.uses_case_insensitive_names:
|
||||
if connection.features.uses_case_insensitive_names:
|
||||
table_name_converter = str.upper
|
||||
else:
|
||||
table_name_converter = lambda x: x
|
||||
@ -125,6 +125,6 @@ class Command(NoArgsCommand):
|
||||
else:
|
||||
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
|
||||
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):
|
||||
"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 = []
|
||||
for app in models.get_apps():
|
||||
for model in models.get_models(app):
|
||||
all_models.append(model)
|
||||
if backend.uses_case_insensitive_names:
|
||||
if connection.features.uses_case_insensitive_names:
|
||||
converter = lambda x: x.upper()
|
||||
else:
|
||||
converter = lambda x: x
|
||||
@ -95,7 +95,7 @@ def sql_create(app, style):
|
||||
|
||||
def sql_delete(app, style):
|
||||
"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
|
||||
introspection = get_introspection_module()
|
||||
|
||||
@ -110,12 +110,13 @@ def sql_delete(app, style):
|
||||
table_names = introspection.get_table_list(cursor)
|
||||
else:
|
||||
table_names = []
|
||||
if backend.uses_case_insensitive_names:
|
||||
if connection.features.uses_case_insensitive_names:
|
||||
table_name_converter = str.upper
|
||||
else:
|
||||
table_name_converter = lambda x: x
|
||||
|
||||
output = []
|
||||
qn = connection.ops.quote_name
|
||||
|
||||
# Output DROP TABLE statements for standard application tables.
|
||||
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:
|
||||
# Drop the table now
|
||||
output.append('%s %s;' % (style.SQL_KEYWORD('DROP TABLE'),
|
||||
style.SQL_TABLE(backend.quote_name(model._meta.db_table))))
|
||||
if backend.supports_constraints and model in references_to_delete:
|
||||
style.SQL_TABLE(qn(model._meta.db_table))))
|
||||
if connection.features.supports_constraints and model in references_to_delete:
|
||||
for rel_class, f in references_to_delete[model]:
|
||||
table = rel_class._meta.db_table
|
||||
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))))
|
||||
output.append('%s %s %s %s;' % \
|
||||
(style.SQL_KEYWORD('ALTER TABLE'),
|
||||
style.SQL_TABLE(backend.quote_name(table)),
|
||||
style.SQL_KEYWORD(backend.get_drop_foreignkey_sql()),
|
||||
style.SQL_FIELD(truncate_name(r_name, backend.get_max_name_length()))))
|
||||
style.SQL_TABLE(qn(table)),
|
||||
style.SQL_KEYWORD(connection.ops.drop_foreignkey_sql()),
|
||||
style.SQL_FIELD(truncate_name(r_name, connection.ops.max_name_length()))))
|
||||
del references_to_delete[model]
|
||||
if model._meta.has_auto_field and hasattr(backend, 'get_drop_sequence'):
|
||||
output.append(backend.get_drop_sequence(model._meta.db_table))
|
||||
if model._meta.has_auto_field:
|
||||
ds = connection.ops.drop_sequence_sql(model._meta.db_table)
|
||||
if ds:
|
||||
output.append(ds)
|
||||
|
||||
# Output DROP TABLE statements for many-to-many tables.
|
||||
for model in app_models:
|
||||
@ -159,9 +162,10 @@ def sql_delete(app, style):
|
||||
for f in opts.many_to_many:
|
||||
if cursor and table_name_converter(f.m2m_db_table()) in table_names:
|
||||
output.append("%s %s;" % (style.SQL_KEYWORD('DROP TABLE'),
|
||||
style.SQL_TABLE(backend.quote_name(f.m2m_db_table()))))
|
||||
if hasattr(backend, 'get_drop_sequence'):
|
||||
output.append(backend.get_drop_sequence("%s_%s" % (model._meta.db_table, f.column)))
|
||||
style.SQL_TABLE(qn(f.m2m_db_table()))))
|
||||
ds = connection.ops.drop_sequence_sql("%s_%s" % (model._meta.db_table, f.column))
|
||||
if ds:
|
||||
output.append(ds)
|
||||
|
||||
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)
|
||||
|
||||
def sql_flush(style):
|
||||
"Returns a list of the SQL statements used to flush the database"
|
||||
from django.db import backend
|
||||
statements = backend.get_sql_flush(style, table_list(), sequence_list())
|
||||
"Returns a list of the SQL statements used to flush the database."
|
||||
from django.db import connection
|
||||
statements = connection.ops.sql_flush(style, table_list(), sequence_list())
|
||||
return statements
|
||||
|
||||
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:
|
||||
(list_of_sql, pending_references_dict)
|
||||
"""
|
||||
from django.db import backend, models
|
||||
from django.db import connection, models
|
||||
|
||||
opts = model._meta
|
||||
final_output = []
|
||||
table_output = []
|
||||
pending_references = {}
|
||||
qn = connection.ops.quote_name
|
||||
for f in opts.fields:
|
||||
col_type = f.db_type()
|
||||
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.
|
||||
continue
|
||||
# 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)]
|
||||
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'))
|
||||
if f.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
|
||||
# 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.to in known_models:
|
||||
field_output.append(style.SQL_KEYWORD('REFERENCES') + ' ' + \
|
||||
style.SQL_TABLE(backend.quote_name(f.rel.to._meta.db_table)) + ' (' + \
|
||||
style.SQL_FIELD(backend.quote_name(f.rel.to._meta.get_field(f.rel.field_name).column)) + ')' +
|
||||
backend.get_deferrable_sql()
|
||||
style.SQL_TABLE(qn(f.rel.to._meta.db_table)) + ' (' + \
|
||||
style.SQL_FIELD(qn(f.rel.to._meta.get_field(f.rel.field_name).column)) + ')' +
|
||||
connection.ops.deferrable_sql()
|
||||
)
|
||||
else:
|
||||
# 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))
|
||||
table_output.append(' '.join(field_output))
|
||||
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_KEYWORD('NULL'))
|
||||
for field_constraints in opts.unique_together:
|
||||
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.
|
||||
full_statement.append(' %s%s' % (line, i < len(table_output)-1 and ',' or ''))
|
||||
full_statement.append(')')
|
||||
if opts.db_tablespace and backend.supports_tablespaces:
|
||||
full_statement.append(backend.get_tablespace_sql(opts.db_tablespace))
|
||||
if opts.db_tablespace and connection.features.supports_tablespaces:
|
||||
full_statement.append(connection.ops.tablespace_sql(opts.db_tablespace))
|
||||
full_statement.append(';')
|
||||
final_output.append('\n'.join(full_statement))
|
||||
|
||||
if opts.has_auto_field and hasattr(backend, 'get_autoinc_sql'):
|
||||
# Add any extra SQL needed to support auto-incrementing primary keys
|
||||
autoinc_sql = backend.get_autoinc_sql(opts.db_table)
|
||||
if opts.has_auto_field:
|
||||
# Add any extra SQL needed to support auto-incrementing primary keys.
|
||||
autoinc_sql = connection.ops.autoinc_sql(opts.db_table)
|
||||
if autoinc_sql:
|
||||
for stmt in autoinc_sql:
|
||||
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.
|
||||
"""
|
||||
from django.db import backend
|
||||
from django.db import connection
|
||||
from django.db.backends.util import truncate_name
|
||||
|
||||
qn = connection.ops.quote_name
|
||||
final_output = []
|
||||
if backend.supports_constraints:
|
||||
if connection.features.supports_constraints:
|
||||
opts = model._meta
|
||||
if model in pending_references:
|
||||
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.
|
||||
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;' % \
|
||||
(backend.quote_name(r_table), truncate_name(r_name, backend.get_max_name_length()),
|
||||
backend.quote_name(r_col), backend.quote_name(table), backend.quote_name(col),
|
||||
backend.get_deferrable_sql()))
|
||||
(qn(r_table), truncate_name(r_name, connection.ops.max_name_length()),
|
||||
qn(r_col), qn(table), qn(col),
|
||||
connection.ops.deferrable_sql()))
|
||||
del pending_references[model]
|
||||
return final_output
|
||||
|
||||
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
|
||||
|
||||
opts = model._meta
|
||||
final_output = []
|
||||
qn = connection.ops.quote_name
|
||||
for f in opts.many_to_many:
|
||||
if not isinstance(f.rel, generic.GenericRel):
|
||||
tablespace = f.db_tablespace or opts.db_tablespace
|
||||
if tablespace and backend.supports_tablespaces and backend.autoindexes_primary_keys:
|
||||
tablespace_sql = ' ' + backend.get_tablespace_sql(tablespace, inline=True)
|
||||
if tablespace and connection.features.supports_tablespaces and connection.features.autoindexes_primary_keys:
|
||||
tablespace_sql = ' ' + connection.ops.tablespace_sql(tablespace, inline=True)
|
||||
else:
|
||||
tablespace_sql = ''
|
||||
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,' % \
|
||||
(style.SQL_FIELD(backend.quote_name('id')),
|
||||
(style.SQL_FIELD(qn('id')),
|
||||
style.SQL_COLTYPE(models.AutoField(primary_key=True).db_type()),
|
||||
style.SQL_KEYWORD('NOT NULL PRIMARY KEY'),
|
||||
tablespace_sql))
|
||||
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_KEYWORD('NOT NULL REFERENCES'),
|
||||
style.SQL_TABLE(backend.quote_name(opts.db_table)),
|
||||
style.SQL_FIELD(backend.quote_name(opts.pk.column)),
|
||||
backend.get_deferrable_sql()))
|
||||
style.SQL_TABLE(qn(opts.db_table)),
|
||||
style.SQL_FIELD(qn(opts.pk.column)),
|
||||
connection.ops.deferrable_sql()))
|
||||
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_KEYWORD('NOT NULL REFERENCES'),
|
||||
style.SQL_TABLE(backend.quote_name(f.rel.to._meta.db_table)),
|
||||
style.SQL_FIELD(backend.quote_name(f.rel.to._meta.pk.column)),
|
||||
backend.get_deferrable_sql()))
|
||||
style.SQL_TABLE(qn(f.rel.to._meta.db_table)),
|
||||
style.SQL_FIELD(qn(f.rel.to._meta.pk.column)),
|
||||
connection.ops.deferrable_sql()))
|
||||
table_output.append(' %s (%s, %s)%s' % \
|
||||
(style.SQL_KEYWORD('UNIQUE'),
|
||||
style.SQL_FIELD(backend.quote_name(f.m2m_column_name())),
|
||||
style.SQL_FIELD(backend.quote_name(f.m2m_reverse_name())),
|
||||
style.SQL_FIELD(qn(f.m2m_column_name())),
|
||||
style.SQL_FIELD(qn(f.m2m_reverse_name())),
|
||||
tablespace_sql))
|
||||
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.
|
||||
table_output.append(backend.get_tablespace_sql(opts.db_tablespace))
|
||||
table_output.append(connection.ops.tablespace_sql(opts.db_tablespace))
|
||||
table_output.append(';')
|
||||
final_output.append('\n'.join(table_output))
|
||||
|
||||
# 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:
|
||||
for stmt in autoinc_sql:
|
||||
final_output.append(stmt)
|
||||
@ -386,23 +393,24 @@ def custom_sql_for_model(model):
|
||||
|
||||
def sql_indexes_for_model(model, style):
|
||||
"Returns the CREATE INDEX SQL statements for a single model"
|
||||
from django.db import backend
|
||||
from django.db import connection
|
||||
output = []
|
||||
|
||||
qn = connection.ops.quote_name
|
||||
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 ''
|
||||
tablespace = f.db_tablespace or model._meta.db_tablespace
|
||||
if tablespace and backend.supports_tablespaces:
|
||||
tablespace_sql = ' ' + backend.get_tablespace_sql(tablespace)
|
||||
if tablespace and connection.features.supports_tablespaces:
|
||||
tablespace_sql = ' ' + connection.ops.tablespace_sql(tablespace)
|
||||
else:
|
||||
tablespace_sql = ''
|
||||
output.append(
|
||||
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_TABLE(backend.quote_name(model._meta.db_table)) + ' ' + \
|
||||
"(%s)" % style.SQL_FIELD(backend.quote_name(f.column)) + \
|
||||
style.SQL_TABLE(qn(model._meta.db_table)) + ' ' + \
|
||||
"(%s)" % style.SQL_FIELD(qn(f.column)) + \
|
||||
"%s;" % tablespace_sql
|
||||
)
|
||||
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/
|
||||
"""
|
||||
|
||||
from django.db.backends import util
|
||||
from django.db.backends import BaseDatabaseWrapper, BaseDatabaseFeatures, BaseDatabaseOperations, util
|
||||
try:
|
||||
import adodbapi as Database
|
||||
except ImportError, e:
|
||||
from django.core.exceptions import ImproperlyConfigured
|
||||
raise ImproperlyConfigured, "Error loading adodbapi module: %s" % e
|
||||
raise ImproperlyConfigured("Error loading adodbapi module: %s" % e)
|
||||
import datetime
|
||||
try:
|
||||
import mx
|
||||
@ -48,148 +48,65 @@ def variantToPython(variant, adType):
|
||||
return res
|
||||
Database.convertVariantToPython = variantToPython
|
||||
|
||||
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 DatabaseFeatures(BaseDatabaseFeatures):
|
||||
supports_tablespaces = True
|
||||
|
||||
class DatabaseWrapper(local):
|
||||
def __init__(self, **kwargs):
|
||||
self.connection = None
|
||||
self.queries = []
|
||||
class DatabaseOperations(BaseDatabaseOperations):
|
||||
def date_extract_sql(self, lookup_type, field_name):
|
||||
return "DATEPART(%s, %s)" % (lookup_type, field_name)
|
||||
|
||||
def cursor(self):
|
||||
from django.conf import settings
|
||||
def date_trunc_sql(self, lookup_type, field_name):
|
||||
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 deferrable_sql(self):
|
||||
return " DEFERRABLE INITIALLY DEFERRED"
|
||||
|
||||
def last_insert_id(self, cursor, table_name, pk_name):
|
||||
cursor.execute("SELECT %s FROM %s WHERE %s = @@IDENTITY" % (pk_name, 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 random_function_sql(self):
|
||||
return 'RAND()'
|
||||
|
||||
def tablespace_sql(self, tablespace, inline=False):
|
||||
return "ON %s" % self.quote_name(tablespace)
|
||||
|
||||
class DatabaseWrapper(BaseDatabaseWrapper):
|
||||
features = DatabaseFeatures()
|
||||
ops = DatabaseOperations()
|
||||
operators = {
|
||||
'exact': '= %s',
|
||||
'iexact': 'LIKE %s',
|
||||
'contains': 'LIKE %s',
|
||||
'icontains': 'LIKE %s',
|
||||
'gt': '> %s',
|
||||
'gte': '>= %s',
|
||||
'lt': '< %s',
|
||||
'lte': '<= %s',
|
||||
'startswith': 'LIKE %s',
|
||||
'endswith': 'LIKE %s',
|
||||
'istartswith': '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."
|
||||
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)
|
||||
cursor = self.connection.cursor()
|
||||
if settings.DEBUG:
|
||||
return util.CursorDebugWrapper(cursor, self)
|
||||
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 = True
|
||||
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 %s FROM %s WHERE %s = @@IDENTITY" % (pk_name, table_name, pk_name))
|
||||
return cursor.fetchone()[0]
|
||||
|
||||
def get_date_extract_sql(lookup_type, table_name):
|
||||
# lookup_type is 'year', 'month', 'day'
|
||||
return "DATEPART(%s, %s)" % (lookup_type, table_name)
|
||||
|
||||
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',
|
||||
'iexact': 'LIKE %s',
|
||||
'contains': 'LIKE %s',
|
||||
'icontains': 'LIKE %s',
|
||||
'gt': '> %s',
|
||||
'gte': '>= %s',
|
||||
'lt': '< %s',
|
||||
'lte': '<= %s',
|
||||
'startswith': 'LIKE %s',
|
||||
'endswith': 'LIKE %s',
|
||||
'istartswith': 'LIKE %s',
|
||||
'iendswith': 'LIKE %s',
|
||||
}
|
||||
return self.connection.cursor()
|
||||
|
@ -21,7 +21,14 @@ class DatabaseError(Exception):
|
||||
class IntegrityError(DatabaseError):
|
||||
pass
|
||||
|
||||
class DatabaseWrapper:
|
||||
class ComplainOnGetattr(object):
|
||||
def __getattr__(self, *args, **kwargs):
|
||||
complain()
|
||||
|
||||
class DatabaseWrapper(object):
|
||||
features = ComplainOnGetattr()
|
||||
ops = ComplainOnGetattr()
|
||||
operators = {}
|
||||
cursor = complain
|
||||
_commit = complain
|
||||
_rollback = ignore
|
||||
@ -30,28 +37,4 @@ class DatabaseWrapper:
|
||||
pass
|
||||
|
||||
def close(self):
|
||||
pass # close()
|
||||
|
||||
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 = {}
|
||||
pass
|
||||
|
@ -4,12 +4,12 @@ MySQL database backend for Django.
|
||||
Requires MySQLdb: http://sourceforge.net/projects/mysql-python
|
||||
"""
|
||||
|
||||
from django.db.backends import util
|
||||
from django.db.backends import BaseDatabaseWrapper, BaseDatabaseFeatures, BaseDatabaseOperations, util
|
||||
try:
|
||||
import MySQLdb as Database
|
||||
except ImportError, e:
|
||||
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
|
||||
# lexicographic ordering in this check because then (1, 2, 1, 'gamma')
|
||||
@ -17,7 +17,7 @@ except ImportError, e:
|
||||
version = Database.version_info
|
||||
if (version < (1,2,1) or (version[:3] == (1, 2, 1) and
|
||||
(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.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
|
||||
# TRADITIONAL will automatically cause most warnings to be treated as errors.
|
||||
|
||||
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 DatabaseFeatures(BaseDatabaseFeatures):
|
||||
autoindexes_primary_keys = False
|
||||
|
||||
class DatabaseOperations(BaseDatabaseOperations):
|
||||
def date_extract_sql(self, lookup_type, field_name):
|
||||
# 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):
|
||||
self.connection = None
|
||||
self.queries = []
|
||||
super(DatabaseWrapper, self).__init__(**kwargs)
|
||||
self.server_version = None
|
||||
self.options = kwargs
|
||||
|
||||
def _valid_connection(self):
|
||||
if self.connection is not None:
|
||||
@ -77,8 +152,7 @@ class DatabaseWrapper(local):
|
||||
self.connection = None
|
||||
return False
|
||||
|
||||
def cursor(self):
|
||||
from django.conf import settings
|
||||
def _cursor(self, settings):
|
||||
from warnings import filterwarnings
|
||||
if not self._valid_connection():
|
||||
kwargs = {
|
||||
@ -100,29 +174,16 @@ class DatabaseWrapper(local):
|
||||
kwargs['port'] = int(settings.DATABASE_PORT)
|
||||
kwargs.update(self.options)
|
||||
self.connection = Database.connect(**kwargs)
|
||||
cursor = self.connection.cursor()
|
||||
else:
|
||||
cursor = self.connection.cursor()
|
||||
cursor = self.connection.cursor()
|
||||
if settings.DEBUG:
|
||||
filterwarnings("error", category=Database.Warning)
|
||||
return util.CursorDebugWrapper(cursor, self)
|
||||
return cursor
|
||||
|
||||
def _commit(self):
|
||||
if self.connection is not None:
|
||||
self.connection.commit()
|
||||
|
||||
def _rollback(self):
|
||||
if self.connection is not None:
|
||||
try:
|
||||
self.connection.rollback()
|
||||
except Database.NotSupportedError:
|
||||
pass
|
||||
|
||||
def close(self):
|
||||
if self.connection is not None:
|
||||
self.connection.close()
|
||||
self.connection = None
|
||||
try:
|
||||
BaseDatabaseWrapper._rollback(self)
|
||||
except Database.NotSupportedError:
|
||||
pass
|
||||
|
||||
def get_server_version(self):
|
||||
if not self.server_version:
|
||||
@ -133,128 +194,3 @@ class DatabaseWrapper(local):
|
||||
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()])
|
||||
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.constants import FIELD_TYPE
|
||||
import re
|
||||
|
||||
quote_name = DatabaseOperations().quote_name
|
||||
foreign_key_re = re.compile(r"\sCONSTRAINT `[^`]*` FOREIGN KEY \(`([^`]*)`\) REFERENCES `([^`]*)` \(`([^`]*)`\)")
|
||||
|
||||
def get_table_list(cursor):
|
||||
|
@ -4,13 +4,13 @@ MySQL database backend for Django.
|
||||
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
|
||||
try:
|
||||
import MySQLdb as Database
|
||||
except ImportError, e:
|
||||
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.constants import FIELD_TYPE
|
||||
import types
|
||||
@ -48,14 +48,14 @@ class MysqlDebugWrapper:
|
||||
return self.cursor.execute(sql, params)
|
||||
except Database.Warning, w:
|
||||
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):
|
||||
try:
|
||||
return self.cursor.executemany(sql, param_list)
|
||||
except Database.Warning, w:
|
||||
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):
|
||||
if attr in self.__dict__:
|
||||
@ -63,19 +63,94 @@ class MysqlDebugWrapper:
|
||||
else:
|
||||
return getattr(self.cursor, attr)
|
||||
|
||||
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 DatabaseFeatures(BaseDatabaseFeatures):
|
||||
autoindexes_primary_keys = False
|
||||
|
||||
class DatabaseOperations(BaseDatabaseOperations):
|
||||
def date_extract_sql(self, lookup_type, field_name):
|
||||
# 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):
|
||||
self.connection = None
|
||||
self.queries = []
|
||||
super(DatabaseWrapper, self).__init__(**kwargs)
|
||||
self.server_version = None
|
||||
self.options = kwargs
|
||||
|
||||
def _valid_connection(self):
|
||||
if self.connection is not None:
|
||||
@ -87,8 +162,7 @@ class DatabaseWrapper(local):
|
||||
self.connection = None
|
||||
return False
|
||||
|
||||
def cursor(self):
|
||||
from django.conf import settings
|
||||
def _cursor(self, settings):
|
||||
if not self._valid_connection():
|
||||
kwargs = {
|
||||
# Note: use_unicode intentonally not set to work around some
|
||||
@ -119,25 +193,16 @@ class DatabaseWrapper(local):
|
||||
self.connection.set_character_set('utf8')
|
||||
else:
|
||||
cursor = self.connection.cursor()
|
||||
if settings.DEBUG:
|
||||
return util.CursorDebugWrapper(MysqlDebugWrapper(cursor), self)
|
||||
return cursor
|
||||
|
||||
def _commit(self):
|
||||
if self.connection is not None:
|
||||
self.connection.commit()
|
||||
def make_debug_cursor(self, cursor):
|
||||
return BaseDatabaseWrapper.make_debug_cursor(self, MysqlDebugWrapper(cursor))
|
||||
|
||||
def _rollback(self):
|
||||
if self.connection is not None:
|
||||
try:
|
||||
self.connection.rollback()
|
||||
except Database.NotSupportedError:
|
||||
pass
|
||||
|
||||
def close(self):
|
||||
if self.connection is not None:
|
||||
self.connection.close()
|
||||
self.connection = None
|
||||
try:
|
||||
BaseDatabaseWrapper._rollback(self)
|
||||
except Database.NotSupportedError:
|
||||
pass
|
||||
|
||||
def get_server_version(self):
|
||||
if not self.server_version:
|
||||
@ -148,128 +213,3 @@ class DatabaseWrapper(local):
|
||||
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()])
|
||||
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.constants import FIELD_TYPE
|
||||
import re
|
||||
|
||||
quote_name = DatabaseOperations().quote_name
|
||||
foreign_key_re = re.compile(r"\sCONSTRAINT `[^`]*` FOREIGN KEY \(`([^`]*)`\) REFERENCES `([^`]*)` \(`([^`]*)`\)")
|
||||
|
||||
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/
|
||||
"""
|
||||
|
||||
from django.conf import settings
|
||||
from django.db.backends import util
|
||||
from django.db.backends import BaseDatabaseWrapper, BaseDatabaseFeatures, BaseDatabaseOperations, util
|
||||
from django.utils.datastructures import SortedDict
|
||||
from django.utils.encoding import smart_str, force_unicode
|
||||
import datetime
|
||||
@ -17,29 +16,392 @@ try:
|
||||
import cx_Oracle as Database
|
||||
except ImportError, e:
|
||||
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
|
||||
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 DatabaseFeatures(BaseDatabaseFeatures):
|
||||
allows_group_by_ordinal = False
|
||||
allows_unique_and_pk = False # Suppress UNIQUE/PK for Oracle (ORA-02259)
|
||||
needs_datetime_string_cast = False
|
||||
needs_upper_for_iops = True
|
||||
supports_tablespaces = True
|
||||
uses_case_insensitive_names = True
|
||||
uses_custom_queryset = True
|
||||
|
||||
class DatabaseWrapper(local):
|
||||
def __init__(self, **kwargs):
|
||||
self.connection = None
|
||||
self.queries = []
|
||||
self.options = kwargs
|
||||
class DatabaseOperations(BaseDatabaseOperations):
|
||||
def autoinc_sql(self, 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, 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):
|
||||
return self.connection is not None
|
||||
|
||||
def cursor(self):
|
||||
def _cursor(self, settings):
|
||||
if not self._valid_connection():
|
||||
if len(settings.DATABASE_HOST.strip()) == 0:
|
||||
settings.DATABASE_HOST = 'localhost'
|
||||
@ -55,32 +417,8 @@ class DatabaseWrapper(local):
|
||||
# Set oracle date to ansi date format.
|
||||
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'")
|
||||
if settings.DEBUG:
|
||||
return util.CursorDebugWrapper(cursor, self)
|
||||
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):
|
||||
"""
|
||||
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 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():
|
||||
# TODO: colorize this SQL code with style.SQL_KEYWORD(), etc.
|
||||
return """
|
||||
@ -249,310 +503,10 @@ def _get_sequence_reset_sql():
|
||||
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):
|
||||
name_length = get_max_name_length() - 3
|
||||
name_length = DatabaseOperations().max_name_length() - 3
|
||||
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):
|
||||
name_length = get_max_name_length() - 3
|
||||
name_length = DatabaseOperations().max_name_length() - 3
|
||||
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'
|
||||
REMEMBER = {}
|
||||
|
||||
|
||||
def create_test_db(settings, connection, backend, verbosity=1, autoclobber=False):
|
||||
|
||||
def create_test_db(settings, connection, verbosity=1, autoclobber=False):
|
||||
TEST_DATABASE_NAME = _test_database_name(settings)
|
||||
TEST_DATABASE_USER = _test_database_user(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.
|
||||
cursor = connection.cursor()
|
||||
|
||||
|
||||
def destroy_test_db(settings, connection, backend, old_database_name, verbosity=1):
|
||||
def destroy_test_db(settings, connection, old_database_name, verbosity=1):
|
||||
connection.close()
|
||||
|
||||
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)
|
||||
connection.close()
|
||||
|
||||
|
||||
def _create_test_db(cursor, parameters, verbosity):
|
||||
if verbosity >= 2:
|
||||
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)
|
||||
|
||||
|
||||
def _create_test_user(cursor, parameters, verbosity):
|
||||
if verbosity >= 2:
|
||||
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)
|
||||
|
||||
|
||||
def _destroy_test_db(cursor, parameters, verbosity):
|
||||
if verbosity >= 2:
|
||||
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)
|
||||
|
||||
|
||||
def _destroy_test_user(cursor, parameters, verbosity):
|
||||
if verbosity >= 2:
|
||||
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)
|
||||
|
||||
|
||||
def _execute_statements(cursor, statements, parameters, verbosity):
|
||||
for template in statements:
|
||||
stmt = template % parameters
|
||||
@ -214,7 +206,6 @@ def _execute_statements(cursor, statements, parameters, verbosity):
|
||||
sys.stderr.write("Failed (%s)\n" % (err))
|
||||
raise
|
||||
|
||||
|
||||
def _test_database_name(settings):
|
||||
name = TEST_DATABASE_PREFIX + settings.DATABASE_NAME
|
||||
try:
|
||||
@ -226,7 +217,6 @@ def _test_database_name(settings):
|
||||
raise
|
||||
return name
|
||||
|
||||
|
||||
def _test_database_create(settings):
|
||||
name = True
|
||||
try:
|
||||
@ -240,7 +230,6 @@ def _test_database_create(settings):
|
||||
raise
|
||||
return name
|
||||
|
||||
|
||||
def _test_user_create(settings):
|
||||
name = True
|
||||
try:
|
||||
@ -254,7 +243,6 @@ def _test_user_create(settings):
|
||||
raise
|
||||
return name
|
||||
|
||||
|
||||
def _test_database_user(settings):
|
||||
name = TEST_DATABASE_PREFIX + settings.DATABASE_NAME
|
||||
try:
|
||||
@ -266,7 +254,6 @@ def _test_database_user(settings):
|
||||
raise
|
||||
return name
|
||||
|
||||
|
||||
def _test_database_passwd(settings):
|
||||
name = PASSWORD
|
||||
try:
|
||||
@ -278,7 +265,6 @@ def _test_database_passwd(settings):
|
||||
raise
|
||||
return name
|
||||
|
||||
|
||||
def _test_database_tblspace(settings):
|
||||
name = TEST_DATABASE_PREFIX + settings.DATABASE_NAME
|
||||
try:
|
||||
@ -290,7 +276,6 @@ def _test_database_tblspace(settings):
|
||||
raise
|
||||
return name
|
||||
|
||||
|
||||
def _test_database_tblspace_tmp(settings):
|
||||
name = TEST_DATABASE_PREFIX + settings.DATABASE_NAME + '_temp'
|
||||
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 cx_Oracle
|
||||
|
||||
|
||||
quote_name = DatabaseOperations().quote_name
|
||||
foreign_key_re = re.compile(r"\sCONSTRAINT `[^`]*` FOREIGN KEY \(`([^`]*)`\) REFERENCES `([^`]*)` \(`([^`]*)`\)")
|
||||
|
||||
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.db.backends import util
|
||||
from django.db.backends import BaseDatabaseWrapper, BaseDatabaseFeatures, util
|
||||
from django.db.backends.postgresql.operations import DatabaseOperations
|
||||
try:
|
||||
import psycopg as Database
|
||||
except ImportError, e:
|
||||
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
|
||||
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):
|
||||
"""
|
||||
A thin wrapper around psycopg cursors that allows them to accept Unicode
|
||||
@ -62,22 +56,36 @@ class UnicodeCursorWrapper(object):
|
||||
else:
|
||||
return getattr(self.cursor, attr)
|
||||
|
||||
postgres_version = None
|
||||
class DatabaseFeatures(BaseDatabaseFeatures):
|
||||
pass # This backend uses all the defaults.
|
||||
|
||||
class DatabaseWrapper(local):
|
||||
def __init__(self, **kwargs):
|
||||
self.connection = None
|
||||
self.queries = []
|
||||
self.options = kwargs
|
||||
class DatabaseWrapper(BaseDatabaseWrapper):
|
||||
features = DatabaseFeatures()
|
||||
ops = DatabaseOperations()
|
||||
operators = {
|
||||
'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):
|
||||
from django.conf import settings
|
||||
def _cursor(self, settings):
|
||||
set_tz = False
|
||||
if self.connection is None:
|
||||
set_tz = True
|
||||
if settings.DATABASE_NAME == '':
|
||||
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
|
||||
if settings.DATABASE_USER:
|
||||
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 client_encoding to 'UNICODE'")
|
||||
cursor = UnicodeCursorWrapper(cursor, 'utf-8')
|
||||
global postgres_version
|
||||
if not postgres_version:
|
||||
if self.ops.postgres_version is None:
|
||||
cursor.execute("SELECT version()")
|
||||
postgres_version = [int(val) for val in cursor.fetchone()[0].split()[1].split('.')]
|
||||
if settings.DEBUG:
|
||||
return util.CursorDebugWrapper(cursor, self)
|
||||
self.ops.postgres_version = [int(val) for val in cursor.fetchone()[0].split()[1].split('.')]
|
||||
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):
|
||||
"""
|
||||
Cast all returned strings to unicode strings.
|
||||
@ -288,26 +121,9 @@ def typecast_string(s):
|
||||
try:
|
||||
Database.register_type(Database.new_type((1082,), "DATE", util.typecast_date))
|
||||
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((1114,1184), "TIMESTAMP", util.typecast_timestamp))
|
||||
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(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):
|
||||
"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
|
||||
"""
|
||||
|
||||
from django.db.backends import util
|
||||
from django.db.backends import BaseDatabaseWrapper, BaseDatabaseFeatures
|
||||
from django.db.backends.postgresql.operations import DatabaseOperations
|
||||
try:
|
||||
import psycopg2 as Database
|
||||
import psycopg2.extensions
|
||||
except ImportError, e:
|
||||
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
|
||||
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)
|
||||
|
||||
postgres_version = None
|
||||
class DatabaseFeatures(BaseDatabaseFeatures):
|
||||
needs_datetime_string_cast = False
|
||||
|
||||
class DatabaseWrapper(local):
|
||||
def __init__(self, **kwargs):
|
||||
self.connection = None
|
||||
self.queries = []
|
||||
self.options = kwargs
|
||||
class DatabaseWrapper(BaseDatabaseWrapper):
|
||||
features = DatabaseFeatures()
|
||||
ops = DatabaseOperations()
|
||||
operators = {
|
||||
'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):
|
||||
from django.conf import settings
|
||||
def _cursor(self, settings):
|
||||
set_tz = False
|
||||
if self.connection is None:
|
||||
set_tz = True
|
||||
if settings.DATABASE_NAME == '':
|
||||
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
|
||||
if settings.DATABASE_USER:
|
||||
conn_string = "user=%s %s" % (settings.DATABASE_USER, conn_string)
|
||||
@ -56,187 +64,7 @@ class DatabaseWrapper(local):
|
||||
cursor.tzinfo_factory = None
|
||||
if set_tz:
|
||||
cursor.execute("SET TIME ZONE %s", [settings.TIME_ZONE])
|
||||
global postgres_version
|
||||
if not postgres_version:
|
||||
if self.ops.postgres_version is None:
|
||||
cursor.execute("SELECT version()")
|
||||
postgres_version = [int(val) for val in cursor.fetchone()[0].split()[1].split('.')]
|
||||
if settings.DEBUG:
|
||||
return util.CursorDebugWrapper(cursor, self)
|
||||
self.ops.postgres_version = [int(val) for val in cursor.fetchone()[0].split()[1].split('.')]
|
||||
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):
|
||||
"Returns a list of table names in the current database."
|
||||
|
@ -2,7 +2,7 @@
|
||||
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:
|
||||
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_adapter(decimal.Decimal, util.rev_typecast_decimal)
|
||||
|
||||
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 DatabaseFeatures(BaseDatabaseFeatures):
|
||||
supports_constraints = False
|
||||
|
||||
class DatabaseWrapper(local):
|
||||
def __init__(self, **kwargs):
|
||||
self.connection = None
|
||||
self.queries = []
|
||||
self.options = kwargs
|
||||
class DatabaseOperations(BaseDatabaseOperations):
|
||||
def date_extract_sql(self, lookup_type, field_name):
|
||||
# sqlite doesn't support extract, so we fake it with the user-defined
|
||||
# function django_extract that's registered in connect().
|
||||
return 'django_extract("%s", %s)' % (lookup_type.lower(), field_name)
|
||||
|
||||
def cursor(self):
|
||||
from django.conf import settings
|
||||
def date_trunc_sql(self, lookup_type, field_name):
|
||||
# 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:
|
||||
kwargs = {
|
||||
'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_date_trunc", 2, _sqlite_date_trunc)
|
||||
self.connection.create_function("regexp", 2, _sqlite_regexp)
|
||||
cursor = 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()
|
||||
return self.connection.cursor(factory=SQLiteCursorWrapper)
|
||||
|
||||
def close(self):
|
||||
from django.conf import settings
|
||||
# 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.
|
||||
if self.connection is not None and settings.DATABASE_NAME != ":memory:":
|
||||
self.connection.close()
|
||||
self.connection = None
|
||||
if settings.DATABASE_NAME != ":memory:":
|
||||
BaseDatabaseWrapper.close(self)
|
||||
|
||||
class SQLiteCursorWrapper(Database.Cursor):
|
||||
"""
|
||||
@ -100,33 +135,6 @@ class SQLiteCursorWrapper(Database.Cursor):
|
||||
def convert_query(self, query, 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):
|
||||
try:
|
||||
dt = util.typecast_timestamp(dt)
|
||||
@ -134,67 +142,6 @@ def _sqlite_extract(lookup_type, dt):
|
||||
return None
|
||||
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):
|
||||
try:
|
||||
dt = util.typecast_timestamp(dt)
|
||||
@ -213,24 +160,3 @@ def _sqlite_regexp(re_pattern, re_string):
|
||||
return bool(re.search(re_pattern, re_string))
|
||||
except:
|
||||
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):
|
||||
"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]
|
||||
|
||||
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.core.exceptions import ObjectDoesNotExist, ImproperlyConfigured
|
||||
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.query import Q
|
||||
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.query import delete_objects
|
||||
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.loading import register_models, get_model
|
||||
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]
|
||||
cursor = connection.cursor()
|
||||
|
||||
qn = connection.ops.quote_name
|
||||
|
||||
# First, try an UPDATE. If that doesn't update anything, do an INSERT.
|
||||
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
|
||||
if pk_set:
|
||||
# Determine whether a record with the primary key already exists.
|
||||
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))
|
||||
# If it does already exist, do an UPDATE.
|
||||
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]
|
||||
if db_values:
|
||||
cursor.execute("UPDATE %s SET %s WHERE %s=%%s" % \
|
||||
(backend.quote_name(self._meta.db_table),
|
||||
','.join(['%s=%%s' % backend.quote_name(f.column) for f in non_pks]),
|
||||
backend.quote_name(self._meta.pk.column)),
|
||||
(qn(self._meta.db_table),
|
||||
','.join(['%s=%%s' % qn(f.column) for f in non_pks]),
|
||||
qn(self._meta.pk.column)),
|
||||
db_values + self._meta.pk.get_db_prep_lookup('exact', pk_val))
|
||||
else:
|
||||
record_exists = False
|
||||
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)]
|
||||
# If the PK has been manually set, respect that.
|
||||
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)]
|
||||
placeholders = ['%s'] * len(field_names)
|
||||
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.
|
||||
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))
|
||||
if db_values:
|
||||
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)
|
||||
else:
|
||||
# Create a new record with defaults for everything.
|
||||
cursor.execute("INSERT INTO %s (%s) VALUES (%s)" %
|
||||
(backend.quote_name(self._meta.db_table),
|
||||
backend.quote_name(self._meta.pk.column),
|
||||
backend.get_pk_default_value()))
|
||||
(qn(self._meta.db_table), qn(self._meta.pk.column),
|
||||
connection.ops.pk_default_value()))
|
||||
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()
|
||||
|
||||
# Run any post-save hooks.
|
||||
@ -329,10 +332,11 @@ class Model(object):
|
||||
return force_unicode(dict(field.choices).get(value, value), strings_only=True)
|
||||
|
||||
def _get_next_or_previous_by_FIELD(self, field, is_next, **kwargs):
|
||||
qn = connection.ops.quote_name
|
||||
op = is_next and '>' or '<'
|
||||
where = '(%s %s %%s OR (%s = %%s AND %s.%s %s %%s))' % \
|
||||
(backend.quote_name(field.column), op, backend.quote_name(field.column),
|
||||
backend.quote_name(self._meta.db_table), backend.quote_name(self._meta.pk.column), op)
|
||||
(qn(field.column), op, qn(field.column),
|
||||
qn(self._meta.db_table), qn(self._meta.pk.column), op)
|
||||
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._where.append(where)
|
||||
@ -343,14 +347,15 @@ class Model(object):
|
||||
raise self.DoesNotExist, "%s matching query does not exist." % self.__class__._meta.object_name
|
||||
|
||||
def _get_next_or_previous_in_order(self, is_next):
|
||||
qn = connection.ops.quote_name
|
||||
cachename = "__%s_order_cache" % is_next
|
||||
if not hasattr(self, cachename):
|
||||
op = is_next and '>' or '<'
|
||||
order_field = self._meta.order_with_respect_to
|
||||
where = ['%s %s (SELECT %s FROM %s WHERE %s=%%s)' % \
|
||||
(backend.quote_name('_order'), op, backend.quote_name('_order'),
|
||||
backend.quote_name(self._meta.db_table), backend.quote_name(self._meta.pk.column)),
|
||||
'%s=%%s' % backend.quote_name(order_field.column)]
|
||||
(qn('_order'), op, qn('_order'),
|
||||
qn(self._meta.db_table), qn(self._meta.pk.column)),
|
||||
'%s=%%s' % qn(order_field.column)]
|
||||
params = [self._get_pk_val(), getattr(self, order_field.attname)]
|
||||
obj = self._default_manager.order_by('_order').extra(where=where, params=params)[:1].get()
|
||||
setattr(self, cachename, obj)
|
||||
@ -432,24 +437,26 @@ class Model(object):
|
||||
# ORDERING METHODS #########################
|
||||
|
||||
def method_set_order(ordered_obj, self, id_list):
|
||||
qn = connection.ops.quote_name
|
||||
cursor = connection.cursor()
|
||||
# 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" % \
|
||||
(backend.quote_name(ordered_obj._meta.db_table), backend.quote_name('_order'),
|
||||
backend.quote_name(ordered_obj._meta.order_with_respect_to.column),
|
||||
backend.quote_name(ordered_obj._meta.pk.column))
|
||||
(qn(ordered_obj._meta.db_table), qn('_order'),
|
||||
qn(ordered_obj._meta.order_with_respect_to.column),
|
||||
qn(ordered_obj._meta.pk.column))
|
||||
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)])
|
||||
transaction.commit_unless_managed()
|
||||
|
||||
def method_get_order(ordered_obj, self):
|
||||
qn = connection.ops.quote_name
|
||||
cursor = connection.cursor()
|
||||
# Example: "SELECT id FROM poll_choices WHERE poll_id = %s ORDER BY _order"
|
||||
sql = "SELECT %s FROM %s WHERE %s = %%s ORDER BY %s" % \
|
||||
(backend.quote_name(ordered_obj._meta.pk.column),
|
||||
backend.quote_name(ordered_obj._meta.db_table),
|
||||
backend.quote_name(ordered_obj._meta.order_with_respect_to.column),
|
||||
backend.quote_name('_order'))
|
||||
(qn(ordered_obj._meta.pk.column),
|
||||
qn(ordered_obj._meta.db_table),
|
||||
qn(ordered_obj._meta.order_with_respect_to.column),
|
||||
qn('_order'))
|
||||
rel_val = getattr(self, ordered_obj._meta.order_with_respect_to.rel.field_name)
|
||||
cursor.execute(sql, [rel_val])
|
||||
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.fields import AutoField, Field, IntegerField, PositiveIntegerField, PositiveSmallIntegerField, get_ul_class
|
||||
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
|
||||
# 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.
|
||||
from django.db import connection
|
||||
|
||||
# If there aren't any objects, there is nothing to do.
|
||||
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
|
||||
# target_col_name: the PK colname in join_table for the target object
|
||||
# *objs - objects to remove
|
||||
from django.db import connection
|
||||
|
||||
# If there aren't any objects, there is nothing to do.
|
||||
if objs:
|
||||
@ -371,7 +369,6 @@ def create_many_related_manager(superclass):
|
||||
|
||||
def _clear_items(self, source_col_name):
|
||||
# source_col_name: the PK colname in join_table for the source object
|
||||
from django.db import connection
|
||||
cursor = connection.cursor()
|
||||
cursor.execute("DELETE FROM %s WHERE %s = %%s" % \
|
||||
(self.join_table, source_col_name),
|
||||
@ -400,7 +397,7 @@ class ManyRelatedObjectsDescriptor(object):
|
||||
superclass = rel_model._default_manager.__class__
|
||||
RelatedManager = create_many_related_manager(superclass)
|
||||
|
||||
qn = backend.quote_name
|
||||
qn = connection.ops.quote_name
|
||||
manager = RelatedManager(
|
||||
model=rel_model,
|
||||
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__
|
||||
RelatedManager = create_many_related_manager(superclass)
|
||||
|
||||
qn = backend.quote_name
|
||||
qn = connection.ops.quote_name
|
||||
manager = RelatedManager(
|
||||
model=rel_model,
|
||||
core_filters={'%s__pk' % self.field.related_query_name(): instance._get_pk_val()},
|
||||
|
@ -4,113 +4,188 @@ from django.conf import settings
|
||||
from django.core.exceptions import ImproperlyConfigured
|
||||
import sys
|
||||
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.
|
||||
# Entry is not placed in app_list cache until entire app is loaded.
|
||||
_app_models = {} # Dictionary of models against app label
|
||||
# Each value is a dictionary of model name: model class
|
||||
# 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
|
||||
# Key is the app_name of the model, value is the exception that was raised
|
||||
# during model loading.
|
||||
_loaded = False # Has the contents of settings.INSTALLED_APPS been loaded?
|
||||
# i.e., has get_apps() been called?
|
||||
|
||||
def get_apps():
|
||||
"Returns a list of all installed modules that contain models."
|
||||
global _app_list
|
||||
global _loaded
|
||||
if not _loaded:
|
||||
_loaded = True
|
||||
for app_name in settings.INSTALLED_APPS:
|
||||
try:
|
||||
load_app(app_name)
|
||||
except Exception, e:
|
||||
# Problem importing the app
|
||||
_app_errors[app_name] = e
|
||||
return _app_list
|
||||
|
||||
def get_app(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."
|
||||
get_apps() # Run get_apps() to populate the _app_list cache. Slightly hackish.
|
||||
for app_name in settings.INSTALLED_APPS:
|
||||
if app_label == app_name.split('.')[-1]:
|
||||
mod = load_app(app_name)
|
||||
if mod is None:
|
||||
if emptyOK:
|
||||
return None
|
||||
else:
|
||||
return mod
|
||||
raise ImproperlyConfigured, "App with label %s could not be found" % app_label
|
||||
|
||||
def load_app(app_name):
|
||||
"Loads the app with the provided fully qualified name, and returns the model module."
|
||||
global _app_list
|
||||
mod = __import__(app_name, {}, {}, ['models'])
|
||||
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():
|
||||
"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):
|
||||
class AppCache(object):
|
||||
"""
|
||||
Given a module containing models, returns a list of the models. Otherwise
|
||||
returns a list of all installed models.
|
||||
A cache that stores installed applications and their models. Used to
|
||||
provide reverse-relations and for app introspection (e.g. admin).
|
||||
"""
|
||||
app_list = get_apps() # Run get_apps() to populate the _app_list cache. Slightly hackish.
|
||||
if app_mod:
|
||||
return _app_models.get(app_mod.__name__.split('.')[-2], {}).values()
|
||||
else:
|
||||
model_list = []
|
||||
for app_mod in app_list:
|
||||
model_list.extend(get_models(app_mod))
|
||||
return model_list
|
||||
# Use the Borg pattern to share state between all instances. Details at
|
||||
# http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/66531.
|
||||
__shared_state = dict(
|
||||
# Keys of app_store are the model modules for each application.
|
||||
app_store = {},
|
||||
|
||||
def get_model(app_label, model_name, seed_cache=True):
|
||||
"""
|
||||
Returns the model matching the given app_label and case-insensitive
|
||||
model_name.
|
||||
# Mapping of app_labels to a dictionary of model names to model code.
|
||||
app_models = {},
|
||||
|
||||
Returns None if no model is found.
|
||||
"""
|
||||
if seed_cache:
|
||||
get_apps()
|
||||
try:
|
||||
model_dict = _app_models[app_label]
|
||||
except KeyError:
|
||||
return None
|
||||
# Mapping of app_labels to errors raised when trying to import the app.
|
||||
app_errors = {},
|
||||
|
||||
try:
|
||||
return model_dict[model_name.lower()]
|
||||
except KeyError:
|
||||
return None
|
||||
# -- Everything below here is only used when populating the cache --
|
||||
loaded = False,
|
||||
handled = {},
|
||||
postponed = [],
|
||||
nesting_level = 0,
|
||||
write_lock = threading.RLock(),
|
||||
)
|
||||
|
||||
def register_models(app_label, *models):
|
||||
"""
|
||||
Register a set of models as belonging to an app.
|
||||
"""
|
||||
for model in models:
|
||||
# Store as 'name: model' pair in a dictionary
|
||||
# in the _app_models dictionary
|
||||
model_name = model._meta.object_name.lower()
|
||||
model_dict = _app_models.setdefault(app_label, {})
|
||||
if model_name in model_dict:
|
||||
# The same model may be imported via different paths (e.g.
|
||||
# appname.models and project.appname.models). We use the source
|
||||
# filename as a means to detect identity.
|
||||
fname1 = os.path.abspath(sys.modules[model.__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
|
||||
# or .pyo the second time, ignore the extension when comparing.
|
||||
if os.path.splitext(fname1)[0] == os.path.splitext(fname2)[0]:
|
||||
continue
|
||||
model_dict[model_name] = model
|
||||
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:
|
||||
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:
|
||||
# Problem importing the app
|
||||
self.app_errors[app_name] = e
|
||||
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 load_app(self, app_name, can_postpone=False):
|
||||
"""
|
||||
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:
|
||||
if app_label == app_name.split('.')[-1]:
|
||||
mod = self.load_app(app_name, False)
|
||||
if mod is None:
|
||||
if emptyOK:
|
||||
return None
|
||||
else:
|
||||
return mod
|
||||
raise ImproperlyConfigured, "App with label %s could not be found" % app_label
|
||||
finally:
|
||||
self.write_lock.release()
|
||||
|
||||
def get_app_errors(self):
|
||||
"Returns the map of known problems with the INSTALLED_APPS."
|
||||
self._populate()
|
||||
return self.app_errors
|
||||
|
||||
def get_models(self, app_mod=None):
|
||||
"""
|
||||
Given a module containing models, returns a list of the models.
|
||||
Otherwise returns a list of all installed models.
|
||||
"""
|
||||
self._populate()
|
||||
if app_mod:
|
||||
return self.app_models.get(app_mod.__name__.split('.')[-2], {}).values()
|
||||
else:
|
||||
model_list = []
|
||||
for app_entry in self.app_models.itervalues():
|
||||
model_list.extend(app_entry.values())
|
||||
return model_list
|
||||
|
||||
def get_model(self, app_label, model_name, seed_cache=True):
|
||||
"""
|
||||
Returns the model matching the given app_label and case-insensitive
|
||||
model_name.
|
||||
|
||||
Returns None if no model is found.
|
||||
"""
|
||||
if seed_cache:
|
||||
self._populate()
|
||||
return self.app_models.get(app_label, {}).get(model_name.lower())
|
||||
|
||||
def register_models(self, app_label, *models):
|
||||
"""
|
||||
Register a set of models as belonging to an app.
|
||||
"""
|
||||
for model in models:
|
||||
# Store as 'name: model' pair in a dictionary
|
||||
# in the _app_models dictionary
|
||||
model_name = model._meta.object_name.lower()
|
||||
model_dict = self.app_models.setdefault(app_label, {})
|
||||
if model_name in model_dict:
|
||||
# The same model may be imported via different paths (e.g.
|
||||
# appname.models and project.appname.models). We use the source
|
||||
# filename as a means to detect identity.
|
||||
fname1 = os.path.abspath(sys.modules[model.__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 or .pyo the second time, ignore the extension when
|
||||
# comparing.
|
||||
if os.path.splitext(fname1)[0] == os.path.splitext(fname2)[0]:
|
||||
continue
|
||||
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.fields.related import ManyToManyRel
|
||||
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.utils.translation import activate, deactivate_all, get_language, string_concat
|
||||
from django.utils.encoding import force_unicode, smart_str
|
||||
@ -62,7 +62,7 @@ class Options(object):
|
||||
del self.meta
|
||||
|
||||
def _prepare(self, model):
|
||||
from django.db import backend
|
||||
from django.db import connection
|
||||
from django.db.backends.util import truncate_name
|
||||
if 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 not self.db_table:
|
||||
self.db_table = "%s_%s" % (self.app_label, self.module_name)
|
||||
self.db_table = truncate_name(self.db_table,
|
||||
backend.get_max_name_length())
|
||||
self.db_table = truncate_name(self.db_table, connection.ops.max_name_length())
|
||||
|
||||
def add_field(self, field):
|
||||
# Insert the given field in the order in which it was created, using
|
||||
@ -178,7 +177,8 @@ class Options(object):
|
||||
for f in klass._meta.many_to_many:
|
||||
if f.rel and self == f.rel.to._meta:
|
||||
rel_objs.append(RelatedObject(f.rel.to, klass, f))
|
||||
self._all_related_many_to_many_objects = rel_objs
|
||||
if app_cache_ready():
|
||||
self._all_related_many_to_many_objects = rel_objs
|
||||
return rel_objs
|
||||
|
||||
def get_ordered_objects(self):
|
||||
|
@ -1,5 +1,5 @@
|
||||
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 import signals, loading
|
||||
from django.dispatch import dispatcher
|
||||
@ -64,23 +64,24 @@ def orderfield2column(f, opts):
|
||||
return f
|
||||
|
||||
def orderlist2sql(order_list, opts, prefix=''):
|
||||
qn = connection.ops.quote_name
|
||||
if prefix.endswith('.'):
|
||||
prefix = backend.quote_name(prefix[:-1]) + '.'
|
||||
prefix = qn(prefix[:-1]) + '.'
|
||||
output = []
|
||||
for f in handle_legacy_orderlist(order_list):
|
||||
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 == '?':
|
||||
output.append(backend.get_random_function_sql())
|
||||
output.append(connection.ops.random_function_sql())
|
||||
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)
|
||||
|
||||
def quote_only_if_word(word):
|
||||
if re.search('\W', word): # Don't quote if there are spaces or non-word chars.
|
||||
return word
|
||||
else:
|
||||
return backend.quote_name(word)
|
||||
return connection.ops.quote_name(word)
|
||||
|
||||
class _QuerySet(object):
|
||||
"Represents a lazy database lookup for a set of objects"
|
||||
@ -235,8 +236,8 @@ class _QuerySet(object):
|
||||
|
||||
cursor = connection.cursor()
|
||||
if self._distinct:
|
||||
id_col = "%s.%s" % (backend.quote_name(self.model._meta.db_table),
|
||||
backend.quote_name(self.model._meta.pk.column))
|
||||
id_col = "%s.%s" % (connection.ops.quote_name(self.model._meta.db_table),
|
||||
connection.ops.quote_name(self.model._meta.pk.column))
|
||||
cursor.execute("SELECT COUNT(DISTINCT(%s))" % id_col + sql, params)
|
||||
else:
|
||||
cursor.execute("SELECT COUNT(*)" + sql, params)
|
||||
@ -308,11 +309,12 @@ class _QuerySet(object):
|
||||
assert self._limit is None and self._offset is None, \
|
||||
"Cannot use 'limit' or 'offset' with in_bulk"
|
||||
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)
|
||||
if id_list == []:
|
||||
return {}
|
||||
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)
|
||||
return dict([(obj._get_pk_val(), obj) for obj in qs.iterator()])
|
||||
|
||||
@ -481,10 +483,11 @@ class _QuerySet(object):
|
||||
return self._result_cache
|
||||
|
||||
def _get_sql_clause(self):
|
||||
qn = connection.ops.quote_name
|
||||
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]
|
||||
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[:]
|
||||
@ -505,10 +508,10 @@ class _QuerySet(object):
|
||||
|
||||
# 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()])
|
||||
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", backend.quote_name(opts.db_table)]
|
||||
sql = [" FROM", qn(opts.db_table)]
|
||||
|
||||
# Compose the join dictionary into SQL describing the joins.
|
||||
if joins:
|
||||
@ -531,7 +534,7 @@ class _QuerySet(object):
|
||||
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())
|
||||
order_by.append(connection.ops.random_function_sql())
|
||||
else:
|
||||
if f.startswith('-'):
|
||||
col_name = f[1:]
|
||||
@ -541,29 +544,29 @@ class _QuerySet(object):
|
||||
order = "ASC"
|
||||
if "." in col_name:
|
||||
table_prefix, col_name = col_name.split('.', 1)
|
||||
table_prefix = backend.quote_name(table_prefix) + '.'
|
||||
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 = backend.quote_name(opts.db_table) + '.'
|
||||
table_prefix = qn(opts.db_table) + '.'
|
||||
else:
|
||||
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:
|
||||
sql.append("ORDER BY " + ", ".join(order_by))
|
||||
|
||||
# LIMIT and OFFSET clauses
|
||||
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:
|
||||
assert self._offset is None, "'offset' is not allowed without 'limit'"
|
||||
|
||||
return select, " ".join(sql), params
|
||||
|
||||
# Use the backend's QuerySet class if it defines one, otherwise use _QuerySet.
|
||||
if hasattr(backend, 'get_query_set_class'):
|
||||
QuerySet = backend.get_query_set_class(_QuerySet)
|
||||
# Use the backend's QuerySet class if it defines one. Otherwise, use _QuerySet.
|
||||
if connection.features.uses_custom_queryset:
|
||||
QuerySet = connection.ops.query_set_class(_QuerySet)
|
||||
else:
|
||||
QuerySet = _QuerySet
|
||||
|
||||
@ -579,6 +582,8 @@ class ValuesQuerySet(QuerySet):
|
||||
except EmptyResultSet:
|
||||
raise StopIteration
|
||||
|
||||
qn = connection.ops.quote_name
|
||||
|
||||
# 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()
|
||||
@ -605,9 +610,9 @@ class ValuesQuerySet(QuerySet):
|
||||
field_names = [f.attname 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:
|
||||
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])
|
||||
|
||||
cursor = connection.cursor()
|
||||
@ -632,32 +637,33 @@ class DateQuerySet(QuerySet):
|
||||
def iterator(self):
|
||||
from django.db.backends.util import typecast_timestamp
|
||||
from django.db.models.fields import DateTimeField
|
||||
|
||||
qn = connection.ops.quote_name
|
||||
self._order_by = () # Clear this because it'll mess things up otherwise.
|
||||
if self._field.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:
|
||||
select, sql, params = self._get_sql_clause()
|
||||
except EmptyResultSet:
|
||||
raise StopIteration
|
||||
|
||||
table_name = backend.quote_name(self.model._meta.db_table)
|
||||
field_name = backend.quote_name(self._field.column)
|
||||
table_name = qn(self.model._meta.db_table)
|
||||
field_name = qn(self._field.column)
|
||||
|
||||
if backend.allows_group_by_ordinal:
|
||||
if connection.features.allows_group_by_ordinal:
|
||||
group_by = '1'
|
||||
else:
|
||||
group_by = backend.get_date_trunc_sql(self._kind,
|
||||
'%s.%s' % (table_name, field_name))
|
||||
group_by = connection.ops.date_trunc_sql(self._kind, '%s.%s' % (table_name, field_name))
|
||||
|
||||
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),
|
||||
backend.quote_name(self._field.column))), sql, group_by, self._order)
|
||||
(connection.ops.date_trunc_sql(self._kind, '%s.%s' % (qn(self.model._meta.db_table),
|
||||
qn(self._field.column))), sql, group_by, self._order)
|
||||
cursor = connection.cursor()
|
||||
cursor.execute(sql, params)
|
||||
|
||||
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 = []
|
||||
# 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
|
||||
@ -777,44 +783,44 @@ class QNot(Q):
|
||||
return SortedDict(), [], []
|
||||
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('.'):
|
||||
table_prefix = backend.quote_name(table_prefix[:-1])+'.'
|
||||
field_name = backend.quote_name(field_name)
|
||||
if type(value) == datetime.datetime and backend.get_datetime_cast_sql():
|
||||
cast_sql = backend.get_datetime_cast_sql()
|
||||
table_prefix = connection.ops.quote_name(table_prefix[:-1])+'.'
|
||||
field_name = connection.ops.quote_name(field_name)
|
||||
if type(value) == datetime.datetime and connection.ops.datetime_cast_sql():
|
||||
cast_sql = connection.ops.datetime_cast_sql()
|
||||
else:
|
||||
cast_sql = '%s'
|
||||
if lookup_type in ('iexact', 'icontains', 'istartswith', 'iendswith') and backend.needs_upper_for_iops:
|
||||
format = 'UPPER(%s%s) %s'
|
||||
field_sql = connection.ops.field_cast_sql(db_type) % (table_prefix + field_name)
|
||||
if lookup_type in ('iexact', 'icontains', 'istartswith', 'iendswith') and connection.features.needs_upper_for_iops:
|
||||
format = 'UPPER(%s) %s'
|
||||
else:
|
||||
format = '%s%s %s'
|
||||
format = '%s %s'
|
||||
try:
|
||||
return format % (table_prefix, field_name,
|
||||
backend.OPERATOR_MAPPING[lookup_type] % cast_sql)
|
||||
return format % (field_sql, connection.operators[lookup_type] % cast_sql)
|
||||
except KeyError:
|
||||
pass
|
||||
if lookup_type == 'in':
|
||||
in_string = ','.join(['%s' for id in value])
|
||||
if in_string:
|
||||
return '%s%s IN (%s)' % (table_prefix, field_name, in_string)
|
||||
return '%s IN (%s)' % (field_sql, in_string)
|
||||
else:
|
||||
raise EmptyResultSet
|
||||
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'):
|
||||
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':
|
||||
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':
|
||||
return backend.get_fulltext_search_sql(table_prefix + field_name)
|
||||
return connection.ops.fulltext_search_sql(field_sql)
|
||||
elif lookup_type in ('regex', 'iregex'):
|
||||
if settings.DATABASE_ENGINE == 'oracle':
|
||||
if lookup_type == 'regex':
|
||||
match_option = 'c'
|
||||
else:
|
||||
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:
|
||||
raise NotImplementedError
|
||||
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:
|
||||
return None
|
||||
|
||||
qn = backend.quote_name
|
||||
qn = connection.ops.quote_name
|
||||
for f in opts.fields:
|
||||
if f.rel and not f.null:
|
||||
db_table = f.rel.to._meta.db_table
|
||||
@ -944,7 +950,7 @@ def field_choices(field_list, related_query):
|
||||
return choices
|
||||
|
||||
def lookup_inner(path, lookup_type, value, opts, table, column):
|
||||
qn = backend.quote_name
|
||||
qn = connection.ops.quote_name
|
||||
joins, where, params = SortedDict(), [], []
|
||||
current_opts = opts
|
||||
current_table = table
|
||||
@ -1071,6 +1077,7 @@ def lookup_inner(path, lookup_type, value, opts, table, column):
|
||||
else:
|
||||
# No elements left in path. Current element is the element on which
|
||||
# the search is being performed.
|
||||
db_type = None
|
||||
|
||||
if join_required:
|
||||
# Last query term is a RelatedObject
|
||||
@ -1100,15 +1107,16 @@ def lookup_inner(path, lookup_type, value, opts, table, column):
|
||||
else:
|
||||
# Last query term was a normal field.
|
||||
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))
|
||||
|
||||
return joins, where, params
|
||||
|
||||
def delete_objects(seen_objs):
|
||||
"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.reverse()
|
||||
|
||||
|
@ -676,16 +676,16 @@ def do_if(parser, token):
|
||||
tag, because the order of logic would be ambigous. For example,
|
||||
this is invalid::
|
||||
|
||||
{% 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:
|
||||
|
||||
{% if athlete_list %}
|
||||
{% if coach_list or cheerleader_list %}
|
||||
We have athletes, and either coaches or cheerleaders!
|
||||
{% if athlete_list %}
|
||||
{% if coach_list or cheerleader_list %}
|
||||
We have athletes, and either coaches or cheerleaders!
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
"""
|
||||
bits = token.contents.split()
|
||||
del bits[0]
|
||||
|
@ -140,7 +140,7 @@ def do_extends(parser, token):
|
||||
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,
|
||||
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).
|
||||
"""
|
||||
bits = token.contents.split()
|
||||
|
@ -1,6 +1,6 @@
|
||||
import sys, time
|
||||
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.management import call_command
|
||||
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
|
||||
creation_module = get_creation_module()
|
||||
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
|
||||
|
||||
if verbosity >= 1:
|
||||
@ -118,13 +118,15 @@ def create_test_db(verbosity=1, autoclobber=False):
|
||||
else:
|
||||
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
|
||||
# if the database supports it because PostgreSQL doesn't allow
|
||||
# CREATE/DROP DATABASE statements within transactions.
|
||||
cursor = connection.cursor()
|
||||
_set_autocommit(connection)
|
||||
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:
|
||||
sys.stderr.write("Got an error creating the test database: %s\n" % e)
|
||||
if not autoclobber:
|
||||
@ -133,10 +135,10 @@ def create_test_db(verbosity=1, autoclobber=False):
|
||||
try:
|
||||
if verbosity >= 1:
|
||||
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:
|
||||
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:
|
||||
sys.stderr.write("Got an error recreating the test database: %s\n" % e)
|
||||
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
|
||||
creation_module = get_creation_module()
|
||||
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
|
||||
|
||||
# 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()
|
||||
_set_autocommit(connection)
|
||||
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()
|
||||
|
@ -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`_
|
||||
for details.
|
||||
|
||||
* In docstrings, use "action words," like so::
|
||||
* In docstrings, use "action words" such as::
|
||||
|
||||
def foo():
|
||||
"""
|
||||
|
@ -48,8 +48,8 @@ Get your database running
|
||||
|
||||
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_,
|
||||
MySQL_, Oracle_ and SQLite_ (the latter doesn't require a separate server to
|
||||
be running).
|
||||
MySQL_, Oracle_ and SQLite_ (although SQLite doesn't require a separate server
|
||||
to be running).
|
||||
|
||||
Additionally, you'll need to make sure your Python database bindings are
|
||||
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):
|
||||
def clean(self, value):
|
||||
if not value:
|
||||
raise forms.ValidationError('Enter at least one e-mail address.')
|
||||
emails = value.split(',')
|
||||
for email in emails:
|
||||
if not is_valid_email(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
|
||||
|
||||
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/``.
|
||||
|
||||
**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
|
||||
~~~~~~~~~~
|
||||
|
||||
@ -1326,12 +1332,17 @@ urlize
|
||||
|
||||
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
|
||||
~~~~~~~~~~~
|
||||
|
||||
Converts URLs into clickable links, truncating URLs longer than the given
|
||||
character limit.
|
||||
|
||||
As with urlize_, this filter should only be applied to *plain* text.
|
||||
|
||||
**Argument:** Length to truncate URLs to
|
||||
|
||||
wordcount
|
||||
|
@ -277,7 +277,7 @@ Subclassing Context: RequestContext
|
||||
|
||||
Django comes with a special ``Context`` class,
|
||||
``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::
|
||||
|
||||
c = RequestContext(request, {
|
||||
|
@ -193,7 +193,7 @@ Change it like so::
|
||||
urlpatterns = patterns('',
|
||||
(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+)/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'),
|
||||
)
|
||||
|
||||
@ -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
|
||||
``object_id`` for the generic views.
|
||||
|
||||
* We've added a name, ``poll_results``, to the results view so that we have
|
||||
a way to refer to its URL later on (see `naming URL patterns`_ for more on
|
||||
named patterns).
|
||||
|
||||
* We've added a name, ``poll_results``, to the results view so that we
|
||||
have a way to refer to its URL later on (see the documentation about
|
||||
`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
|
||||
.. _url(): ../url_dispatch/#url
|
||||
|
||||
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
|
||||
|
@ -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.
|
||||
|
||||
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
|
||||
----------
|
||||
|
||||
@ -512,7 +520,7 @@ view::
|
||||
|
||||
This is completely valid, but it leads to problems when you try to do reverse
|
||||
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
|
||||
*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
|
||||
``myapp-comment`` instead of ``comment``.
|
||||
|
||||
.. _template tag: ../templates/#url
|
||||
|
||||
Utility methods
|
||||
===============
|
||||
|
||||
reverse()
|
||||
---------
|
||||
|
||||
If you need to use something similar to the ``{% url %}`` template tag in your
|
||||
code, Django provides the ``django.core.urlresolvers.reverse()``. The
|
||||
If you need to use something similar to the ``{% url %}`` `template tag`_ in
|
||||
your code, Django provides the ``django.core.urlresolvers.reverse()``. The
|
||||
``reverse()`` function has the following signature::
|
||||
|
||||
reverse(viewname, urlconf=None, args=None, kwargs=None)
|
||||
|
@ -247,6 +247,7 @@ class ClientTest(TestCase):
|
||||
self.failIf(login)
|
||||
|
||||
def test_logout(self):
|
||||
"Request a logout after logging in"
|
||||
# Log in
|
||||
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):
|
||||
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"""
|
||||
# Regression test for #1661 and #1662: Check that string form referencing of
|
||||
# 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.
|
||||
>>> Foo.objects.get(friend__contains='\xc3\xa7')
|
||||
<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