Skip to content

Commit

Permalink
Fixes #2
Browse files Browse the repository at this point in the history
  • Loading branch information
mattiaslinnap committed Aug 7, 2017
1 parent b006746 commit 69fe048
Show file tree
Hide file tree
Showing 5 changed files with 76 additions and 7 deletions.
9 changes: 6 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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.
Expand Down
30 changes: 29 additions & 1 deletion partial_index/__init__.py
Original file line number Diff line number Diff line change
@@ -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)


Expand Down Expand Up @@ -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()
6 changes: 3 additions & 3 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -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='[email protected]',
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=[
Expand All @@ -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',
]
Expand Down
33 changes: 33 additions & 0 deletions tests/test_partial_index.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
"""
from django.test import SimpleTestCase
from partial_index import PartialIndex
from testapp.models import AB


class PartialIndexTest(SimpleTestCase):
Expand Down Expand Up @@ -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)
5 changes: 5 additions & 0 deletions tests/testapp/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down

0 comments on commit 69fe048

Please sign in to comment.