mirror of
				https://github.com/django/django.git
				synced 2025-10-30 17:16:10 +00:00 
			
		
		
		
	Moved postgresql backend DatabaseOperations class into a new module, postgresql/operations.py, so that it can be imported by both the postgresql and postgresql_psycopg2 backends. Hence the two backends no longer have a duplicated DatabaseOperations class
git-svn-id: http://code.djangoproject.com/svn/django/trunk@5972 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
		| @@ -5,7 +5,8 @@ Requires psycopg 1: http://initd.org/projects/psycopg1 | |||||||
| """ | """ | ||||||
|  |  | ||||||
| from django.utils.encoding import smart_str, smart_unicode | from django.utils.encoding import smart_str, smart_unicode | ||||||
| from django.db.backends import BaseDatabaseWrapper, BaseDatabaseOperations, util | from django.db.backends import BaseDatabaseWrapper, util | ||||||
|  | from django.db.backends.postgresql.operations import DatabaseOperations | ||||||
| try: | try: | ||||||
|     import psycopg as Database |     import psycopg as Database | ||||||
| except ImportError, e: | except ImportError, e: | ||||||
| @@ -55,110 +56,6 @@ class UnicodeCursorWrapper(object): | |||||||
|         else: |         else: | ||||||
|             return getattr(self.cursor, attr) |             return getattr(self.cursor, attr) | ||||||
|  |  | ||||||
| postgres_version = None |  | ||||||
|  |  | ||||||
| class DatabaseOperations(BaseDatabaseOperations): |  | ||||||
|     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 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([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 |  | ||||||
|  |  | ||||||
| class DatabaseWrapper(BaseDatabaseWrapper): | class DatabaseWrapper(BaseDatabaseWrapper): | ||||||
|     ops = DatabaseOperations() |     ops = DatabaseOperations() | ||||||
|  |  | ||||||
| @@ -185,10 +82,9 @@ class DatabaseWrapper(BaseDatabaseWrapper): | |||||||
|             cursor.execute("SET TIME ZONE %s", [settings.TIME_ZONE]) |             cursor.execute("SET TIME ZONE %s", [settings.TIME_ZONE]) | ||||||
|         cursor.execute("SET client_encoding to 'UNICODE'") |         cursor.execute("SET client_encoding to 'UNICODE'") | ||||||
|         cursor = UnicodeCursorWrapper(cursor, 'utf-8') |         cursor = UnicodeCursorWrapper(cursor, 'utf-8') | ||||||
|         global postgres_version |         if self.ops.postgres_version is None: | ||||||
|         if not postgres_version: |  | ||||||
|             cursor.execute("SELECT version()") |             cursor.execute("SELECT version()") | ||||||
|             postgres_version = [int(val) for val in cursor.fetchone()[0].split()[1].split('.')] |             self.ops.postgres_version = [int(val) for val in cursor.fetchone()[0].split()[1].split('.')] | ||||||
|         return cursor |         return cursor | ||||||
|  |  | ||||||
| allows_group_by_ordinal = True | allows_group_by_ordinal = True | ||||||
|   | |||||||
							
								
								
									
										109
									
								
								django/db/backends/postgresql/operations.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										109
									
								
								django/db/backends/postgresql/operations.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,109 @@ | |||||||
|  | from django.db.backends import BaseDatabaseOperations | ||||||
|  |  | ||||||
|  | # This DatabaseOperations class lives in here instead of base.py because it's | ||||||
|  | # used by both the 'postgresql' and 'postgresql_psycopg2' backends. | ||||||
|  |  | ||||||
|  | class DatabaseOperations(BaseDatabaseOperations): | ||||||
|  |     def __init__(self, postgres_version=None): | ||||||
|  |         self.postgres_version = postgres_version | ||||||
|  |  | ||||||
|  |     def date_extract_sql(self, lookup_type, field_name): | ||||||
|  |         # http://www.postgresql.org/docs/8.0/static/functions-datetime.html#FUNCTIONS-DATETIME-EXTRACT | ||||||
|  |         return "EXTRACT('%s' FROM %s)" % (lookup_type, field_name) | ||||||
|  |  | ||||||
|  |     def date_trunc_sql(self, lookup_type, field_name): | ||||||
|  |         # http://www.postgresql.org/docs/8.0/static/functions-datetime.html#FUNCTIONS-DATETIME-TRUNC | ||||||
|  |         return "DATE_TRUNC('%s', %s)" % (lookup_type, field_name) | ||||||
|  |  | ||||||
|  |     def deferrable_sql(self): | ||||||
|  |         return " DEFERRABLE INITIALLY DEFERRED" | ||||||
|  |  | ||||||
|  |     def last_insert_id(self, cursor, table_name, pk_name): | ||||||
|  |         cursor.execute("SELECT CURRVAL('\"%s_%s_seq\"')" % (table_name, pk_name)) | ||||||
|  |         return cursor.fetchone()[0] | ||||||
|  |  | ||||||
|  |     def quote_name(self, name): | ||||||
|  |         if name.startswith('"') and name.endswith('"'): | ||||||
|  |             return name # Quoting once is enough. | ||||||
|  |         return '"%s"' % name | ||||||
|  |  | ||||||
|  |     def sql_flush(self, style, tables, sequences): | ||||||
|  |         if tables: | ||||||
|  |             if self.postgres_version[0] >= 8 and self.postgres_version[1] >= 1: | ||||||
|  |                 # Postgres 8.1+ can do 'TRUNCATE x, y, z...;'. In fact, it *has to* | ||||||
|  |                 # in order to be able to truncate tables referenced by a foreign | ||||||
|  |                 # key in any other table. The result is a single SQL TRUNCATE | ||||||
|  |                 # statement. | ||||||
|  |                 sql = ['%s %s;' % \ | ||||||
|  |                     (style.SQL_KEYWORD('TRUNCATE'), | ||||||
|  |                      style.SQL_FIELD(', '.join([self.quote_name(table) for table in tables])) | ||||||
|  |                 )] | ||||||
|  |             else: | ||||||
|  |                 # Older versions of Postgres can't do TRUNCATE in a single call, so | ||||||
|  |                 # they must use a simple delete. | ||||||
|  |                 sql = ['%s %s %s;' % \ | ||||||
|  |                         (style.SQL_KEYWORD('DELETE'), | ||||||
|  |                          style.SQL_KEYWORD('FROM'), | ||||||
|  |                          style.SQL_FIELD(self.quote_name(table)) | ||||||
|  |                          ) for table in tables] | ||||||
|  |  | ||||||
|  |             # 'ALTER SEQUENCE sequence_name RESTART WITH 1;'... style SQL statements | ||||||
|  |             # to reset sequence indices | ||||||
|  |             for sequence_info in sequences: | ||||||
|  |                 table_name = sequence_info['table'] | ||||||
|  |                 column_name = sequence_info['column'] | ||||||
|  |                 if column_name and len(column_name)>0: | ||||||
|  |                     # sequence name in this case will be <table>_<column>_seq | ||||||
|  |                     sql.append("%s %s %s %s %s %s;" % \ | ||||||
|  |                         (style.SQL_KEYWORD('ALTER'), | ||||||
|  |                         style.SQL_KEYWORD('SEQUENCE'), | ||||||
|  |                         style.SQL_FIELD(self.quote_name('%s_%s_seq' % (table_name, column_name))), | ||||||
|  |                         style.SQL_KEYWORD('RESTART'), | ||||||
|  |                         style.SQL_KEYWORD('WITH'), | ||||||
|  |                         style.SQL_FIELD('1') | ||||||
|  |                         ) | ||||||
|  |                     ) | ||||||
|  |                 else: | ||||||
|  |                     # sequence name in this case will be <table>_id_seq | ||||||
|  |                     sql.append("%s %s %s %s %s %s;" % \ | ||||||
|  |                         (style.SQL_KEYWORD('ALTER'), | ||||||
|  |                          style.SQL_KEYWORD('SEQUENCE'), | ||||||
|  |                          style.SQL_FIELD(self.quote_name('%s_id_seq' % table_name)), | ||||||
|  |                          style.SQL_KEYWORD('RESTART'), | ||||||
|  |                          style.SQL_KEYWORD('WITH'), | ||||||
|  |                          style.SQL_FIELD('1') | ||||||
|  |                          ) | ||||||
|  |                     ) | ||||||
|  |             return sql | ||||||
|  |         else: | ||||||
|  |             return [] | ||||||
|  |  | ||||||
|  |     def sequence_reset_sql(self, style, model_list): | ||||||
|  |         from django.db import models | ||||||
|  |         output = [] | ||||||
|  |         qn = self.quote_name | ||||||
|  |         for model in model_list: | ||||||
|  |             # Use `coalesce` to set the sequence for each model to the max pk value if there are records, | ||||||
|  |             # or 1 if there are none. Set the `is_called` property (the third argument to `setval`) to true | ||||||
|  |             # if there are records (as the max pk value is already in use), otherwise set it to false. | ||||||
|  |             for f in model._meta.fields: | ||||||
|  |                 if isinstance(f, models.AutoField): | ||||||
|  |                     output.append("%s setval('%s', coalesce(max(%s), 1), max(%s) %s null) %s %s;" % \ | ||||||
|  |                         (style.SQL_KEYWORD('SELECT'), | ||||||
|  |                         style.SQL_FIELD(qn('%s_%s_seq' % (model._meta.db_table, f.column))), | ||||||
|  |                         style.SQL_FIELD(qn(f.column)), | ||||||
|  |                         style.SQL_FIELD(qn(f.column)), | ||||||
|  |                         style.SQL_KEYWORD('IS NOT'), | ||||||
|  |                         style.SQL_KEYWORD('FROM'), | ||||||
|  |                         style.SQL_TABLE(qn(model._meta.db_table)))) | ||||||
|  |                     break # Only one AutoField is allowed per model, so don't bother continuing. | ||||||
|  |             for f in model._meta.many_to_many: | ||||||
|  |                 output.append("%s setval('%s', coalesce(max(%s), 1), max(%s) %s null) %s %s;" % \ | ||||||
|  |                     (style.SQL_KEYWORD('SELECT'), | ||||||
|  |                     style.SQL_FIELD(qn('%s_id_seq' % f.m2m_db_table())), | ||||||
|  |                     style.SQL_FIELD(qn('id')), | ||||||
|  |                     style.SQL_FIELD(qn('id')), | ||||||
|  |                     style.SQL_KEYWORD('IS NOT'), | ||||||
|  |                     style.SQL_KEYWORD('FROM'), | ||||||
|  |                     style.SQL_TABLE(f.m2m_db_table()))) | ||||||
|  |         return output | ||||||
| @@ -4,7 +4,8 @@ PostgreSQL database backend for Django. | |||||||
| Requires psycopg 2: http://initd.org/projects/psycopg2 | Requires psycopg 2: http://initd.org/projects/psycopg2 | ||||||
| """ | """ | ||||||
|  |  | ||||||
| from django.db.backends import BaseDatabaseWrapper, BaseDatabaseOperations, util | from django.db.backends import BaseDatabaseWrapper, util | ||||||
|  | from django.db.backends.postgresql.operations import DatabaseOperations | ||||||
| try: | try: | ||||||
|     import psycopg2 as Database |     import psycopg2 as Database | ||||||
|     import psycopg2.extensions |     import psycopg2.extensions | ||||||
| @@ -17,111 +18,6 @@ IntegrityError = Database.IntegrityError | |||||||
|  |  | ||||||
| psycopg2.extensions.register_type(psycopg2.extensions.UNICODE) | psycopg2.extensions.register_type(psycopg2.extensions.UNICODE) | ||||||
|  |  | ||||||
| postgres_version = None |  | ||||||
|  |  | ||||||
| class DatabaseOperations(BaseDatabaseOperations): |  | ||||||
|     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): |  | ||||||
|         qn = self.quote_name |  | ||||||
|         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([qn(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(qn(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(qn('%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(qn('%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 |  | ||||||
|         qn = self.quote_name |  | ||||||
|         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(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 |  | ||||||
|  |  | ||||||
| class DatabaseWrapper(BaseDatabaseWrapper): | class DatabaseWrapper(BaseDatabaseWrapper): | ||||||
|     ops = DatabaseOperations() |     ops = DatabaseOperations() | ||||||
|  |  | ||||||
| @@ -148,10 +44,9 @@ class DatabaseWrapper(BaseDatabaseWrapper): | |||||||
|         cursor.tzinfo_factory = None |         cursor.tzinfo_factory = None | ||||||
|         if set_tz: |         if set_tz: | ||||||
|             cursor.execute("SET TIME ZONE %s", [settings.TIME_ZONE]) |             cursor.execute("SET TIME ZONE %s", [settings.TIME_ZONE]) | ||||||
|         global postgres_version |         if self.ops.postgres_version is None: | ||||||
|         if not postgres_version: |  | ||||||
|             cursor.execute("SELECT version()") |             cursor.execute("SELECT version()") | ||||||
|             postgres_version = [int(val) for val in cursor.fetchone()[0].split()[1].split('.')] |             self.ops.postgres_version = [int(val) for val in cursor.fetchone()[0].split()[1].split('.')] | ||||||
|         return cursor |         return cursor | ||||||
|  |  | ||||||
| allows_group_by_ordinal = True | allows_group_by_ordinal = True | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user