1 try: |
1 import decimal |
2 # Only exists in Python 2.4+ |
2 from threading import local |
3 from threading import local |
3 |
4 except ImportError: |
4 from django.db import DEFAULT_DB_ALIAS |
5 # Import copy of _thread_local.py from Python 2.4 |
|
6 from django.utils._threading_local import local |
|
7 try: |
|
8 set |
|
9 except NameError: |
|
10 # Python 2.3 compat |
|
11 from sets import Set as set |
|
12 |
|
13 try: |
|
14 import decimal |
|
15 except ImportError: |
|
16 # Python 2.3 fallback |
|
17 from django.utils import _decimal as decimal |
|
18 |
|
19 from django.db.backends import util |
5 from django.db.backends import util |
20 from django.utils import datetime_safe |
6 from django.utils import datetime_safe |
|
7 from django.utils.importlib import import_module |
21 |
8 |
22 class BaseDatabaseWrapper(local): |
9 class BaseDatabaseWrapper(local): |
23 """ |
10 """ |
24 Represents a database connection. |
11 Represents a database connection. |
25 """ |
12 """ |
26 ops = None |
13 ops = None |
27 def __init__(self, settings_dict): |
14 |
|
15 def __init__(self, settings_dict, alias=DEFAULT_DB_ALIAS): |
28 # `settings_dict` should be a dictionary containing keys such as |
16 # `settings_dict` should be a dictionary containing keys such as |
29 # DATABASE_NAME, DATABASE_USER, etc. It's called `settings_dict` |
17 # NAME, USER, etc. It's called `settings_dict` instead of `settings` |
30 # instead of `settings` to disambiguate it from Django settings |
18 # to disambiguate it from Django settings modules. |
31 # modules. |
|
32 self.connection = None |
19 self.connection = None |
33 self.queries = [] |
20 self.queries = [] |
34 self.settings_dict = settings_dict |
21 self.settings_dict = settings_dict |
|
22 self.alias = alias |
|
23 |
|
24 def __eq__(self, other): |
|
25 return self.settings_dict == other.settings_dict |
|
26 |
|
27 def __ne__(self, other): |
|
28 return not self == other |
35 |
29 |
36 def _commit(self): |
30 def _commit(self): |
37 if self.connection is not None: |
31 if self.connection is not None: |
38 return self.connection.commit() |
32 return self.connection.commit() |
39 |
33 |
89 class BaseDatabaseFeatures(object): |
83 class BaseDatabaseFeatures(object): |
90 allows_group_by_pk = False |
84 allows_group_by_pk = False |
91 # True if django.db.backend.utils.typecast_timestamp is used on values |
85 # True if django.db.backend.utils.typecast_timestamp is used on values |
92 # returned from dates() calls. |
86 # returned from dates() calls. |
93 needs_datetime_string_cast = True |
87 needs_datetime_string_cast = True |
94 uses_custom_query_class = False |
|
95 empty_fetchmany_value = [] |
88 empty_fetchmany_value = [] |
96 update_can_self_select = True |
89 update_can_self_select = True |
97 interprets_empty_strings_as_nulls = False |
90 interprets_empty_strings_as_nulls = False |
98 can_use_chunked_reads = True |
91 can_use_chunked_reads = True |
99 can_return_id_from_insert = False |
92 can_return_id_from_insert = False |
100 uses_autocommit = False |
93 uses_autocommit = False |
101 uses_savepoints = False |
94 uses_savepoints = False |
102 # If True, don't use integer foreign keys referring to, e.g., positive |
95 # If True, don't use integer foreign keys referring to, e.g., positive |
103 # integer primary keys. |
96 # integer primary keys. |
104 related_fields_match_type = False |
97 related_fields_match_type = False |
|
98 allow_sliced_subqueries = True |
105 |
99 |
106 class BaseDatabaseOperations(object): |
100 class BaseDatabaseOperations(object): |
107 """ |
101 """ |
108 This class encapsulates all backend-specific differences, such as the way |
102 This class encapsulates all backend-specific differences, such as the way |
109 a backend performs ordering or calculates the ID of a recently-inserted |
103 a backend performs ordering or calculates the ID of a recently-inserted |
110 row. |
104 row. |
111 """ |
105 """ |
|
106 compiler_module = "django.db.models.sql.compiler" |
|
107 |
|
108 def __init__(self): |
|
109 self._cache = {} |
|
110 |
112 def autoinc_sql(self, table, column): |
111 def autoinc_sql(self, table, column): |
113 """ |
112 """ |
114 Returns any SQL needed to support auto-incrementing primary keys, or |
113 Returns any SQL needed to support auto-incrementing primary keys, or |
115 None if no SQL is necessary. |
114 None if no SQL is necessary. |
116 |
115 |
206 according to their own quoting schemes. |
205 according to their own quoting schemes. |
207 """ |
206 """ |
208 from django.utils.encoding import smart_unicode, force_unicode |
207 from django.utils.encoding import smart_unicode, force_unicode |
209 |
208 |
210 # Convert params to contain Unicode values. |
209 # Convert params to contain Unicode values. |
211 to_unicode = lambda s: force_unicode(s, strings_only=True) |
210 to_unicode = lambda s: force_unicode(s, strings_only=True, errors='replace') |
212 if isinstance(params, (list, tuple)): |
211 if isinstance(params, (list, tuple)): |
213 u_params = tuple([to_unicode(val) for val in params]) |
212 u_params = tuple([to_unicode(val) for val in params]) |
214 else: |
213 else: |
215 u_params = dict([(to_unicode(k), to_unicode(v)) for k, v in params.items()]) |
214 u_params = dict([(to_unicode(k), to_unicode(v)) for k, v in params.items()]) |
216 |
215 |
269 append to the INSERT query. The returned fragment should |
268 append to the INSERT query. The returned fragment should |
270 contain a format string to hold the appropriate column. |
269 contain a format string to hold the appropriate column. |
271 """ |
270 """ |
272 pass |
271 pass |
273 |
272 |
274 def query_class(self, DefaultQueryClass): |
273 def compiler(self, compiler_name): |
275 """ |
274 """ |
276 Given the default Query class, returns a custom Query class |
275 Returns the SQLCompiler class corresponding to the given name, |
277 to use for this backend. Returns None if a custom Query isn't used. |
276 in the namespace corresponding to the `compiler_module` attribute |
278 See also BaseDatabaseFeatures.uses_custom_query_class, which regulates |
277 on this backend. |
279 whether this method is called at all. |
278 """ |
280 """ |
279 if compiler_name not in self._cache: |
281 return None |
280 self._cache[compiler_name] = getattr( |
|
281 import_module(self.compiler_module), compiler_name |
|
282 ) |
|
283 return self._cache[compiler_name] |
282 |
284 |
283 def quote_name(self, name): |
285 def quote_name(self, name): |
284 """ |
286 """ |
285 Returns a quoted version of the given table, index or column name. Does |
287 Returns a quoted version of the given table, index or column name. Does |
286 not quote the given name if it's already been quoted. |
288 not quote the given name if it's already been quoted. |
496 are in INSTALLED_APPS. |
498 are in INSTALLED_APPS. |
497 |
499 |
498 If only_existing is True, the resulting list will only include the tables |
500 If only_existing is True, the resulting list will only include the tables |
499 that actually exist in the database. |
501 that actually exist in the database. |
500 """ |
502 """ |
501 from django.db import models |
503 from django.db import models, router |
502 tables = set() |
504 tables = set() |
503 for app in models.get_apps(): |
505 for app in models.get_apps(): |
504 for model in models.get_models(app): |
506 for model in models.get_models(app): |
505 if not model._meta.managed: |
507 if not model._meta.managed: |
|
508 continue |
|
509 if not router.allow_syncdb(self.connection.alias, model): |
506 continue |
510 continue |
507 tables.add(model._meta.db_table) |
511 tables.add(model._meta.db_table) |
508 tables.update([f.m2m_db_table() for f in model._meta.local_many_to_many]) |
512 tables.update([f.m2m_db_table() for f in model._meta.local_many_to_many]) |
509 if only_existing: |
513 if only_existing: |
510 tables = [t for t in tables if self.table_name_converter(t) in self.table_names()] |
514 tables = [t for t in tables if self.table_name_converter(t) in self.table_names()] |
511 return tables |
515 return tables |
512 |
516 |
513 def installed_models(self, tables): |
517 def installed_models(self, tables): |
514 "Returns a set of all models represented by the provided list of table names." |
518 "Returns a set of all models represented by the provided list of table names." |
515 from django.db import models |
519 from django.db import models, router |
516 all_models = [] |
520 all_models = [] |
517 for app in models.get_apps(): |
521 for app in models.get_apps(): |
518 for model in models.get_models(app): |
522 for model in models.get_models(app): |
519 all_models.append(model) |
523 if router.allow_syncdb(self.connection.alias, model): |
|
524 all_models.append(model) |
520 return set([m for m in all_models |
525 return set([m for m in all_models |
521 if self.table_name_converter(m._meta.db_table) in map(self.table_name_converter, tables) |
526 if self.table_name_converter(m._meta.db_table) in map(self.table_name_converter, tables) |
522 ]) |
527 ]) |
523 |
528 |
524 def sequence_list(self): |
529 def sequence_list(self): |
525 "Returns a list of information about all DB sequences for all models in all apps." |
530 "Returns a list of information about all DB sequences for all models in all apps." |
526 from django.db import models |
531 from django.db import models, router |
527 |
532 |
528 apps = models.get_apps() |
533 apps = models.get_apps() |
529 sequence_list = [] |
534 sequence_list = [] |
530 |
535 |
531 for app in apps: |
536 for app in apps: |
532 for model in models.get_models(app): |
537 for model in models.get_models(app): |
533 if not model._meta.managed: |
538 if not model._meta.managed: |
|
539 continue |
|
540 if not router.allow_syncdb(self.connection.alias, model): |
534 continue |
541 continue |
535 for f in model._meta.local_fields: |
542 for f in model._meta.local_fields: |
536 if isinstance(f, models.AutoField): |
543 if isinstance(f, models.AutoField): |
537 sequence_list.append({'table': model._meta.db_table, 'column': f.column}) |
544 sequence_list.append({'table': model._meta.db_table, 'column': f.column}) |
538 break # Only one AutoField is allowed per model, so don't bother continuing. |
545 break # Only one AutoField is allowed per model, so don't bother continuing. |
563 |
570 |
564 class BaseDatabaseValidation(object): |
571 class BaseDatabaseValidation(object): |
565 """ |
572 """ |
566 This class encapsualtes all backend-specific model validation. |
573 This class encapsualtes all backend-specific model validation. |
567 """ |
574 """ |
|
575 def __init__(self, connection): |
|
576 self.connection = connection |
|
577 |
568 def validate_field(self, errors, opts, f): |
578 def validate_field(self, errors, opts, f): |
569 "By default, there is no backend-specific validation" |
579 "By default, there is no backend-specific validation" |
570 pass |
580 pass |