1
0
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:
Adrian Holovaty 2007-08-20 05:00:52 +00:00
parent 0ae4e102dc
commit c49f2d21ea
76 changed files with 2325 additions and 1849 deletions

View File

@ -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>

View File

@ -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:

View File

@ -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"

View File

@ -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'

View File

@ -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 %}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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))

View File

@ -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>

View File

@ -0,0 +1 @@
{% extends "databrowse/base.html" %}

View File

@ -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 %}

View File

@ -1,4 +1,4 @@
{% extends "databrowse/base.html" %}
{% extends "databrowse/base_site.html" %}
{% block title %}Calendars{% endblock %}

View File

@ -1,4 +1,4 @@
{% extends "databrowse/base.html" %}
{% extends "databrowse/base_site.html" %}
{% block title %}{{ field.verbose_name|capfirst }} calendar{% endblock %}

View File

@ -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 %}

View File

@ -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 %}

View File

@ -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 %}

View File

@ -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 %}

View File

@ -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 %}

View File

@ -1,4 +1,4 @@
{% extends "databrowse/base.html" %}
{% extends "databrowse/base_site.html" %}
{% block title %}Browsable fields in {{ model.verbose_name_plural|escape }}{% endblock %}

View File

@ -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 %}

View File

@ -1,4 +1,4 @@
{% extends "databrowse/base.html" %}
{% extends "databrowse/base_site.html" %}
{% block title %}Databrowse{% endblock %}

View File

@ -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 }}

View File

@ -1,4 +1,4 @@
{% extends "databrowse/base.html" %}
{% extends "databrowse/base_site.html" %}
{% block title %}{{ object.model.verbose_name|capfirst }}: {{ object }}{% endblock %}

View 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)

View 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'),
)

View 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')),
)

View File

@ -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;")

View File

@ -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(');')

View File

@ -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"

View File

@ -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)))

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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()

View File

@ -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

View File

@ -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',
}

View File

@ -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):

View File

@ -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',
}

View File

@ -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):

View File

@ -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 '\\'",
}

View File

@ -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:

View File

@ -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):

View File

@ -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',
}

View File

@ -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."

View 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

View File

@ -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',
}

View File

@ -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."

View File

@ -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 '\\'",
}

View File

@ -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."

View File

@ -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)

View File

@ -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

View File

@ -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()]

View File

@ -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()},

View File

@ -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

View File

@ -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):

View File

@ -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()

View File

@ -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]

View File

@ -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()

View File

@ -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()

View File

@ -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():
"""

View File

@ -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.

View 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
View 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.

View 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
View 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.

View File

@ -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

View File

@ -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

View File

@ -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, {

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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>
"""}