Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Fixed #1142 -- Added multiple database support.

This monster of a patch is the result of Alex Gaynor's 2009 Google Summer of Code project.
Congratulations to Alex for a job well done.

Big thanks also go to:
 * Justin Bronn for keeping GIS in line with the changes,
 * Karen Tracey and Jani Tiainen for their help testing Oracle support
 * Brett Hoerner, Jon Loyens, and Craig Kimmerer for their feedback.
 * Malcolm Treddinick for his guidance during the GSoC submission process.
 * Simon Willison for driving the original design process
 * Cal Henderson for complaining about ponies he wanted.

... and everyone else too numerous to mention that helped to bring this feature into fruition.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@11952 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information...
commit ff60c5f9de3e8690d1e86f3e9e3f7248a15397c8 1 parent 7ef212a
@freakboy3742 freakboy3742 authored
Showing with 5,854 additions and 5,344 deletions.
  1. +3 −0  django/conf/global_settings.py
  2. +10 −6 django/conf/project_template/settings.py
  3. +21 −10 django/contrib/admin/options.py
  4. +5 −4 django/contrib/admin/widgets.py
  5. +5 −9 django/contrib/auth/models.py
  6. +10 −8 django/contrib/comments/forms.py
  7. +4 −4 django/contrib/comments/models.py
  8. +2 −2 django/contrib/comments/views/comments.py
  9. +11 −11 django/contrib/contenttypes/generic.py
  10. +5 −4 django/contrib/contenttypes/management.py
  11. +10 −10 django/contrib/contenttypes/models.py
  12. +0 −20 django/contrib/gis/db/backend/__init__.py
  13. +0 −26 django/contrib/gis/db/backend/base.py
  14. +0 −13 django/contrib/gis/db/backend/mysql/__init__.py
  15. +0 −5 django/contrib/gis/db/backend/mysql/creation.py
  16. +0 −53 django/contrib/gis/db/backend/mysql/field.py
  17. +0 −59 django/contrib/gis/db/backend/mysql/query.py
  18. +0 −35 django/contrib/gis/db/backend/oracle/__init__.py
  19. +0 −5 django/contrib/gis/db/backend/oracle/adaptor.py
  20. +0 −5 django/contrib/gis/db/backend/oracle/creation.py
  21. +0 −102 django/contrib/gis/db/backend/oracle/field.py
  22. +0 −154 django/contrib/gis/db/backend/oracle/query.py
  23. +0 −51 django/contrib/gis/db/backend/postgis/__init__.py
  24. +0 −231 django/contrib/gis/db/backend/postgis/creation.py
  25. +0 −95 django/contrib/gis/db/backend/postgis/field.py
  26. +0 −54 django/contrib/gis/db/backend/postgis/management.py
  27. +0 −313 django/contrib/gis/db/backend/postgis/query.py
  28. +0 −60 django/contrib/gis/db/backend/spatialite/__init__.py
  29. +0 −61 django/contrib/gis/db/backend/spatialite/creation.py
  30. +0 −82 django/contrib/gis/db/backend/spatialite/field.py
  31. +0 −160 django/contrib/gis/db/backend/spatialite/query.py
  32. 0  django/contrib/gis/db/backends/__init__.py
  33. +1 −1  django/contrib/gis/db/{backend/adaptor.py → backends/adapter.py}
  34. +327 −0 django/contrib/gis/db/backends/base.py
  35. 0  django/contrib/gis/db/backends/mysql/__init__.py
  36. +11 −0 django/contrib/gis/db/backends/mysql/base.py
  37. +18 −0 django/contrib/gis/db/backends/mysql/creation.py
  38. +64 −0 django/contrib/gis/db/backends/mysql/operations.py
  39. 0  django/contrib/gis/db/backends/oracle/__init__.py
  40. +5 −0 django/contrib/gis/db/backends/oracle/adapter.py
  41. +10 −0 django/contrib/gis/db/backends/oracle/base.py
  42. +44 −0 django/contrib/gis/db/backends/oracle/compiler.py
  43. +42 −0 django/contrib/gis/db/backends/oracle/creation.py
  44. +9 −4 django/contrib/gis/db/{backend → backends}/oracle/models.py
  45. +289 −0 django/contrib/gis/db/backends/oracle/operations.py
  46. 0  django/contrib/gis/db/backends/postgis/__init__.py
  47. +3 −4 django/contrib/gis/db/{backend/postgis/adaptor.py → backends/postgis/adapter.py}
  48. +10 −0 django/contrib/gis/db/backends/postgis/base.py
  49. +60 −0 django/contrib/gis/db/backends/postgis/creation.py
  50. +3 −2 django/contrib/gis/db/{backend → backends}/postgis/models.py
  51. +570 −0 django/contrib/gis/db/backends/postgis/operations.py
  52. 0  django/contrib/gis/db/backends/spatialite/__init__.py
  53. +2 −2 django/contrib/gis/db/{backend/spatialite/adaptor.py → backends/spatialite/adapter.py}
  54. +73 −0 django/contrib/gis/db/backends/spatialite/base.py
  55. +5 −0 django/contrib/gis/db/backends/spatialite/client.py
  56. +97 −0 django/contrib/gis/db/backends/spatialite/creation.py
  57. +4 −3 django/contrib/gis/db/{backend → backends}/spatialite/models.py
  58. +329 −0 django/contrib/gis/db/backends/spatialite/operations.py
  59. +24 −23 django/contrib/gis/db/{backend → backends}/util.py
  60. +5 −22 django/contrib/gis/db/models/aggregates.py
  61. +130 −101 django/contrib/gis/db/models/{fields/__init__.py → fields.py}
  62. +1 −5 django/contrib/gis/db/models/manager.py
  63. +86 −65 django/contrib/gis/db/models/query.py
  64. +30 −95 django/contrib/gis/db/models/sql/aggregates.py
  65. +276 −0 django/contrib/gis/db/models/sql/compiler.py
  66. +1 −3 django/contrib/gis/db/models/sql/conversion.py
  67. +39 −286 django/contrib/gis/db/models/sql/query.py
  68. +0 −39 django/contrib/gis/db/models/sql/subqueries.py
  69. +37 −84 django/contrib/gis/db/models/sql/where.py
  70. 0  django/contrib/gis/geometry/__init__.py
  71. +21 −0 django/contrib/gis/geometry/backend/__init__.py
  72. +3 −0  django/contrib/gis/geometry/backend/geos.py
  73. +0 −233 django/contrib/gis/models.py
  74. +13 −11 django/contrib/gis/sitemaps/views.py
  75. +12 −74 django/contrib/gis/tests/__init__.py
  76. +7 −7 django/contrib/gis/tests/distapp/tests.py
  77. +97 −0 django/contrib/gis/tests/geoapp/fixtures/initial_data.json
  78. +0 −8 django/contrib/gis/tests/geoapp/sql/city.mysql.sql
  79. +0 −8 django/contrib/gis/tests/geoapp/sql/city.oracle.sql
  80. +0 −8 django/contrib/gis/tests/geoapp/sql/city.postgresql_psycopg2.sql
  81. +0 −8 django/contrib/gis/tests/geoapp/sql/city.sqlite3.sql
  82. +0 −1  django/contrib/gis/tests/geoapp/sql/co.wkt
  83. +0 −4 django/contrib/gis/tests/geoapp/sql/country.mysql.sql
  84. +0 −4 django/contrib/gis/tests/geoapp/sql/country.postgresql_psycopg2.sql
  85. +0 −4 django/contrib/gis/tests/geoapp/sql/country.sqlite3.sql
  86. +0 −1  django/contrib/gis/tests/geoapp/sql/ks.wkt
  87. +0 −1  django/contrib/gis/tests/geoapp/sql/nz.wkt
  88. +0 −5 django/contrib/gis/tests/geoapp/sql/state.mysql.sql
  89. +0 −5 django/contrib/gis/tests/geoapp/sql/state.postgresql_psycopg2.sql
  90. +0 −5 django/contrib/gis/tests/geoapp/sql/state.sqlite3.sql
  91. +0 −1  django/contrib/gis/tests/geoapp/sql/tx.wkt
  92. +0 −1  django/contrib/gis/tests/geoapp/test_regress.py
  93. +120 −142 django/contrib/gis/tests/geoapp/tests.py
  94. +0 −186 django/contrib/gis/tests/geoapp/tests_mysql.py
  95. +15 −11 django/contrib/gis/tests/layermap/tests.py
  96. +0 −1  django/contrib/gis/tests/layermap/tests_mysql.py
  97. +11 −11 django/contrib/gis/tests/relatedapp/tests.py
  98. +0 −1  django/contrib/gis/tests/relatedapp/tests_mysql.py
  99. +10 −8 django/contrib/gis/tests/test_spatialrefsys.py
  100. +9 −7 django/contrib/gis/tests/utils.py
  101. +72 −165 django/contrib/gis/utils/layermapping.py
  102. +2 −2 django/contrib/localflavor/us/models.py
  103. +8 −3 django/contrib/sessions/backends/db.py
  104. +2 −2 django/contrib/sites/management.py
  105. +14 −3 django/core/management/commands/createcachetable.py
  106. +15 −5 django/core/management/commands/dbshell.py
  107. +10 −4 django/core/management/commands/dumpdata.py
  108. +26 −17 django/core/management/commands/flush.py
  109. +13 −4 django/core/management/commands/inspectdb.py
  110. +88 −74 django/core/management/commands/loaddata.py
  111. +12 −7 django/core/management/commands/reset.py
  112. +11 −2 django/core/management/commands/sql.py
  113. +11 −2 django/core/management/commands/sqlall.py
  114. +11 −2 django/core/management/commands/sqlclear.py
  115. +11 −2 django/core/management/commands/sqlcustom.py
  116. +11 −2 django/core/management/commands/sqlflush.py
  117. +12 −2 django/core/management/commands/sqlindexes.py
  118. +12 −2 django/core/management/commands/sqlreset.py
  119. +12 −1 django/core/management/commands/sqlsequencereset.py
  120. +29 −21 django/core/management/commands/syncdb.py
  121. +28 −32 django/core/management/sql.py
  122. +7 −7 django/core/serializers/__init__.py
  123. +4 −3 django/core/serializers/base.py
  124. +1 −1  django/core/serializers/json.py
  125. +4 −3 django/core/serializers/python.py
  126. +2 −6 django/core/serializers/pyyaml.py
  127. +4 −3 django/core/serializers/xml_serializer.py
  128. +66 −47 django/db/__init__.py
  129. +32 −28 django/db/backends/__init__.py
  130. +15 −19 django/db/backends/creation.py
  131. +2 −2 django/db/backends/dummy/base.py
  132. +14 −14 django/db/backends/mysql/base.py
  133. +6 −6 django/db/backends/mysql/client.py
  134. +8 −10 django/db/backends/mysql/creation.py
  135. +1 −3 django/db/backends/mysql/validation.py
  136. +60 −16 django/db/backends/oracle/base.py
  137. +66 −0 django/db/backends/oracle/compiler.py
  138. +64 −65 django/db/backends/oracle/creation.py
  139. +0 −150 django/db/backends/oracle/query.py
  140. +15 −15 django/db/backends/postgresql/base.py
  141. +7 −7 django/db/backends/postgresql/client.py
  142. +3 −4 django/db/backends/postgresql/creation.py
  143. +4 −3 django/db/backends/postgresql/operations.py
  144. +15 −17 django/db/backends/postgresql_psycopg2/base.py
  145. +6 −10 django/db/backends/sqlite3/base.py
  146. +1 −1  django/db/backends/sqlite3/client.py
  147. +5 −6 django/db/backends/sqlite3/creation.py
  148. +0 −3  django/db/models/aggregates.py
  149. +49 −28 django/db/models/base.py
  150. +4 −4 django/db/models/expressions.py
  151. +97 −37 django/db/models/fields/__init__.py
  152. +3 −3 django/db/models/fields/files.py
  153. +54 −33 django/db/models/fields/related.py
  154. +69 −3 django/db/models/fields/subclassing.py
  155. +22 −4 django/db/models/manager.py
  156. +110 −49 django/db/models/query.py
  157. +2 −2 django/db/models/query_utils.py
  158. +3 −2 django/db/models/related.py
  159. +3 −6 django/db/models/sql/aggregates.py
  160. +921 −0 django/db/models/sql/compiler.py
  161. +4 −8 django/db/models/sql/datastructures.py
  162. +9 −13 django/db/models/sql/expressions.py
  163. +44 −724 django/db/models/sql/query.py
  164. +41 −228 django/db/models/sql/subqueries.py
  165. +46 −31 django/db/models/sql/where.py
  166. +159 −93 django/db/transaction.py
  167. +82 −0 django/db/utils.py
  168. +4 −1 django/forms/models.py
  169. +8 −4 django/test/simple.py
  170. +61 −19 django/test/testcases.py
  171. +0 −1  django/test/utils.py
  172. +20 −0 django/utils/itercompat.py
  173. +1 −1  django/views/debug.py
  174. +93 −36 docs/howto/custom-model-fields.txt
  175. +20 −16 docs/howto/initial-data.txt
  176. +10 −9 docs/howto/legacy-databases.txt
  177. +2 −1  docs/index.txt
  178. +36 −10 docs/internals/contributing.txt
  179. +13 −1 docs/internals/deprecation.txt
  180. +31 −25 docs/intro/tutorial01.txt
  181. +2 −2 docs/man/django-admin.1
Sorry, we could not display the entire diff because it was too big.
View
3  django/conf/global_settings.py
@@ -131,6 +131,9 @@
DATABASE_PORT = '' # Set to empty string for default. Not used with sqlite3.
DATABASE_OPTIONS = {} # Set to empty dictionary for default.
+DATABASES = {
+}
+
# The email backend to use. For possible shortcuts see django.core.mail.
# The default is to use the SMTP backend.
# Third-party backends can be specified by providing a Python path
View
16 django/conf/project_template/settings.py
@@ -9,12 +9,16 @@
MANAGERS = ADMINS
-DATABASE_ENGINE = '' # 'postgresql_psycopg2', 'postgresql', 'mysql', 'sqlite3' or 'oracle'.
-DATABASE_NAME = '' # Or path to database file if using sqlite3.
-DATABASE_USER = '' # Not used with sqlite3.
-DATABASE_PASSWORD = '' # Not used with sqlite3.
-DATABASE_HOST = '' # Set to empty string for localhost. Not used with sqlite3.
-DATABASE_PORT = '' # Set to empty string for default. Not used with sqlite3.
+DATABASES = {
+ 'default': {
+ 'ENGINE': 'django.db.backends.', # Add 'postgresql_psycopg2', 'postgresql', 'mysql', 'sqlite3' or 'oracle'.
+ 'NAME': '', # Or path to database file if using sqlite3.
+ 'USER': '', # Not used with sqlite3.
+ 'PASSWORD': '', # Not used with sqlite3.
+ 'HOST': '', # Set to empty string for localhost. Not used with sqlite3.
+ 'PORT': '', # Set to empty string for default. Not used with sqlite3.
+ }
+}
# Local time zone for this installation. Choices can be found here:
# http://en.wikipedia.org/wiki/List_of_tz_zones_by_name
View
31 django/contrib/admin/options.py
@@ -141,8 +141,9 @@ def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
"""
Get a form Field for a ForeignKey.
"""
+ db = kwargs.get('using')
if db_field.name in self.raw_id_fields:
- kwargs['widget'] = widgets.ForeignKeyRawIdWidget(db_field.rel)
+ kwargs['widget'] = widgets.ForeignKeyRawIdWidget(db_field.rel, using=db)
elif db_field.name in self.radio_fields:
kwargs['widget'] = widgets.AdminRadioSelect(attrs={
'class': get_ul_class(self.radio_fields[db_field.name]),
@@ -159,9 +160,10 @@ def formfield_for_manytomany(self, db_field, request=None, **kwargs):
# a field in admin.
if not db_field.rel.through._meta.auto_created:
return None
+ db = kwargs.get('using')
if db_field.name in self.raw_id_fields:
- kwargs['widget'] = widgets.ManyToManyRawIdWidget(db_field.rel)
+ kwargs['widget'] = widgets.ManyToManyRawIdWidget(db_field.rel, using=db)
kwargs['help_text'] = ''
elif db_field.name in (list(self.filter_vertical) + list(self.filter_horizontal)):
kwargs['widget'] = widgets.FilteredSelectMultiple(db_field.verbose_name, (db_field.name in self.filter_vertical))
@@ -739,7 +741,7 @@ def add_view(self, request, form_url='', extra_context=None):
form_validated = False
new_object = self.model()
prefixes = {}
- for FormSet in self.get_formsets(request):
+ for FormSet, inline in zip(self.get_formsets(request), self.inline_instances):
prefix = FormSet.get_default_prefix()
prefixes[prefix] = prefixes.get(prefix, 0) + 1
if prefixes[prefix] != 1:
@@ -747,7 +749,7 @@ def add_view(self, request, form_url='', extra_context=None):
formset = FormSet(data=request.POST, files=request.FILES,
instance=new_object,
save_as_new=request.POST.has_key("_saveasnew"),
- prefix=prefix)
+ prefix=prefix, queryset=inline.queryset(request))
formsets.append(formset)
if all_valid(formsets) and form_validated:
self.save_model(request, new_object, form, change=False)
@@ -770,12 +772,14 @@ def add_view(self, request, form_url='', extra_context=None):
initial[k] = initial[k].split(",")
form = ModelForm(initial=initial)
prefixes = {}
- for FormSet in self.get_formsets(request):
+ for FormSet, inline in zip(self.get_formsets(request),
+ self.inline_instances):
prefix = FormSet.get_default_prefix()
prefixes[prefix] = prefixes.get(prefix, 0) + 1
if prefixes[prefix] != 1:
prefix = "%s-%s" % (prefix, prefixes[prefix])
- formset = FormSet(instance=self.model(), prefix=prefix)
+ formset = FormSet(instance=self.model(), prefix=prefix,
+ queryset=inline.queryset(request))
formsets.append(formset)
adminForm = helpers.AdminForm(form, list(self.get_fieldsets(request)), self.prepopulated_fields)
@@ -837,13 +841,16 @@ def change_view(self, request, object_id, extra_context=None):
form_validated = False
new_object = obj
prefixes = {}
- for FormSet in self.get_formsets(request, new_object):
+ for FormSet, inline in zip(self.get_formsets(request, new_object),
+ self.inline_instances):
prefix = FormSet.get_default_prefix()
prefixes[prefix] = prefixes.get(prefix, 0) + 1
if prefixes[prefix] != 1:
prefix = "%s-%s" % (prefix, prefixes[prefix])
formset = FormSet(request.POST, request.FILES,
- instance=new_object, prefix=prefix)
+ instance=new_object, prefix=prefix,
+ queryset=inline.queryset(request))
+
formsets.append(formset)
if all_valid(formsets) and form_validated:
@@ -859,12 +866,13 @@ def change_view(self, request, object_id, extra_context=None):
else:
form = ModelForm(instance=obj)
prefixes = {}
- for FormSet in self.get_formsets(request, obj):
+ for FormSet, inline in zip(self.get_formsets(request, obj), self.inline_instances):
prefix = FormSet.get_default_prefix()
prefixes[prefix] = prefixes.get(prefix, 0) + 1
if prefixes[prefix] != 1:
prefix = "%s-%s" % (prefix, prefixes[prefix])
- formset = FormSet(instance=obj, prefix=prefix)
+ formset = FormSet(instance=obj, prefix=prefix,
+ queryset=inline.queryset(request))
formsets.append(formset)
adminForm = helpers.AdminForm(form, self.get_fieldsets(request, obj), self.prepopulated_fields)
@@ -1187,6 +1195,9 @@ def get_fieldsets(self, request, obj=None):
form = self.get_formset(request).form
return [(None, {'fields': form.base_fields.keys()})]
+ def queryset(self, request):
+ return self.model._default_manager.all()
+
class StackedInline(InlineModelAdmin):
template = 'admin/edit_inline/stacked.html'
View
9 django/contrib/admin/widgets.py
@@ -102,8 +102,9 @@ class ForeignKeyRawIdWidget(forms.TextInput):
A Widget for displaying ForeignKeys in the "raw_id" interface rather than
in a <select> box.
"""
- def __init__(self, rel, attrs=None):
+ def __init__(self, rel, attrs=None, using=None):
self.rel = rel
+ self.db = using
super(ForeignKeyRawIdWidget, self).__init__(attrs)
def render(self, name, value, attrs=None):
@@ -148,7 +149,7 @@ def url_parameters(self):
def label_for_value(self, value):
key = self.rel.get_related_field().name
- obj = self.rel.to._default_manager.get(**{key: value})
+ obj = self.rel.to._default_manager.using(self.db).get(**{key: value})
return '&nbsp;<strong>%s</strong>' % escape(truncate_words(obj, 14))
class ManyToManyRawIdWidget(ForeignKeyRawIdWidget):
@@ -156,8 +157,8 @@ class ManyToManyRawIdWidget(ForeignKeyRawIdWidget):
A Widget for displaying ManyToMany ids in the "raw_id" interface rather than
in a <select multiple> box.
"""
- def __init__(self, rel, attrs=None):
- super(ManyToManyRawIdWidget, self).__init__(rel, attrs)
+ def __init__(self, rel, attrs=None, using=None):
+ super(ManyToManyRawIdWidget, self).__init__(rel, attrs, using=None)
def render(self, name, value, attrs=None):
attrs['class'] = 'vManyToManyRawIdAdminField'
View
14 django/contrib/auth/models.py
@@ -3,19 +3,15 @@
from django.contrib import auth
from django.core.exceptions import ImproperlyConfigured
-from django.db import models
+from django.db import models, DEFAULT_DB_ALIAS
from django.db.models.manager import EmptyManager
from django.contrib.contenttypes.models import ContentType
from django.utils.encoding import smart_str
from django.utils.hashcompat import md5_constructor, sha_constructor
from django.utils.translation import ugettext_lazy as _
-UNUSABLE_PASSWORD = '!' # This will never be a valid hash
-try:
- set
-except NameError:
- from sets import Set as set # Python 2.3 fallback
+UNUSABLE_PASSWORD = '!' # This will never be a valid hash
def get_hexdigest(algorithm, salt, raw_password):
"""
@@ -114,7 +110,7 @@ def create_user(self, username, email, password=None):
user.set_password(password)
else:
user.set_unusable_password()
- user.save()
+ user.save(using=self.db)
return user
def create_superuser(self, username, email, password):
@@ -122,7 +118,7 @@ def create_superuser(self, username, email, password):
u.is_staff = True
u.is_active = True
u.is_superuser = True
- u.save()
+ u.save(using=self.db)
return u
def make_random_password(self, length=10, allowed_chars='abcdefghjkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789'):
@@ -319,7 +315,7 @@ def get_profile(self):
try:
app_label, model_name = settings.AUTH_PROFILE_MODULE.split('.')
model = models.get_model(app_label, model_name)
- self._profile_cache = model._default_manager.get(user__id__exact=self.id)
+ self._profile_cache = model._default_manager.using(self._state.db).get(user__id__exact=self.id)
self._profile_cache.user = self
except (ImportError, ImproperlyConfigured):
raise SiteProfileNotAvailable
View
18 django/contrib/comments/forms.py
@@ -28,7 +28,7 @@ def __init__(self, target_object, data=None, initial=None):
initial = {}
initial.update(self.generate_security_data())
super(CommentSecurityForm, self).__init__(data=data, initial=initial)
-
+
def security_errors(self):
"""Return just those errors associated with security"""
errors = ErrorDict()
@@ -107,13 +107,13 @@ def get_comment_object(self):
"""
if not self.is_valid():
raise ValueError("get_comment_object may only be called on valid forms")
-
+
CommentModel = self.get_comment_model()
new = CommentModel(**self.get_comment_create_data())
new = self.check_for_duplicate_comment(new)
-
+
return new
-
+
def get_comment_model(self):
"""
Get the comment model to create with this form. Subclasses in custom
@@ -121,7 +121,7 @@ def get_comment_model(self):
check_for_duplicate_comment to provide custom comment models.
"""
return Comment
-
+
def get_comment_create_data(self):
"""
Returns the dict of data to be used to create a comment. Subclasses in
@@ -140,13 +140,15 @@ def get_comment_create_data(self):
is_public = True,
is_removed = False,
)
-
+
def check_for_duplicate_comment(self, new):
"""
Check that a submitted comment isn't a duplicate. This might be caused
by someone posting a comment twice. If it is a dup, silently return the *previous* comment.
"""
- possible_duplicates = self.get_comment_model()._default_manager.filter(
+ possible_duplicates = self.get_comment_model()._default_manager.using(
+ self.target_object._state.db
+ ).filter(
content_type = new.content_type,
object_pk = new.object_pk,
user_name = new.user_name,
@@ -156,7 +158,7 @@ def check_for_duplicate_comment(self, new):
for old in possible_duplicates:
if old.submit_date.date() == new.submit_date.date() and old.comment == new.comment:
return old
-
+
return new
def clean_comment(self):
View
8 django/contrib/comments/models.py
@@ -79,10 +79,10 @@ class Meta:
def __unicode__(self):
return "%s: %s..." % (self.name, self.comment[:50])
- def save(self, force_insert=False, force_update=False):
+ def save(self, *args, **kwargs):
if self.submit_date is None:
self.submit_date = datetime.datetime.now()
- super(Comment, self).save(force_insert, force_update)
+ super(Comment, self).save(*args, **kwargs)
def _get_userinfo(self):
"""
@@ -185,7 +185,7 @@ def __unicode__(self):
return "%s flag of comment ID %s by %s" % \
(self.flag, self.comment_id, self.user.username)
- def save(self, force_insert=False, force_update=False):
+ def save(self, *args, **kwargs):
if self.flag_date is None:
self.flag_date = datetime.datetime.now()
- super(CommentFlag, self).save(force_insert, force_update)
+ super(CommentFlag, self).save(*args, **kwargs)
View
4 django/contrib/comments/views/comments.py
@@ -25,7 +25,7 @@ def __init__(self, why):
@csrf_protect
@require_POST
-def post_comment(request, next=None):
+def post_comment(request, next=None, using=None):
"""
Post a comment.
@@ -50,7 +50,7 @@ def post_comment(request, next=None):
return CommentPostBadRequest("Missing content_type or object_pk field.")
try:
model = models.get_model(*ctype.split(".", 1))
- target = model._default_manager.get(pk=object_pk)
+ target = model._default_manager.using(using).get(pk=object_pk)
except TypeError:
return CommentPostBadRequest(
"Invalid content_type value: %r" % escape(ctype))
View
22 django/contrib/contenttypes/generic.py
@@ -5,7 +5,7 @@
from django.core.exceptions import ObjectDoesNotExist
from django.db import connection
from django.db.models import signals
-from django.db import models
+from django.db import models, DEFAULT_DB_ALIAS
from django.db.models.fields.related import RelatedField, Field, ManyToManyRel
from django.db.models.loading import get_model
from django.forms import ModelForm
@@ -45,14 +45,14 @@ def instance_pre_init(self, signal, sender, args, kwargs, **_kwargs):
kwargs[self.ct_field] = self.get_content_type(obj=value)
kwargs[self.fk_field] = value._get_pk_val()
- def get_content_type(self, obj=None, id=None):
+ def get_content_type(self, obj=None, id=None, using=None):
# Convenience function using get_model avoids a circular import when
# using this model
ContentType = get_model("contenttypes", "contenttype")
if obj:
- return ContentType.objects.get_for_model(obj)
+ return ContentType.objects.db_manager(obj._state.db).get_for_model(obj)
elif id:
- return ContentType.objects.get_for_id(id)
+ return ContentType.objects.db_manager(using).get_for_id(id)
else:
# This should never happen. I love comments like this, don't you?
raise Exception("Impossible arguments to GFK.get_content_type!")
@@ -73,7 +73,7 @@ def __get__(self, instance, instance_type=None):
f = self.model._meta.get_field(self.ct_field)
ct_id = getattr(instance, f.get_attname(), None)
if ct_id:
- ct = self.get_content_type(id=ct_id)
+ ct = self.get_content_type(id=ct_id, using=instance._state.db)
try:
rel_obj = ct.get_object_for_this_type(pk=getattr(instance, self.fk_field))
except ObjectDoesNotExist:
@@ -149,7 +149,7 @@ def set_attributes_from_rel(self):
def get_internal_type(self):
return "ManyToManyField"
- def db_type(self):
+ def db_type(self, connection):
# Since we're simulating a ManyToManyField, in effect, best return the
# same db_type as well.
return None
@@ -201,7 +201,7 @@ def __get__(self, instance, instance_type=None):
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(instance),
+ content_type = ContentType.objects.db_manager(instance._state.db).get_for_model(instance),
content_type_field_name = self.field.content_type_field_name,
object_id_field_name = self.field.object_id_field_name
)
@@ -247,7 +247,7 @@ def get_query_set(self):
'%s__pk' % self.content_type_field_name : self.content_type.id,
'%s__exact' % self.object_id_field_name : self.pk_val,
}
- return superclass.get_query_set(self).filter(**query)
+ return superclass.get_query_set(self).using(self.instance._state.db).filter(**query)
def add(self, *objs):
for obj in objs:
@@ -255,17 +255,17 @@ def add(self, *objs):
raise TypeError, "'%s' instance expected" % self.model._meta.object_name
setattr(obj, self.content_type_field_name, self.content_type)
setattr(obj, self.object_id_field_name, self.pk_val)
- obj.save()
+ obj.save(using=self.instance._state.db)
add.alters_data = True
def remove(self, *objs):
for obj in objs:
- obj.delete()
+ obj.delete(using=self.instance._state.db)
remove.alters_data = True
def clear(self):
for obj in self.all():
- obj.delete()
+ obj.delete(using=self.instance._state.db)
clear.alters_data = True
def create(self, **kwargs):
View
9 django/contrib/contenttypes/management.py
@@ -7,21 +7,22 @@ def update_contenttypes(app, created_models, verbosity=2, **kwargs):
Creates content types for models in the given app, removing any model
entries that no longer have a matching model class.
"""
+ db = kwargs['db']
ContentType.objects.clear_cache()
- content_types = list(ContentType.objects.filter(app_label=app.__name__.split('.')[-2]))
+ content_types = list(ContentType.objects.using(db).filter(app_label=app.__name__.split('.')[-2]))
app_models = get_models(app)
if not app_models:
return
for klass in app_models:
opts = klass._meta
try:
- ct = ContentType.objects.get(app_label=opts.app_label,
- model=opts.object_name.lower())
+ ct = ContentType.objects.using(db).get(app_label=opts.app_label,
+ model=opts.object_name.lower())
content_types.remove(ct)
except ContentType.DoesNotExist:
ct = ContentType(name=smart_unicode(opts.verbose_name_raw),
app_label=opts.app_label, model=opts.object_name.lower())
- ct.save()
+ ct.save(using=db)
if verbosity >= 2:
print "Adding content type '%s | %s'" % (ct.app_label, ct.model)
# The presence of any remaining content types means the supplied app has an
View
20 django/contrib/contenttypes/models.py
@@ -1,4 +1,4 @@
-from django.db import models
+from django.db import models, DEFAULT_DB_ALIAS
from django.utils.translation import ugettext_lazy as _
from django.utils.encoding import smart_unicode
@@ -10,7 +10,7 @@ class ContentTypeManager(models.Manager):
def get_by_natural_key(self, app_label, model):
try:
- ct = self.__class__._cache[(app_label, model)]
+ ct = self.__class__._cache[self.db][(app_label, model)]
except KeyError:
ct = self.get(app_label=app_label, model=model)
return ct
@@ -27,7 +27,7 @@ def get_for_model(self, model):
opts = model._meta
key = (opts.app_label, opts.object_name.lower())
try:
- ct = self.__class__._cache[key]
+ ct = self.__class__._cache[self.db][key]
except KeyError:
# Load or create the ContentType entry. The smart_unicode() is
# needed around opts.verbose_name_raw because name_raw might be a
@@ -37,7 +37,7 @@ def get_for_model(self, model):
model = opts.object_name.lower(),
defaults = {'name': smart_unicode(opts.verbose_name_raw)},
)
- self._add_to_cache(ct)
+ self._add_to_cache(self.db, ct)
return ct
@@ -47,12 +47,12 @@ def get_for_id(self, id):
(though ContentTypes are obviously not created on-the-fly by get_by_id).
"""
try:
- ct = self.__class__._cache[id]
+ ct = self.__class__._cache[self.db][id]
except KeyError:
# This could raise a DoesNotExist; that's correct behavior and will
# make sure that only correct ctypes get stored in the cache dict.
ct = self.get(pk=id)
- self._add_to_cache(ct)
+ self._add_to_cache(self.db, ct)
return ct
def clear_cache(self):
@@ -64,12 +64,12 @@ def clear_cache(self):
"""
self.__class__._cache.clear()
- def _add_to_cache(self, ct):
+ def _add_to_cache(self, using, ct):
"""Insert a ContentType into the cache."""
model = ct.model_class()
key = (model._meta.app_label, model._meta.object_name.lower())
- self.__class__._cache[key] = ct
- self.__class__._cache[ct.id] = ct
+ self.__class__._cache.setdefault(using, {})[key] = ct
+ self.__class__._cache.setdefault(using, {})[ct.id] = ct
class ContentType(models.Model):
name = models.CharField(max_length=100)
@@ -99,7 +99,7 @@ def get_object_for_this_type(self, **kwargs):
method. The ObjectNotExist exception, if thrown, will not be caught,
so code that calls this method should catch it.
"""
- return self.model_class()._default_manager.get(**kwargs)
+ return self.model_class()._default_manager.using(self._state.db).get(**kwargs)
def natural_key(self):
return (self.app_label, self.model)
View
20 django/contrib/gis/db/backend/__init__.py
@@ -1,20 +0,0 @@
-"""
- This module provides the backend for spatial SQL construction with Django.
-
- Specifically, this module will import the correct routines and modules
- needed for GeoDjango to interface with the spatial database.
-"""
-from django.conf import settings
-from django.contrib.gis.db.backend.util import gqn
-
-# Retrieving the necessary settings from the backend.
-if settings.DATABASE_ENGINE == 'postgresql_psycopg2':
- from django.contrib.gis.db.backend.postgis import create_test_spatial_db, get_geo_where_clause, SpatialBackend
-elif settings.DATABASE_ENGINE == 'oracle':
- from django.contrib.gis.db.backend.oracle import create_test_spatial_db, get_geo_where_clause, SpatialBackend
-elif settings.DATABASE_ENGINE == 'mysql':
- from django.contrib.gis.db.backend.mysql import create_test_spatial_db, get_geo_where_clause, SpatialBackend
-elif settings.DATABASE_ENGINE == 'sqlite3':
- from django.contrib.gis.db.backend.spatialite import create_test_spatial_db, get_geo_where_clause, SpatialBackend
-else:
- raise NotImplementedError('No Geographic Backend exists for %s' % settings.DATABASE_ENGINE)
View
26 django/contrib/gis/db/backend/base.py
@@ -1,26 +0,0 @@
-"""
- This module holds the base `SpatialBackend` object, which is
- instantiated by each spatial backend with the features it has.
-"""
-# TODO: Create a `Geometry` protocol and allow user to use
-# different Geometry objects -- for now we just use GEOSGeometry.
-from django.contrib.gis.geos import GEOSGeometry, GEOSException
-
-class BaseSpatialBackend(object):
- Geometry = GEOSGeometry
- GeometryException = GEOSException
-
- def __init__(self, **kwargs):
- kwargs.setdefault('distance_functions', {})
- kwargs.setdefault('limited_where', {})
- for k, v in kwargs.iteritems(): setattr(self, k, v)
-
- def __getattr__(self, name):
- """
- All attributes of the spatial backend return False by default.
- """
- try:
- return self.__dict__[name]
- except KeyError:
- return False
-
View
13 django/contrib/gis/db/backend/mysql/__init__.py
@@ -1,13 +0,0 @@
-__all__ = ['create_test_spatial_db', 'get_geo_where_clause', 'SpatialBackend']
-
-from django.contrib.gis.db.backend.base import BaseSpatialBackend
-from django.contrib.gis.db.backend.adaptor import WKTAdaptor
-from django.contrib.gis.db.backend.mysql.creation import create_test_spatial_db
-from django.contrib.gis.db.backend.mysql.field import MySQLGeoField
-from django.contrib.gis.db.backend.mysql.query import *
-
-SpatialBackend = BaseSpatialBackend(name='mysql', mysql=True,
- gis_terms=MYSQL_GIS_TERMS,
- select=GEOM_SELECT,
- Adaptor=WKTAdaptor,
- Field=MySQLGeoField)
View
5 django/contrib/gis/db/backend/mysql/creation.py
@@ -1,5 +0,0 @@
-
-def create_test_spatial_db(verbosity=1, autoclobber=False):
- "A wrapper over the MySQL `create_test_db` method."
- from django.db import connection
- connection.creation.create_test_db(verbosity, autoclobber)
View
53 django/contrib/gis/db/backend/mysql/field.py
@@ -1,53 +0,0 @@
-from django.db import connection
-from django.db.models.fields import Field # Django base Field class
-from django.contrib.gis.db.backend.mysql.query import GEOM_FROM_TEXT
-
-# Quotename & geographic quotename, respectively.
-qn = connection.ops.quote_name
-
-class MySQLGeoField(Field):
- """
- The backend-specific geographic field for MySQL.
- """
-
- def _geom_index(self, style, db_table):
- """
- Creates a spatial index for the geometry column. If MyISAM tables are
- used an R-Tree index is created, otherwise a B-Tree index is created.
- Thus, for best spatial performance, you should use MyISAM tables
- (which do not support transactions). For more information, see Ch.
- 16.6.1 of the MySQL 5.0 documentation.
- """
-
- # Getting the index name.
- idx_name = '%s_%s_id' % (db_table, self.column)
-
- sql = (style.SQL_KEYWORD('CREATE SPATIAL INDEX ') +
- style.SQL_TABLE(qn(idx_name)) +
- style.SQL_KEYWORD(' ON ') +
- style.SQL_TABLE(qn(db_table)) + '(' +
- style.SQL_FIELD(qn(self.column)) + ');')
- return sql
-
- def post_create_sql(self, style, db_table):
- """
- Returns SQL that will be executed after the model has been
- created.
- """
- # Getting the geometric index for this Geometry column.
- if self.spatial_index:
- return (self._geom_index(style, db_table),)
- else:
- return ()
-
- def db_type(self):
- "The OpenGIS name is returned for the MySQL database column type."
- return self.geom_type
-
- def get_placeholder(self, value):
- """
- The placeholder here has to include MySQL's WKT constructor. Because
- MySQL does not support spatial transformations, there is no need to
- modify the placeholder based on the contents of the given value.
- """
- return '%s(%%s)' % GEOM_FROM_TEXT
View
59 django/contrib/gis/db/backend/mysql/query.py
@@ -1,59 +0,0 @@
-"""
- This module contains the spatial lookup types, and the `get_geo_where_clause`
- routine for MySQL.
-
- Please note that MySQL only supports bounding box queries, also
- known as MBRs (Minimum Bounding Rectangles). Moreover, spatial
- indices may only be used on MyISAM tables -- if you need
- transactions, take a look at PostGIS.
-"""
-from django.db import connection
-qn = connection.ops.quote_name
-
-# To ease implementation, WKT is passed to/from MySQL.
-GEOM_FROM_TEXT = 'GeomFromText'
-GEOM_FROM_WKB = 'GeomFromWKB'
-GEOM_SELECT = 'AsText(%s)'
-
-# WARNING: MySQL is NOT compliant w/the OpenGIS specification and
-# _every_ one of these lookup types is on the _bounding box_ only.
-MYSQL_GIS_FUNCTIONS = {
- 'bbcontains' : 'MBRContains', # For consistency w/PostGIS API
- 'bboverlaps' : 'MBROverlaps', # .. ..
- 'contained' : 'MBRWithin', # .. ..
- 'contains' : 'MBRContains',
- 'disjoint' : 'MBRDisjoint',
- 'equals' : 'MBREqual',
- 'exact' : 'MBREqual',
- 'intersects' : 'MBRIntersects',
- 'overlaps' : 'MBROverlaps',
- 'same_as' : 'MBREqual',
- 'touches' : 'MBRTouches',
- 'within' : 'MBRWithin',
- }
-
-# This lookup type does not require a mapping.
-MISC_TERMS = ['isnull']
-
-# Assacceptable lookup types for Oracle spatial.
-MYSQL_GIS_TERMS = MYSQL_GIS_FUNCTIONS.keys()
-MYSQL_GIS_TERMS += MISC_TERMS
-MYSQL_GIS_TERMS = dict((term, None) for term in MYSQL_GIS_TERMS) # Making dictionary
-
-def get_geo_where_clause(table_alias, name, lookup_type, geo_annot):
- "Returns the SQL WHERE clause for use in MySQL spatial SQL construction."
- # Getting the quoted field as `geo_col`.
- geo_col = '%s.%s' % (qn(table_alias), qn(name))
-
- # See if a MySQL Geometry function matches the lookup type next
- lookup_info = MYSQL_GIS_FUNCTIONS.get(lookup_type, False)
- if lookup_info:
- return "%s(%s, %%s)" % (lookup_info, geo_col)
-
- # Handling 'isnull' lookup type
- # TODO: Is this needed because MySQL cannot handle NULL
- # geometries in its spatial indices.
- if lookup_type == 'isnull':
- return "%s IS %sNULL" % (geo_col, (not geo_annot.value and 'NOT ' or ''))
-
- raise TypeError("Got invalid lookup_type: %s" % repr(lookup_type))
View
35 django/contrib/gis/db/backend/oracle/__init__.py
@@ -1,35 +0,0 @@
-__all__ = ['create_test_spatial_db', 'get_geo_where_clause', 'SpatialBackend']
-
-from django.contrib.gis.db.backend.base import BaseSpatialBackend
-from django.contrib.gis.db.backend.oracle.adaptor import OracleSpatialAdaptor
-from django.contrib.gis.db.backend.oracle.creation import create_test_spatial_db
-from django.contrib.gis.db.backend.oracle.field import OracleSpatialField
-from django.contrib.gis.db.backend.oracle.models import GeometryColumns, SpatialRefSys
-from django.contrib.gis.db.backend.oracle.query import *
-
-SpatialBackend = BaseSpatialBackend(name='oracle', oracle=True,
- area=AREA,
- centroid=CENTROID,
- difference=DIFFERENCE,
- distance=DISTANCE,
- distance_functions=DISTANCE_FUNCTIONS,
- extent=EXTENT,
- gis_terms=ORACLE_SPATIAL_TERMS,
- gml=ASGML,
- intersection=INTERSECTION,
- length=LENGTH,
- limited_where = {'relate' : None},
- num_geom=NUM_GEOM,
- num_points=NUM_POINTS,
- perimeter=LENGTH,
- point_on_surface=POINT_ON_SURFACE,
- select=GEOM_SELECT,
- sym_difference=SYM_DIFFERENCE,
- transform=TRANSFORM,
- unionagg=UNIONAGG,
- union=UNION,
- Adaptor=OracleSpatialAdaptor,
- Field=OracleSpatialField,
- GeometryColumns=GeometryColumns,
- SpatialRefSys=SpatialRefSys,
- )
View
5 django/contrib/gis/db/backend/oracle/adaptor.py
@@ -1,5 +0,0 @@
-from cx_Oracle import CLOB
-from django.contrib.gis.db.backend.adaptor import WKTAdaptor
-
-class OracleSpatialAdaptor(WKTAdaptor):
- input_size = CLOB
View
5 django/contrib/gis/db/backend/oracle/creation.py
@@ -1,5 +0,0 @@
-
-def create_test_spatial_db(verbosity=1, autoclobber=False):
- "A wrapper over the Oracle `create_test_db` routine."
- from django.db import connection
- connection.creation.create_test_db(verbosity, autoclobber)
View
102 django/contrib/gis/db/backend/oracle/field.py
@@ -1,102 +0,0 @@
-from django.db import connection
-from django.db.backends.util import truncate_name
-from django.db.models.fields import Field # Django base Field class
-from django.contrib.gis.db.backend.util import gqn
-from django.contrib.gis.db.backend.oracle.query import TRANSFORM
-
-# Quotename & geographic quotename, respectively.
-qn = connection.ops.quote_name
-
-class OracleSpatialField(Field):
- """
- The backend-specific geographic field for Oracle Spatial.
- """
-
- empty_strings_allowed = False
-
- def __init__(self, extent=(-180.0, -90.0, 180.0, 90.0), tolerance=0.05, **kwargs):
- """
- Oracle Spatial backend needs to have the extent -- for projected coordinate
- systems _you must define the extent manually_, since the coordinates are
- for geodetic systems. The `tolerance` keyword specifies the tolerance
- for error (in meters), and defaults to 0.05 (5 centimeters).
- """
- # Oracle Spatial specific keyword arguments.
- self._extent = extent
- self._tolerance = tolerance
- # Calling the Django field initialization.
- super(OracleSpatialField, self).__init__(**kwargs)
-
- def _add_geom(self, style, db_table):
- """
- Adds this geometry column into the Oracle USER_SDO_GEOM_METADATA
- table.
- """
- # Checking the dimensions.
- # TODO: Add support for 3D geometries.
- if self.dim != 2:
- raise Exception('3D geometries not yet supported on Oracle Spatial backend.')
-
- # Constructing the SQL that will be used to insert information about
- # the geometry column into the USER_GSDO_GEOM_METADATA table.
- meta_sql = (style.SQL_KEYWORD('INSERT INTO ') +
- style.SQL_TABLE('USER_SDO_GEOM_METADATA') +
- ' (%s, %s, %s, %s)\n ' % tuple(map(qn, ['TABLE_NAME', 'COLUMN_NAME', 'DIMINFO', 'SRID'])) +
- style.SQL_KEYWORD(' VALUES ') + '(\n ' +
- style.SQL_TABLE(gqn(db_table)) + ',\n ' +
- style.SQL_FIELD(gqn(self.column)) + ',\n ' +
- style.SQL_KEYWORD("MDSYS.SDO_DIM_ARRAY") + '(\n ' +
- style.SQL_KEYWORD("MDSYS.SDO_DIM_ELEMENT") +
- ("('LONG', %s, %s, %s),\n " % (self._extent[0], self._extent[2], self._tolerance)) +
- style.SQL_KEYWORD("MDSYS.SDO_DIM_ELEMENT") +
- ("('LAT', %s, %s, %s)\n ),\n" % (self._extent[1], self._extent[3], self._tolerance)) +
- ' %s\n );' % self.srid)
- return meta_sql
-
- def _geom_index(self, style, db_table):
- "Creates an Oracle Geometry index (R-tree) for this geometry field."
-
- # Getting the index name, Oracle doesn't allow object
- # names > 30 characters.
- idx_name = truncate_name('%s_%s_id' % (db_table, self.column), 30)
-
- sql = (style.SQL_KEYWORD('CREATE INDEX ') +
- style.SQL_TABLE(qn(idx_name)) +
- style.SQL_KEYWORD(' ON ') +
- style.SQL_TABLE(qn(db_table)) + '(' +
- style.SQL_FIELD(qn(self.column)) + ') ' +
- style.SQL_KEYWORD('INDEXTYPE IS ') +
- style.SQL_TABLE('MDSYS.SPATIAL_INDEX') + ';')
- return sql
-
- def post_create_sql(self, style, db_table):
- """
- Returns SQL that will be executed after the model has been
- created.
- """
- # Getting the meta geometry information.
- post_sql = self._add_geom(style, db_table)
-
- # Getting the geometric index for this Geometry column.
- if self.spatial_index:
- return (post_sql, self._geom_index(style, db_table))
- else:
- return (post_sql,)
-
- def db_type(self):
- "The Oracle geometric data type is MDSYS.SDO_GEOMETRY."
- return 'MDSYS.SDO_GEOMETRY'
-
- def get_placeholder(self, value):
- """
- Provides a proper substitution value for Geometries that are not in the
- SRID of the field. Specifically, this routine will substitute in the
- SDO_CS.TRANSFORM() function call.
- """
- if value is None:
- return 'NULL'
- elif value.srid != self.srid:
- # Adding Transform() to the SQL placeholder.
- return '%s(SDO_GEOMETRY(%%s, %s), %s)' % (TRANSFORM, value.srid, self.srid)
- else:
- return 'SDO_GEOMETRY(%%s, %s)' % self.srid
View
154 django/contrib/gis/db/backend/oracle/query.py
@@ -1,154 +0,0 @@
-"""
- This module contains the spatial lookup types, and the `get_geo_where_clause`
- routine for Oracle Spatial.
-
- Please note that WKT support is broken on the XE version, and thus
- this backend will not work on such platforms. Specifically, XE lacks
- support for an internal JVM, and Java libraries are required to use
- the WKT constructors.
-"""
-import re
-from decimal import Decimal
-from django.db import connection
-from django.contrib.gis.db.backend.util import SpatialFunction
-from django.contrib.gis.measure import Distance
-qn = connection.ops.quote_name
-
-# The GML, distance, transform, and union procedures.
-AREA = 'SDO_GEOM.SDO_AREA'
-ASGML = 'SDO_UTIL.TO_GMLGEOMETRY'
-CENTROID = 'SDO_GEOM.SDO_CENTROID'
-DIFFERENCE = 'SDO_GEOM.SDO_DIFFERENCE'
-DISTANCE = 'SDO_GEOM.SDO_DISTANCE'
-EXTENT = 'SDO_AGGR_MBR'
-INTERSECTION = 'SDO_GEOM.SDO_INTERSECTION'
-LENGTH = 'SDO_GEOM.SDO_LENGTH'
-NUM_GEOM = 'SDO_UTIL.GETNUMELEM'
-NUM_POINTS = 'SDO_UTIL.GETNUMVERTICES'
-POINT_ON_SURFACE = 'SDO_GEOM.SDO_POINTONSURFACE'
-SYM_DIFFERENCE = 'SDO_GEOM.SDO_XOR'
-TRANSFORM = 'SDO_CS.TRANSFORM'
-UNION = 'SDO_GEOM.SDO_UNION'
-UNIONAGG = 'SDO_AGGR_UNION'
-
-# We want to get SDO Geometries as WKT because it is much easier to
-# instantiate GEOS proxies from WKT than SDO_GEOMETRY(...) strings.
-# However, this adversely affects performance (i.e., Java is called
-# to convert to WKT on every query). If someone wishes to write a
-# SDO_GEOMETRY(...) parser in Python, let me know =)
-GEOM_SELECT = 'SDO_UTIL.TO_WKTGEOMETRY(%s)'
-
-#### Classes used in constructing Oracle spatial SQL ####
-class SDOOperation(SpatialFunction):
- "Base class for SDO* Oracle operations."
- def __init__(self, func, **kwargs):
- kwargs.setdefault('operator', '=')
- kwargs.setdefault('result', 'TRUE')
- kwargs.setdefault('end_subst', ") %s '%s'")
- super(SDOOperation, self).__init__(func, **kwargs)
-
-class SDODistance(SpatialFunction):
- "Class for Distance queries."
- def __init__(self, op, tolerance=0.05):
- super(SDODistance, self).__init__(DISTANCE, end_subst=', %s) %%s %%s' % tolerance,
- operator=op, result='%%s')
-
-class SDOGeomRelate(SpatialFunction):
- "Class for using SDO_GEOM.RELATE."
- def __init__(self, mask, tolerance=0.05):
- # SDO_GEOM.RELATE(...) has a peculiar argument order: column, mask, geom, tolerance.
- # Moreover, the runction result is the mask (e.g., 'DISJOINT' instead of 'TRUE').
- end_subst = "%s%s) %s '%s'" % (', %%s, ', tolerance, '=', mask)
- beg_subst = "%%s(%%s, '%s'" % mask
- super(SDOGeomRelate, self).__init__('SDO_GEOM.RELATE', beg_subst=beg_subst, end_subst=end_subst)
-
-class SDORelate(SpatialFunction):
- "Class for using SDO_RELATE."
- masks = 'TOUCH|OVERLAPBDYDISJOINT|OVERLAPBDYINTERSECT|EQUAL|INSIDE|COVEREDBY|CONTAINS|COVERS|ANYINTERACT|ON'
- mask_regex = re.compile(r'^(%s)(\+(%s))*$' % (masks, masks), re.I)
- def __init__(self, mask):
- func = 'SDO_RELATE'
- if not self.mask_regex.match(mask):
- raise ValueError('Invalid %s mask: "%s"' % (func, mask))
- super(SDORelate, self).__init__(func, end_subst=", 'mask=%s') = 'TRUE'" % mask)
-
-#### Lookup type mapping dictionaries of Oracle spatial operations ####
-
-# Valid distance types and substitutions
-dtypes = (Decimal, Distance, float, int, long)
-DISTANCE_FUNCTIONS = {
- 'distance_gt' : (SDODistance('>'), dtypes),
- 'distance_gte' : (SDODistance('>='), dtypes),
- 'distance_lt' : (SDODistance('<'), dtypes),
- 'distance_lte' : (SDODistance('<='), dtypes),
- 'dwithin' : (SDOOperation('SDO_WITHIN_DISTANCE',
- beg_subst="%s(%s, %%s, 'distance=%%s'"), dtypes),
- }
-
-ORACLE_GEOMETRY_FUNCTIONS = {
- 'contains' : SDOOperation('SDO_CONTAINS'),
- 'coveredby' : SDOOperation('SDO_COVEREDBY'),
- 'covers' : SDOOperation('SDO_COVERS'),
- 'disjoint' : SDOGeomRelate('DISJOINT'),
- 'intersects' : SDOOperation('SDO_OVERLAPBDYINTERSECT'), # TODO: Is this really the same as ST_Intersects()?
- 'equals' : SDOOperation('SDO_EQUAL'),
- 'exact' : SDOOperation('SDO_EQUAL'),
- 'overlaps' : SDOOperation('SDO_OVERLAPS'),
- 'same_as' : SDOOperation('SDO_EQUAL'),
- 'relate' : (SDORelate, basestring), # Oracle uses a different syntax, e.g., 'mask=inside+touch'
- 'touches' : SDOOperation('SDO_TOUCH'),
- 'within' : SDOOperation('SDO_INSIDE'),
- }
-ORACLE_GEOMETRY_FUNCTIONS.update(DISTANCE_FUNCTIONS)
-
-# This lookup type does not require a mapping.
-MISC_TERMS = ['isnull']
-
-# Acceptable lookup types for Oracle spatial.
-ORACLE_SPATIAL_TERMS = ORACLE_GEOMETRY_FUNCTIONS.keys()
-ORACLE_SPATIAL_TERMS += MISC_TERMS
-ORACLE_SPATIAL_TERMS = dict((term, None) for term in ORACLE_SPATIAL_TERMS) # Making dictionary for fast lookups
-
-#### The `get_geo_where_clause` function for Oracle ####
-def get_geo_where_clause(table_alias, name, lookup_type, geo_annot):
- "Returns the SQL WHERE clause for use in Oracle spatial SQL construction."
- # Getting the quoted table name as `geo_col`.
- geo_col = '%s.%s' % (qn(table_alias), qn(name))
-
- # See if a Oracle Geometry function matches the lookup type next
- lookup_info = ORACLE_GEOMETRY_FUNCTIONS.get(lookup_type, False)
- if lookup_info:
- # Lookup types that are tuples take tuple arguments, e.g., 'relate' and
- # 'dwithin' lookup types.
- if isinstance(lookup_info, tuple):
- # First element of tuple is lookup type, second element is the type
- # of the expected argument (e.g., str, float)
- sdo_op, arg_type = lookup_info
-
- # Ensuring that a tuple _value_ was passed in from the user
- if not isinstance(geo_annot.value, tuple):
- raise TypeError('Tuple required for `%s` lookup type.' % lookup_type)
- if len(geo_annot.value) != 2:
- raise ValueError('2-element tuple required for %s lookup type.' % lookup_type)
-
- # Ensuring the argument type matches what we expect.
- if not isinstance(geo_annot.value[1], arg_type):
- raise TypeError('Argument type should be %s, got %s instead.' % (arg_type, type(geo_annot.value[1])))
-
- if lookup_type == 'relate':
- # The SDORelate class handles construction for these queries,
- # and verifies the mask argument.
- return sdo_op(geo_annot.value[1]).as_sql(geo_col)
- else:
- # Otherwise, just call the `as_sql` method on the SDOOperation instance.
- return sdo_op.as_sql(geo_col)
- else:
- # Lookup info is a SDOOperation instance, whose `as_sql` method returns
- # the SQL necessary for the geometry function call. For example:
- # SDO_CONTAINS("geoapp_country"."poly", SDO_GEOMTRY('POINT(5 23)', 4326)) = 'TRUE'
- return lookup_info.as_sql(geo_col)
- elif lookup_type == 'isnull':
- # Handling 'isnull' lookup type
- return "%s IS %sNULL" % (geo_col, (not geo_annot.value and 'NOT ' or ''))
-
- raise TypeError("Got invalid lookup_type: %s" % repr(lookup_type))
View
51 django/contrib/gis/db/backend/postgis/__init__.py
@@ -1,51 +0,0 @@
-__all__ = ['create_test_spatial_db', 'get_geo_where_clause', 'SpatialBackend']
-
-from django.contrib.gis.db.backend.base import BaseSpatialBackend
-from django.contrib.gis.db.backend.postgis.adaptor import PostGISAdaptor
-from django.contrib.gis.db.backend.postgis.creation import create_test_spatial_db
-from django.contrib.gis.db.backend.postgis.field import PostGISField
-from django.contrib.gis.db.backend.postgis.models import GeometryColumns, SpatialRefSys
-from django.contrib.gis.db.backend.postgis.query import *
-
-SpatialBackend = BaseSpatialBackend(name='postgis', postgis=True,
- area=AREA,
- centroid=CENTROID,
- collect=COLLECT,
- difference=DIFFERENCE,
- distance=DISTANCE,
- distance_functions=DISTANCE_FUNCTIONS,
- distance_sphere=DISTANCE_SPHERE,
- distance_spheroid=DISTANCE_SPHEROID,
- envelope=ENVELOPE,
- extent=EXTENT,
- extent3d=EXTENT3D,
- gis_terms=POSTGIS_TERMS,
- geojson=ASGEOJSON,
- gml=ASGML,
- intersection=INTERSECTION,
- kml=ASKML,
- length=LENGTH,
- length3d=LENGTH3D,
- length_spheroid=LENGTH_SPHEROID,
- make_line=MAKE_LINE,
- mem_size=MEM_SIZE,
- num_geom=NUM_GEOM,
- num_points=NUM_POINTS,
- perimeter=PERIMETER,
- perimeter3d=PERIMETER3D,
- point_on_surface=POINT_ON_SURFACE,
- scale=SCALE,
- select=GEOM_SELECT,
- snap_to_grid=SNAP_TO_GRID,
- svg=ASSVG,
- sym_difference=SYM_DIFFERENCE,
- transform=TRANSFORM,
- translate=TRANSLATE,
- union=UNION,
- unionagg=UNIONAGG,
- version=(MAJOR_VERSION, MINOR_VERSION1, MINOR_VERSION2),
- Adaptor=PostGISAdaptor,
- Field=PostGISField,
- GeometryColumns=GeometryColumns,
- SpatialRefSys=SpatialRefSys,
- )
View
231 django/contrib/gis/db/backend/postgis/creation.py
@@ -1,231 +0,0 @@
-import os, re, sys
-
-from django.conf import settings
-from django.core.management import call_command
-from django.db import connection
-from django.db.backends.creation import TEST_DATABASE_PREFIX
-from django.contrib.gis.db.backend.util import getstatusoutput
-
-def create_lang(db_name, verbosity=1):
- "Sets up the pl/pgsql language on the given database."
-
- # Getting the command-line options for the shell command
- options = get_cmd_options(db_name)
-
- # Constructing the 'createlang' command.
- createlang_cmd = 'createlang %splpgsql' % options
- if verbosity >= 1: print createlang_cmd
-
- # Must have database super-user privileges to execute createlang -- it must
- # also be in your path.
- status, output = getstatusoutput(createlang_cmd)
-
- # Checking the status of the command, 0 => execution successful
- if status:
- raise Exception("Error executing 'plpgsql' command: %s\n" % output)
-
-def _create_with_cursor(db_name, verbosity=1, autoclobber=False):
- "Creates database with psycopg2 cursor."
- qn = connection.ops.quote_name
-
- # Constructing the necessary SQL to create the database.
- create_sql = 'CREATE DATABASE %s' % qn(db_name)
-
- # If there's a template database for PostGIS set, then use it.
- if hasattr(settings, 'POSTGIS_TEMPLATE'):
- create_sql += ' TEMPLATE %s' % qn(settings.POSTGIS_TEMPLATE)
-
- # The DATABASE_USER must possess the privileges to create a spatial database.
- if settings.DATABASE_USER:
- create_sql += ' OWNER %s' % qn(settings.DATABASE_USER)
-
- cursor = connection.cursor()
- connection.creation.set_autocommit()
-
- try:
- # Trying to create the database first.
- cursor.execute(create_sql)
- except Exception, e:
- if 'already exists' in e.pgerror.lower():
- # Database already exists, drop and recreate if user agrees.
- if not autoclobber:
- confirm = raw_input("\nIt appears the database, %s, already exists. Type 'yes' to delete it, or 'no' to cancel: " % db_name)
- if autoclobber or confirm == 'yes':
- if verbosity >= 1: print 'Destroying old spatial database...'
- drop_db(db_name)
- if verbosity >= 1: print 'Creating new spatial database...'
- cursor.execute(create_sql)
- else:
- raise Exception('Spatial database creation canceled.')
- else:
- raise Exception('Spatial database creation failed: "%s"' % e.pgerror.strip())
-
-created_regex = re.compile(r'^createdb: database creation failed: ERROR: database ".+" already exists')
-def _create_with_shell(db_name, verbosity=1, autoclobber=False):
- """
- If no spatial database already exists, then using a cursor will not work.
- Thus, a `createdb` command will be issued through the shell to bootstrap
- creation of the spatial database.
-
- TODO: Actually allow this method to be used without a spatial database
- in place first.
- """
- # Getting the command-line options for the shell command
- options = get_cmd_options(False)
- if hasattr(settings, 'POSTGIS_TEMPLATE'):
- options += '-T %s ' % settings.POSTGIS_TEMPlATE
-
- create_cmd = 'createdb -O %s %s%s' % (settings.DATABASE_USER, options, db_name)
- if verbosity >= 1: print create_cmd
-
- # Attempting to create the database.
- status, output = getstatusoutput(create_cmd)
-
- if status:
- if created_regex.match(output):
- if not autoclobber:
- confirm = raw_input("\nIt appears the database, %s, already exists. Type 'yes' to delete it, or 'no' to cancel: " % db_name)
- if autoclobber or confirm == 'yes':
- if verbosity >= 1: print 'Destroying old spatial database...'
- drop_cmd = 'dropdb %s%s' % (options, db_name)
- status, output = getstatusoutput(drop_cmd)
- if status != 0:
- raise Exception('Could not drop database %s: %s' % (db_name, output))
- if verbosity >= 1: print 'Creating new spatial database...'
- status, output = getstatusoutput(create_cmd)
- if status != 0:
- raise Exception('Could not create database after dropping: %s' % output)
- else:
- raise Exception('Spatial Database Creation canceled.')
- else:
- raise Exception('Unknown error occurred in creating database: %s' % output)
-
-def create_test_spatial_db(verbosity=1, autoclobber=False, interactive=False):
- "Creates a test spatial database based on the settings."
-
- # Making sure we're using PostgreSQL and psycopg2
- if settings.DATABASE_ENGINE != 'postgresql_psycopg2':
- raise Exception('Spatial database creation only supported postgresql_psycopg2 platform.')
-
- # Getting the spatial database name
- db_name = get_spatial_db(test=True)
- _create_with_cursor(db_name, verbosity=verbosity, autoclobber=autoclobber)
-
- # If a template database is used, then don't need to do any of the following.
- if not hasattr(settings, 'POSTGIS_TEMPLATE'):
- # Creating the db language, does not need to be done on NT platforms
- # since the PostGIS installer enables this capability.
- if os.name != 'nt':
- create_lang(db_name, verbosity=verbosity)
-
- # Now adding in the PostGIS routines.
- load_postgis_sql(db_name, verbosity=verbosity)
-
- if verbosity >= 1: print 'Creation of spatial database %s successful.' % db_name
-
- # Closing the connection
- connection.close()
- settings.DATABASE_NAME = db_name
- connection.settings_dict["DATABASE_NAME"] = db_name
- can_rollback = connection.creation._rollback_works()
- settings.DATABASE_SUPPORTS_TRANSACTIONS = can_rollback
- connection.settings_dict["DATABASE_SUPPORTS_TRANSACTIONS"] = can_rollback
-
- # Syncing the database
- call_command('syncdb', verbosity=verbosity, interactive=interactive)
-
-def drop_db(db_name=False, test=False):
- """
- Drops the given database (defaults to what is returned from
- get_spatial_db()). All exceptions are propagated up to the caller.
- """
- if not db_name: db_name = get_spatial_db(test=test)
- cursor = connection.cursor()
- cursor.execute('DROP DATABASE %s' % connection.ops.quote_name(db_name))
-
-def get_cmd_options(db_name):
- "Obtains the command-line PostgreSQL connection options for shell commands."
- # The db_name parameter is optional
- options = ''
- if db_name:
- options += '-d %s ' % db_name
- if settings.DATABASE_USER:
- options += '-U %s ' % settings.DATABASE_USER
- if settings.DATABASE_HOST:
- options += '-h %s ' % settings.DATABASE_HOST
- if settings.DATABASE_PORT:
- options += '-p %s ' % settings.DATABASE_PORT
- return options
-
-def get_spatial_db(test=False):
- """
- Returns the name of the spatial database. The 'test' keyword may be set
- to return the test spatial database name.
- """
- if test:
- if settings.TEST_DATABASE_NAME:
- test_db_name = settings.TEST_DATABASE_NAME
- else:
- test_db_name = TEST_DATABASE_PREFIX + settings.DATABASE_NAME
- return test_db_name
- else:
- if not settings.DATABASE_NAME:
- raise Exception('must configure DATABASE_NAME in settings.py')
- return settings.DATABASE_NAME
-
-def load_postgis_sql(db_name, verbosity=1):
- """
- This routine loads up the PostGIS SQL files lwpostgis.sql and
- spatial_ref_sys.sql.
- """
- # Getting the path to the PostGIS SQL
- try:
- # POSTGIS_SQL_PATH may be placed in settings to tell GeoDjango where the
- # PostGIS SQL files are located. This is especially useful on Win32
- # platforms since the output of pg_config looks like "C:/PROGRA~1/..".
- sql_path = settings.POSTGIS_SQL_PATH
- except AttributeError:
- status, sql_path = getstatusoutput('pg_config --sharedir')
- if status:
- sql_path = '/usr/local/share'
-
- # The PostGIS SQL post-creation files.
- lwpostgis_file = os.path.join(sql_path, 'lwpostgis.sql')
- srefsys_file = os.path.join(sql_path, 'spatial_ref_sys.sql')
- if not os.path.isfile(lwpostgis_file):
- raise Exception('Could not find PostGIS function definitions in %s' % lwpostgis_file)
- if not os.path.isfile(srefsys_file):
- raise Exception('Could not find PostGIS spatial reference system definitions in %s' % srefsys_file)
-
- # Getting the psql command-line options, and command format.
- options = get_cmd_options(db_name)
- cmd_fmt = 'psql %s-f "%%s"' % options
-
- # Now trying to load up the PostGIS functions
- cmd = cmd_fmt % lwpostgis_file
- if verbosity >= 1: print cmd
- status, output = getstatusoutput(cmd)
- if status:
- raise Exception('Error in loading PostGIS lwgeometry routines.')
-
- # Now trying to load up the Spatial Reference System table
- cmd = cmd_fmt % srefsys_file
- if verbosity >= 1: print cmd
- status, output = getstatusoutput(cmd)
- if status:
- raise Exception('Error in loading PostGIS spatial_ref_sys table.')
-
- # Setting the permissions because on Windows platforms the owner
- # of the spatial_ref_sys and geometry_columns tables is always
- # the postgres user, regardless of how the db is created.
- if os.name == 'nt': set_permissions(db_name)
-
-def set_permissions(db_name):
- """
- Sets the permissions on the given database to that of the user specified
- in the settings. Needed specifically for PostGIS on Win32 platforms.
- """
- cursor = connection.cursor()
- user = settings.DATABASE_USER
- cursor.execute('ALTER TABLE geometry_columns OWNER TO %s' % user)
- cursor.execute('ALTER TABLE spatial_ref_sys OWNER TO %s' % user)
View
95 django/contrib/gis/db/backend/postgis/field.py
@@ -1,95 +0,0 @@
-from django.db import connection
-from django.db.models.fields import Field # Django base Field class
-from django.contrib.gis.db.backend.util import gqn
-from django.contrib.gis.db.backend.postgis.query import TRANSFORM
-
-# Quotename & geographic quotename, respectively
-qn = connection.ops.quote_name
-
-class PostGISField(Field):
- """
- The backend-specific geographic field for PostGIS.
- """
-
- def _add_geom(self, style, db_table):
- """
- Constructs the addition of the geometry to the table using the
- AddGeometryColumn(...) PostGIS (and OGC standard) stored procedure.
-
- Takes the style object (provides syntax highlighting) and the
- database table as parameters.
- """
- sql = (style.SQL_KEYWORD('SELECT ') +
- style.SQL_TABLE('AddGeometryColumn') + '(' +
- style.SQL_TABLE(gqn(db_table)) + ', ' +
- style.SQL_FIELD(gqn(self.column)) + ', ' +
- style.SQL_FIELD(str(self.srid)) + ', ' +
- style.SQL_COLTYPE(gqn(self.geom_type)) + ', ' +
- style.SQL_KEYWORD(str(self.dim)) + ');')
-
- if not self.null:
- # Add a NOT NULL constraint to the field
- sql += ('\n' +
- style.SQL_KEYWORD('ALTER TABLE ') +
- style.SQL_TABLE(qn(db_table)) +
- style.SQL_KEYWORD(' ALTER ') +
- style.SQL_FIELD(qn(self.column)) +
- style.SQL_KEYWORD(' SET NOT NULL') + ';')
- return sql
-
- def _geom_index(self, style, db_table,
- index_type='GIST', index_opts='GIST_GEOMETRY_OPS'):
- "Creates a GiST index for this geometry field."
- sql = (style.SQL_KEYWORD('CREATE INDEX ') +
- style.SQL_TABLE(qn('%s_%s_id' % (db_table, self.column))) +
- style.SQL_KEYWORD(' ON ') +
- style.SQL_TABLE(qn(db_table)) +
- style.SQL_KEYWORD(' USING ') +
- style.SQL_COLTYPE(index_type) + ' ( ' +
- style.SQL_FIELD(qn(self.column)) + ' ' +
- style.SQL_KEYWORD(index_opts) + ' );')
- return sql
-
- def post_create_sql(self, style, db_table):
- """
- Returns SQL that will be executed after the model has been
- created. Geometry columns must be added after creation with the
- PostGIS AddGeometryColumn() function.
- """
-
- # Getting the AddGeometryColumn() SQL necessary to create a PostGIS
- # geometry field.
- post_sql = self._add_geom(style, db_table)
-
- # If the user wants to index this data, then get the indexing SQL as well.
- if self.spatial_index:
- return (post_sql, self._geom_index(style, db_table))
- else:
- return (post_sql,)
-
- def _post_delete_sql(self, style, db_table):
- "Drops the geometry column."
- sql = (style.SQL_KEYWORD('SELECT ') +
- style.SQL_KEYWORD('DropGeometryColumn') + '(' +
- style.SQL_TABLE(gqn(db_table)) + ', ' +
- style.SQL_FIELD(gqn(self.column)) + ');')
- return sql
-
- def db_type(self):
- """
- PostGIS geometry columns are added by stored procedures, should be
- None.
- """
- return None
-
- def get_placeholder(self, value):
- """
- Provides a proper substitution value for Geometries that are not in the
- SRID of the field. Specifically, this routine will substitute in the
- ST_Transform() function call.
- """
- if value is None or value.srid == self.srid:
- return '%s'
- else:
- # Adding Transform() to the SQL placeholder.
- return '%s(%%s, %s)' % (TRANSFORM, self.srid)
View
54 django/contrib/gis/db/backend/postgis/management.py
@@ -1,54 +0,0 @@
-"""
- This utility module is for obtaining information about the PostGIS
- installation.
-
- See PostGIS docs at Ch. 6.2.1 for more information on these functions.
-"""
-import re
-
-def _get_postgis_func(func):
- "Helper routine for calling PostGIS functions and returning their result."
- from django.db import connection
- cursor = connection.cursor()
- cursor.execute('SELECT %s()' % func)
- row = cursor.fetchone()
- cursor.close()
- return row[0]
-
-### PostGIS management functions ###
-def postgis_geos_version():
- "Returns the version of the GEOS library used with PostGIS."
- return _get_postgis_func('postgis_geos_version')
-
-def postgis_lib_version():
- "Returns the version number of the PostGIS library used with PostgreSQL."
- return _get_postgis_func('postgis_lib_version')
-
-def postgis_proj_version():
- "Returns the version of the PROJ.4 library used with PostGIS."
- return _get_postgis_func('postgis_proj_version')
-
-def postgis_version():
- "Returns PostGIS version number and compile-time options."
- return _get_postgis_func('postgis_version')
-
-def postgis_full_version():
- "Returns PostGIS version number and compile-time options."
- return _get_postgis_func('postgis_full_version')
-
-### Routines for parsing output of management functions. ###
-version_regex = re.compile('^(?P<major>\d)\.(?P<minor1>\d)\.(?P<minor2>\d+)')
-def postgis_version_tuple():
- "Returns the PostGIS version as a tuple."
-
- # Getting the PostGIS version
- version = postgis_lib_version()
- m = version_regex.match(version)
- if m:
- major = int(m.group('major'))
- minor1 = int(m.group('minor1'))
- minor2 = int(m.group('minor2'))
- else:
- raise Exception('Could not parse PostGIS version string: %s' % version)
-
- return (version, major, minor1, minor2)
View
313 django/contrib/gis/db/backend/postgis/query.py
@@ -1,313 +0,0 @@
-"""
- This module contains the spatial lookup types, and the get_geo_where_clause()
- routine for PostGIS.
-"""
-
-import re
-from decimal import Decimal
-from django.db import connection
-from django.conf import settings
-from django.contrib.gis.measure import Distance
-from django.contrib.gis.db.backend.util import SpatialOperation, SpatialFunction
-
-qn = connection.ops.quote_name
-
-# Get the PostGIS version information.
-# To avoid the need to do a database query to determine the PostGIS version
-# each time the server starts up, one can optionally specify a
-# POSTGIS_VERSION setting. This setting is intentionally undocumented and
-# should be considered experimental, because an upcoming GIS backend
-# refactoring might remove the need for it.
-if hasattr(settings, 'POSTGIS_VERSION') and settings.POSTGIS_VERSION is not None:
- version_tuple = settings.POSTGIS_VERSION
-else:
- # This import is intentionally within the 'else' so that it isn't executed
- # if the POSTGIS_VERSION setting is available.
- from django.contrib.gis.db.backend.postgis.management import postgis_version_tuple
- version_tuple = postgis_version_tuple()
-POSTGIS_VERSION, MAJOR_VERSION, MINOR_VERSION1, MINOR_VERSION2 = version_tuple
-
-# The supported PostGIS versions.
-# TODO: Confirm tests with PostGIS versions 1.1.x -- should work.
-# Versions <= 1.0.x do not use GEOS C API, and will not be supported.
-if MAJOR_VERSION != 1 or (MAJOR_VERSION == 1 and MINOR_VERSION1 < 1):
- raise Exception('PostGIS version %s not supported.' % POSTGIS_VERSION)
-
-# Versions of PostGIS >= 1.2.2 changed their naming convention to be
-# 'SQL-MM-centric' to conform with the ISO standard. Practically, this
-# means that 'ST_' prefixes geometry function names.
-GEOM_FUNC_PREFIX = ''
-if MAJOR_VERSION >= 1:
- if (MINOR_VERSION1 > 2 or
- (MINOR_VERSION1 == 2 and MINOR_VERSION2 >= 2)):
- GEOM_FUNC_PREFIX = 'ST_'
-
- def get_func(func): return '%s%s' % (GEOM_FUNC_PREFIX, func)
-
- # Custom selection not needed for PostGIS because GEOS geometries are
- # instantiated directly from the HEXEWKB returned by default. If
- # WKT is needed for some reason in the future, this value may be changed,
- # e.g,, 'AsText(%s)'.
- GEOM_SELECT = None
-
- # Functions used by the GeoManager & GeoQuerySet
- AREA = get_func('Area')
- ASGEOJSON = get_func('AsGeoJson')
- ASKML = get_func('AsKML')
- ASGML = get_func('AsGML')
- ASSVG = get_func('AsSVG')
- CENTROID = get_func('Centroid')
- COLLECT = get_func('Collect')
- DIFFERENCE = get_func('Difference')
- DISTANCE = get_func('Distance')
- DISTANCE_SPHERE = get_func('distance_sphere')
- DISTANCE_SPHEROID = get_func('distance_spheroid')
- ENVELOPE = get_func('Envelope')
- EXTENT = get_func('Extent')
- EXTENT3D = get_func('Extent3D')
- GEOM_FROM_TEXT = get_func('GeomFromText')
- GEOM_FROM_EWKB = get_func('GeomFromEWKB')
- GEOM_FROM_WKB = get_func('GeomFromWKB')
- INTERSECTION = get_func('Intersection')
- LENGTH = get_func('Length')
- LENGTH3D = get_func('Length3D')
- LENGTH_SPHEROID = get_func('length_spheroid')
- MAKE_LINE = get_func('MakeLine')
- MEM_SIZE = get_func('mem_size')
- NUM_GEOM = get_func('NumGeometries')
- NUM_POINTS = get_func('npoints')
- PERIMETER = get_func('Perimeter')
- PERIMETER3D = get_func('Perimeter3D')
- POINT_ON_SURFACE = get_func('PointOnSurface')
- SCALE = get_func('Scale')
- SNAP_TO_GRID = get_func('SnapToGrid')
- SYM_DIFFERENCE = get_func('SymDifference')
- TRANSFORM = get_func('Transform')
- TRANSLATE = get_func('Translate')
-
- # Special cases for union, KML, and GeoJSON methods.
- if MINOR_VERSION1 < 3:
- UNIONAGG = 'GeomUnion'
- UNION = 'Union'
- else:
- UNIONAGG = 'ST_Union'
- UNION = 'ST_Union'
-
- if MINOR_VERSION1 == 1:
- ASKML = False
-
- # Only 1.3.4+ have AsGeoJson.
- if (MINOR_VERSION1 < 3 or
- (MINOR_VERSION1 == 3 and MINOR_VERSION2 < 4)):
- ASGEOJSON = False
-else:
- raise NotImplementedError('PostGIS versions < 1.0 are not supported.')
-
-#### Classes used in constructing PostGIS spatial SQL ####
-class PostGISOperator(SpatialOperation):
- "For PostGIS operators (e.g. `&&`, `~`)."
- def __init__(self, operator):
- super(PostGISOperator, self).__init__(operator=operator, beg_subst='%s %s %%s')
-
-class PostGISFunction(SpatialFunction):
- "For PostGIS function calls (e.g., `ST_Contains(table, geom)`)."
- def __init__(self, function, **kwargs):
- super(PostGISFunction, self).__init__(get_func(function), **kwargs)
-
-class PostGISFunctionParam(PostGISFunction):
- "For PostGIS functions that take another parameter (e.g. DWithin, Relate)."
- def __init__(self, func):
- super(PostGISFunctionParam, self).__init__(func, end_subst=', %%s)')
-
-class PostGISDistance(PostGISFunction):
- "For PostGIS distance operations."
- dist_func = 'Distance'
- def __init__(self, operator):
- super(PostGISDistance, self).__init__(self.dist_func, end_subst=') %s %s',
- operator=operator, result='%%s')
-
-class PostGISSpheroidDistance(PostGISFunction):
- "For PostGIS spherical distance operations (using the spheroid)."
- dist_func = 'distance_spheroid'
- def __init__(self, operator):
- # An extra parameter in `end_subst` is needed for the spheroid string.
- super(PostGISSpheroidDistance, self).__init__(self.dist_func,
- beg_subst='%s(%s, %%s, %%s',
- end_subst=') %s %s',
- operator=operator, result='%%s')
-
-class PostGISSphereDistance(PostGISFunction):
- "For PostGIS spherical distance operations."
- dist_func = 'distance_sphere'
- def __init__(self, operator):
- super(PostGISSphereDistance, self).__init__(self.dist_func, end_subst=') %s %s',
- operator=operator, result='%%s')
-
-class PostGISRelate(PostGISFunctionParam):
- "For PostGIS Relate(<geom>, <pattern>) calls."
- pattern_regex = re.compile(r'^[012TF\*]{9}$')
- def __init__(self, pattern):
- if not self.pattern_regex.match(pattern):
- raise ValueError('Invalid intersection matrix pattern "%s".' % pattern)
- super(PostGISRelate, self).__init__('Relate')
-
-#### Lookup type mapping dictionaries of PostGIS operations. ####
-
-# PostGIS-specific operators. The commented descriptions of these
-# operators come from Section 6.2.2 of the official PostGIS documentation.
-POSTGIS_OPERATORS = {
- # The "&<" operator returns true if A's bounding box overlaps or
- # is to the left of B's bounding box.
- 'overlaps_left' : PostGISOperator('&<'),
- # The "&>" operator returns true if A's bounding box overlaps or
- # is to the right of B's bounding box.
- 'overlaps_right' : PostGISOperator('&>'),
- # The "<<" operator returns true if A's bounding box is strictly
- # to the left of B's bounding box.
- 'left' : PostGISOperator('<<'),
- # The ">>" operator returns true if A's bounding box is strictly
- # to the right of B's bounding box.
- 'right' : PostGISOperator('>>'),
- # The "&<|" operator returns true if A's bounding box overlaps or
- # is below B's bounding box.
- 'overlaps_below' : PostGISOperator('&<|'),
- # The "|&>" operator returns true if A's bounding box overlaps or
- # is above B's bounding box.
- 'overlaps_above' : PostGISOperator('|&>'),
- # The "<<|" operator returns true if A's bounding box is strictly
- # below B's bounding box.
- 'strictly_below' : PostGISOperator('<<|'),
- # The "|>>" operator returns true if A's bounding box is strictly
- # above B's bounding box.
- 'strictly_above' : PostGISOperator('|>>'),
- # The "~=" operator is the "same as" operator. It tests actual
- # geometric equality of two features. So if A and B are the same feature,
- # vertex-by-vertex, the operator returns true.
- 'same_as' : PostGISOperator('~='),
- 'exact' : PostGISOperator('~='),
- # The "@" operator returns true if A's bounding box is completely contained
- # by B's bounding box.
- 'contained' : PostGISOperator('@'),
- # The "~" operator returns true if A's bounding box completely contains
- # by B's bounding box.
- 'bbcontains' : PostGISOperator('~'),
- # The "&&" operator returns true if A's bounding box overlaps
- # B's bounding box.
- 'bboverlaps' : PostGISOperator('&&'),
- }
-
-# For PostGIS >= 1.2.2 the following lookup types will do a bounding box query
-# first before calling the more computationally expensive GEOS routines (called
-# "inline index magic"):
-# 'touches', 'crosses', 'contains', 'intersects', 'within', 'overlaps', and
-# 'covers'.
-POSTGIS_GEOMETRY_FUNCTIONS = {
- 'equals' : PostGISFunction('Equals'),
- 'disjoint' : PostGISFunction('Disjoint'),
- 'touches' : PostGISFunction('Touches'),
- 'crosses' : PostGISFunction('Crosses'),
- 'within' : PostGISFunction('Within'),
- 'overlaps' : PostGISFunction('Overlaps'),
- 'contains' : PostGISFunction('Contains'),
- 'intersects' : PostGISFunction('Intersects'),
- 'relate' : (PostGISRelate, basestring),
- }
-
-# Valid distance types and substitutions
-dtypes = (Decimal, Distance, float, int, long)
-def get_dist_ops(operator):
- "Returns operations for both regular and spherical distances."
- return (PostGISDistance(operator), PostGISSphereDistance(operator), PostGISSpheroidDistance(operator))
-DISTANCE_FUNCTIONS = {
- 'distance_gt' : (get_dist_ops('>'), dtypes),
- 'distance_gte' : (get_dist_ops('>='), dtypes),
- 'distance_lt' : (get_dist_ops('<'), dtypes),
- 'distance_lte' : (get_dist_ops('<='), dtypes),
- }
-
-if GEOM_FUNC_PREFIX == 'ST_':
- # The ST_DWithin, ST_CoveredBy, and ST_Covers routines become available in 1.2.2+
- POSTGIS_GEOMETRY_FUNCTIONS.update(
- {'coveredby' : PostGISFunction('CoveredBy'),
- 'covers' : PostGISFunction('Covers'),
- })
- DISTANCE_FUNCTIONS['dwithin'] = (PostGISFunctionParam('DWithin'), dtypes)
-
-# Distance functions are a part of PostGIS geometry functions.
-POSTGIS_GEOMETRY_FUNCTIONS.update(DISTANCE_FUNCTIONS)
-
-# Any other lookup types that do not require a mapping.
-MISC_TERMS = ['isnull']
-
-# These are the PostGIS-customized QUERY_TERMS -- a list of the lookup types
-# allowed for geographic queries.
-POSTGIS_TERMS = POSTGIS_OPERATORS.keys() # Getting the operators first
-POSTGIS_TERMS += POSTGIS_GEOMETRY_FUNCTIONS.keys() # Adding on the Geometry Functions
-POSTGIS_TERMS += MISC_TERMS # Adding any other miscellaneous terms (e.g., 'isnull')
-POSTGIS_TERMS = dict((term, None) for term in POSTGIS_TERMS) # Making a dictionary for fast lookups
-
-# For checking tuple parameters -- not very pretty but gets job done.
-def exactly_two(val): return val == 2
-def two_to_three(val): return val >= 2 and val <=3
-def num_params(lookup_type, val):
- if lookup_type in DISTANCE_FUNCTIONS and lookup_type != 'dwithin': return two_to_three(val)
- else: return exactly_two(val)
-
-#### The `get_geo_where_clause` function for PostGIS. ####
-def get_geo_where_clause(table_alias, name, lookup_type, geo_annot):
- "Returns the SQL WHERE clause for use in PostGIS SQL construction."
- # Getting the quoted field as `geo_col`.
- geo_col = '%s.%s' % (qn(table_alias), qn(name))
- if lookup_type in POSTGIS_OPERATORS:
- # See if a PostGIS operator matches the lookup type.
- return POSTGIS_OPERATORS[lookup_type].as_sql(geo_col)
- elif lookup_type in POSTGIS_GEOMETRY_FUNCTIONS:
- # See if a PostGIS geometry function matches the lookup type.
- tmp = POSTGIS_GEOMETRY_FUNCTIONS[lookup_type]
-
- # Lookup types that are tuples take tuple arguments, e.g., 'relate' and
- # distance lookups.
- if isinstance(tmp, tuple):
- # First element of tuple is the PostGISOperation instance, and the
- # second element is either the type or a tuple of acceptable types
- # that may passed in as further parameters for the lookup type.
- op, arg_type = tmp
-
- # Ensuring that a tuple _value_ was passed in from the user
- if not isinstance(geo_annot.value, (tuple, list)):
- raise TypeError('Tuple required for `%s` lookup type.' % lookup_type)
-
- # Number of valid tuple parameters depends on the lookup type.
- nparams = len(geo_annot.value)
- if not num_params(lookup_type, nparams):
- raise ValueError('Incorrect number of parameters given for `%s` lookup type.' % lookup_type)
-
- # Ensuring the argument type matches what we expect.
- if not isinstance(geo_annot.value[1], arg_type):
- raise TypeError('Argument type should be %s, got %s instead.' % (arg_type, type(geo_annot.value[1])))
-
- # For lookup type `relate`, the op instance is not yet created (has
- # to be instantiated here to check the pattern parameter).
- if lookup_type == 'relate':
- op = op(geo_annot.value[1])
- elif lookup_type in DISTANCE_FUNCTIONS and lookup_type != 'dwithin':
- if geo_annot.geodetic:
- # Geodetic distances are only availble from Points to PointFields.
- if geo_annot.geom_type != 'POINT':
- raise TypeError('PostGIS spherical operations are only valid on PointFields.')
- if geo_annot.value[0].geom_typeid != 0:
- raise TypeError('PostGIS geometry distance parameter is required to be of type Point.')
- # Setting up the geodetic operation appropriately.
- if nparams == 3 and geo_annot.value[2] == 'spheroid': op = op[2]
- else: op = op[1]
- else:
- op = op[0]
- else:
- op = tmp
- # Calling the `as_sql` function on the operation instance.
- return op.as_sql(geo_col)
- elif lookup_type == 'isnull':
- # Handling 'isnull' lookup type
- return "%s IS %sNULL" % (geo_col, (not geo_annot.value and 'NOT ' or ''))
-
- raise TypeError("Got invalid lookup_type: %s" % repr(lookup_type))
View
60 django/contrib/gis/db/backend/spatialite/__init__.py
@@ -1,60 +0,0 @@
-__all__ = ['create_test_spatial_db', 'get_geo_where_clause', 'SpatialBackend']
-
-from ctypes.util import find_library
-from django.conf import settings
-from django.db.backends.signals import connection_created
-
-from django.contrib.gis.db.backend.base import BaseSpatialBackend
-from django.contrib.gis.db.backend.spatialite.adaptor import SpatiaLiteAdaptor
-from django.contrib.gis.db.backend.spatialite.creation import create_test_spatial_db
-from django.contrib.gis.db.backend.spatialite.field import SpatiaLiteField
-from django.contrib.gis.db.backend.spatialite.models import GeometryColumns, SpatialRefSys
-from django.contrib.gis.db.backend.spatialite.query import *
-
-# Here we are figuring out the path to the SpatiLite library (`libspatialite`).
-# If it's not in the system PATH, it may be set manually in the settings via
-# the `SPATIALITE_LIBRARY_PATH` setting.
-spatialite_lib = getattr(settings, 'SPATIALITE_LIBRARY_PATH', find_library('spatialite'))
-if spatialite_lib:
- def initialize_spatialite(sender=None, **kwargs):
- """
- This function initializes the pysqlite2 connection to enable the