Created
April 14, 2015 19:49
-
-
Save MarkusH/9084e4279880fc686ec1 to your computer and use it in GitHub Desktop.
Postgresql function based indexes in Django (based on 825bb0ab08cec353edcd2b9aea651bfe9392ef97)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff --git a/django/db/backends/postgresql_psycopg2/schema.py b/django/db/backends/postgresql_psycopg2/schema.py | |
index 8340059..692866e 100644 | |
--- a/django/db/backends/postgresql_psycopg2/schema.py | |
+++ b/django/db/backends/postgresql_psycopg2/schema.py | |
@@ -1,6 +1,21 @@ | |
import psycopg2 | |
from django.db.backends.base.schema import BaseDatabaseSchemaEditor | |
+from django.db.models.expressions import Func | |
+from django.db.models.sql.compiler import SQLCompiler | |
+ | |
+ | |
+class SQLFuncCompiler(SQLCompiler): | |
+ | |
+ def __init__(self, connection): | |
+ super(SQLFuncCompiler, self).__init__(None, connection, None) | |
+ | |
+ def quote_name_unless_alias(self, name): | |
+ if name in self.quote_cache: | |
+ return self.quote_cache[name] | |
+ r = self.connection.ops.quote_name(name) | |
+ self.quote_cache[name] = r | |
+ return r | |
class DatabaseSchemaEditor(BaseDatabaseSchemaEditor): | |
@@ -19,6 +34,8 @@ class DatabaseSchemaEditor(BaseDatabaseSchemaEditor): | |
if not model._meta.managed or model._meta.proxy or model._meta.swapped: | |
return output | |
+ compiler = SQLFuncCompiler(self.connection) | |
+ | |
for field in model._meta.local_fields: | |
db_type = field.db_type(connection=self.connection) | |
if db_type is not None and (field.db_index or field.unique): | |
@@ -32,6 +49,16 @@ class DatabaseSchemaEditor(BaseDatabaseSchemaEditor): | |
elif db_type.startswith('text'): | |
output.append(self._create_index_sql( | |
model, [field], suffix='_like', sql=self.sql_create_text_index)) | |
+ if isinstance(field.db_index, Func): | |
+ columns, params = field.db_index.as_sql(compiler, self.connection) | |
+ suffix = "_func" | |
+ output.append(self.sql_create_index % { | |
+ "table": self.quote_name(model._meta.db_table), | |
+ "name": self.quote_name(self._create_index_name(model, columns, suffix=suffix)), | |
+ "columns": columns, | |
+ "extra": '', | |
+ }) | |
+ | |
return output | |
def _alter_column_type_sql(self, table, column, type): | |
diff --git a/django/db/models/fields/__init__.py b/django/db/models/fields/__init__.py | |
index b305330..8a75d44 100644 | |
--- a/django/db/models/fields/__init__.py | |
+++ b/django/db/models/fields/__init__.py | |
@@ -278,10 +278,11 @@ class Field(RegisterLookupMixin): | |
return [] | |
def _check_db_index(self): | |
- if self.db_index not in (None, True, False): | |
+ from django.db.models.expressions import Func # circular import | |
+ if self.db_index not in (None, True, False) and not isinstance(self.db_index, Func): | |
return [ | |
checks.Error( | |
- "'db_index' must be None, True or False.", | |
+ "'db_index' must be None, True or False or a database function instance.", | |
hint=None, | |
obj=self, | |
id='fields.E006', |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
django=# \d c_project | |
Table "public.c_project" | |
Column | Type | Modifiers | |
-------------+--------------------------+-------------------------------------------------------- | |
id | integer | not null default nextval('c_project_id_seq'::regclass) | |
name | character varying(100) | not null | |
description | character varying(500) | not null | |
createdBy | character varying(50) | not null | |
createdOn | timestamp with time zone | not null | |
modifiedBy | character varying(50) | | |
modifiedOn | timestamp with time zone | not null | |
Indexes: | |
"c_project_pkey" PRIMARY KEY, btree (id) | |
"c_project_67daf92c" btree (description) | |
"c_project_L_a3a6aa61_func" btree (lower(name::text)) | |
"c_project_U_ee9b75f9_func" btree (upper(description::text)) | |
"c_project_b068931c" btree (name) | |
"c_project_description_107f2cb7_like" btree (description varchar_pattern_ops) | |
"c_project_name_d268d16e_like" btree (name varchar_pattern_ops) | |
Referenced by: | |
TABLE "c_projectuser" CONSTRAINT "c_projectuser_project_id_221391ba_fk_c_project_id" FOREIGN KEY (project_id) REFERENCES c_project(id) DEFERRABLE INITIALLY DEFERRED |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from django.db import models | |
from django.db.models.expressions import Ref | |
from django.db.models.functions import Lower, Upper | |
class Project(models.Model): | |
name = models.CharField(max_length=100, db_index=Lower(Ref('name', None))) | |
description = models.CharField(max_length=500, db_index=Upper(Ref('description', None))) | |
createdBy = models.CharField(max_length=50) | |
createdOn = models.DateTimeField(auto_now_add=True) | |
modifiedBy = models.CharField(max_length=50, null=True, blank=True) | |
modifiedOn = models.DateTimeField(auto_now=True) | |
def __str__(self): | |
return self.name |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
In [1]: from django.db import connection | |
In [2]: from django.db.backends.postgresql_psycopg2.schema import DatabaseSchemaEditor | |
In [3]: from c.models import Project | |
In [4]: schema_editor = DatabaseSchemaEditor(connection, True) | |
In [5]: schema_editor._model_indexes_sql(Project) | |
Out[5]: | |
['CREATE INDEX "c_project_b068931c" ON "c_project" ("name")', | |
'CREATE INDEX "c_project_67daf92c" ON "c_project" ("description")', | |
'CREATE INDEX "c_project_name_d268d16e_like" ON "c_project" ("name" varchar_pattern_ops)', | |
'CREATE INDEX "c_project_L_a3a6aa61_func" ON "c_project" (LOWER("name"))', | |
'CREATE INDEX "c_project_description_107f2cb7_like" ON "c_project" ("description" varchar_pattern_ops)', | |
'CREATE INDEX "c_project_U_ee9b75f9_func" ON "c_project" (UPPER("description"))'] | |
In [6]: with connection.cursor() as cursor: | |
...: for q in schema_editor._model_indexes_sql(Project): | |
...: cursor.execute(q) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment