From 69fe0488b4e8b9b21aaf7f64c597528a8816892d Mon Sep 17 00:00:00 2001 From: Mattias Linnap Date: Mon, 7 Aug 2017 11:29:44 +0300 Subject: [PATCH] Fixes https://github.com/mattiaslinnap/django-partial-index/issues/2 --- README.md | 9 ++++++--- partial_index/__init__.py | 30 +++++++++++++++++++++++++++++- setup.py | 6 +++--- tests/test_partial_index.py | 33 +++++++++++++++++++++++++++++++++ tests/testapp/models.py | 5 +++++ 5 files changed, 76 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index a1f2850..670e3a0 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ Requirements: * Django 1.11 or later. * PostgreSQL or SQLite database backend. (Partial indexes are not supported on MySQL, and require major hackery on Oracle.) -* Python 2.7, 3.4, 3.5, or 3.6. Other versions will probably work as well, but have not been tested yet. +* Python 2.7 and 3.4 - 3.6. (All Python versions supported by Django 1.11.) ## Usage @@ -70,10 +70,13 @@ But the constraints are part of the business logic, and best kept close to the m ## Version History -### 0.2.0 (latest) +## 0.2.1 (latest) +* Ensure that automatically generated index names depend on the "unique" and "where" parameters. Otherwise two indexes with the same fields would be considered identical by Django. + +### 0.2.0 * Fully tested SQLite and PostgreSQL support. * Tests for generated SQL statements, adding and removing indexes, and that unique constraints work when inserting rows into the db tables. -* Python 2.7, 3.4, 3.5, 3.6 support. +* Python 2.7, 3.4-3.6 support. ### 0.1.1 * Experimental SQLite support. diff --git a/partial_index/__init__.py b/partial_index/__init__.py index 8f1f0bb..d082918 100644 --- a/partial_index/__init__.py +++ b/partial_index/__init__.py @@ -1,6 +1,6 @@ # Provide a nicer error message than failing to import models.Index. -VERSION = (0, 2, 0) +VERSION = (0, 2, 1) __version__ = '.'.join(str(v) for v in VERSION) @@ -71,3 +71,31 @@ def create_sql(self, model, schema_editor, using=''): sql_template = self.sql_create_index[vendor] sql_parameters = self.get_sql_create_template_values(model, schema_editor, using) return sql_template % sql_parameters + + def name_hash_extra_data(self): + return [str(self.unique), self.where] + + def set_name_with_model(self, model): + """Sets an unique generated name for the index. + + PartialIndex would like to only override "hash_data = ...", but the entire method must be duplicated for that. + """ + table_name = model._meta.db_table + column_names = [model._meta.get_field(field_name).column for field_name, order in self.fields_orders] + column_names_with_order = [ + (('-%s' if order else '%s') % column_name) + for column_name, (field_name, order) in zip(column_names, self.fields_orders) + ] + # The length of the parts of the name is based on the default max + # length of 30 characters. + hash_data = [table_name] + column_names_with_order + [self.suffix] + self.name_hash_extra_data() + self.name = '%s_%s_%s' % ( + table_name[:11], + column_names[0][:7], + '%s_%s' % (self._hash_generator(*hash_data), self.suffix), + ) + assert len(self.name) <= self.max_name_length, ( + 'Index too long for multiple database support. Is self.suffix ' + 'longer than 3 characters?' + ) + self.check_name() diff --git a/setup.py b/setup.py index 3fe4dec..70d478b 100755 --- a/setup.py +++ b/setup.py @@ -6,13 +6,13 @@ setup( name='django-partial-index', packages=['partial_index'], - version='0.2.0', + version='0.2.1', description='PostgreSQL and SQLite partial indexes for Django models', long_description=open('README.md').read(), author='Mattias Linnap', author_email='mattias@linnap.com', url='https://github.com/mattiaslinnap/django-partial-index', - download_url='https://github.com/mattiaslinnap/django-partial-index/archive/0.2.0.tar.gz', + download_url='https://github.com/mattiaslinnap/django-partial-index/archive/0.2.1.tar.gz', license='BSD', install_requires=[], classifiers=[ @@ -27,9 +27,9 @@ 'Programming Language :: Python :: 2', 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.3', 'Programming Language :: Python :: 3.4', 'Programming Language :: Python :: 3.5', + 'Programming Language :: Python :: 3.6', 'Topic :: Database', 'Topic :: Internet :: WWW/HTTP', ] diff --git a/tests/test_partial_index.py b/tests/test_partial_index.py index b94b7cb..0ef5204 100644 --- a/tests/test_partial_index.py +++ b/tests/test_partial_index.py @@ -5,6 +5,7 @@ """ from django.test import SimpleTestCase from partial_index import PartialIndex +from testapp.models import AB class PartialIndexTest(SimpleTestCase): @@ -40,3 +41,35 @@ def test_deconstruct(self): def test_suffix(self): self.assertEqual(self.idx.suffix, 'partial') + def test_generated_name_ends_with_partial(self): + idx = PartialIndex(fields=['a', 'b'], unique=False, where='a IS NULL') + idx.set_name_with_model(AB) + self.assertEqual(idx.name[-8:], '_partial') + + def test_field_sort_changes_generated_name(self): + idx1 = PartialIndex(fields=['a', 'b'], unique=False, where='a IS NULL') + idx1.set_name_with_model(AB) + idx2 = PartialIndex(fields=['a', '-b'], unique=False, where='a IS NULL') + idx2.set_name_with_model(AB) + self.assertNotEqual(idx1.name, idx2.name) + + def test_field_order_changes_generated_name(self): + idx1 = PartialIndex(fields=['a', 'b'], unique=False, where='a IS NULL') + idx1.set_name_with_model(AB) + idx2 = PartialIndex(fields=['b', 'a'], unique=False, where='a IS NULL') + idx2.set_name_with_model(AB) + self.assertNotEqual(idx1.name, idx2.name) + + def test_unique_changes_generated_name(self): + idx1 = PartialIndex(fields=['a', 'b'], unique=False, where='a IS NULL') + idx1.set_name_with_model(AB) + idx2 = PartialIndex(fields=['a', 'b'], unique=True, where='a IS NULL') + idx2.set_name_with_model(AB) + self.assertNotEqual(idx1.name, idx2.name) + + def test_where_changes_generated_name(self): + idx1 = PartialIndex(fields=['a', 'b'], unique=False, where='a IS NULL') + idx1.set_name_with_model(AB) + idx2 = PartialIndex(fields=['a', 'b'], unique=False, where='a IS NOT NULL') + idx2.set_name_with_model(AB) + self.assertNotEqual(idx1.name, idx2.name) diff --git a/tests/testapp/models.py b/tests/testapp/models.py index b7d4c33..2aaafe9 100644 --- a/tests/testapp/models.py +++ b/tests/testapp/models.py @@ -3,6 +3,11 @@ from partial_index import PartialIndex +class AB(models.Model): + a = models.CharField(max_length=50) + b = models.CharField(max_length=50) + + class User(models.Model): name = models.CharField(max_length=50)