Skip to content

Commit

Permalink
Merge pull request #577 from orf/patch-4
Browse files Browse the repository at this point in the history
Add 'tree_id', 'lft' to index_together. Thanks @orf :)
  • Loading branch information
craigds committed Sep 1, 2017
2 parents 9f8b0e3 + a772178 commit 9aa8e58
Show file tree
Hide file tree
Showing 4 changed files with 77 additions and 4 deletions.
3 changes: 3 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
sudo: false
language: python
cache: pip
git:
depth: 1

python:
- "3.6"
Expand Down
18 changes: 14 additions & 4 deletions mptt/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -326,8 +326,10 @@ def register(meta, cls, **kwargs):

bases.insert(0, MPTTModel)
cls.__bases__ = tuple(bases)

if _get_tree_model(cls) is cls:

is_cls_tree_model = _get_tree_model(cls) is cls

if is_cls_tree_model:
# HACK: _meta.get_field() doesn't work before AppCache.ready in Django>=1.8
# ( see https://code.djangoproject.com/ticket/24231 )
# So the only way to get existing fields is using local_fields on all superclasses.
Expand All @@ -336,12 +338,20 @@ def register(meta, cls, **kwargs):
if hasattr(base, '_meta'):
existing_field_names.update([f.name for f in base._meta.local_fields])

for key in ('left_attr', 'right_attr', 'tree_id_attr', 'level_attr'):
field_name = getattr(cls._mptt_meta, key)
mptt_meta = cls._mptt_meta
field_names = (mptt_meta.left_attr, mptt_meta.right_attr, mptt_meta.tree_id_attr, mptt_meta.level_attr)

for field_name in field_names:
if field_name not in existing_field_names:
field = models.PositiveIntegerField(db_index=True, editable=False)
field.contribute_to_class(cls, field_name)

# Add an index_together on tree_id_attr and left_attr, as these are very
# commonly queried (pretty much all reads).
index_together = (cls._mptt_meta.tree_id_attr, cls._mptt_meta.left_attr)
if index_together not in cls._meta.index_together:
cls._meta.index_together += (index_together,)

# Add a tree manager, if there isn't one already
if not abstract:
# make sure we have a tree manager somewhere
Expand Down
10 changes: 10 additions & 0 deletions tests/manage.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#!/usr/bin/env python
import os
import sys

if __name__ == "__main__":
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "settings")

from django.core.management import execute_from_command_line

execute_from_command_line(sys.argv)
50 changes: 50 additions & 0 deletions tests/myapp/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -2343,3 +2343,53 @@ def test_nullable_ordered_insertion_desc(self):
2 1 1 1 2 3
3 1 1 1 4 5
""")


class ModelMeta(TreeTestCase):
def test_index_together(self):
already_idx = [['tree_id', 'lft'], ('tree_id', 'lft')]
no_idx = [tuple(), list()]
some_idx = [['tree_id'], ('tree_id',), [['tree_id']], (('tree_id',),)]

for idx, case in enumerate(already_idx + no_idx + some_idx):
class Meta:
index_together = case
app_label = 'myapp'

# Use type() here and in test_index_together_different_attr over
# an explicit class X(MPTTModel):, as this throws a warning that
# re-registering models with the same name (which is what an explicit
# class does) could cause errors. Kind of... weird, but surprisingly
# effective.

# Use str(__name__) as __module__ must be a 'str' type and not unicode
# on Python 2.7

SomeModel = type(str('model_{0}'.format(idx)), (MPTTModel,), {
'Meta': Meta,
'__module__': str(__name__)
})

self.assertIn(('tree_id', 'lft'), SomeModel._meta.index_together)

def test_index_together_different_attr(self):
already_idx = [['abc', 'def'], ('abc', 'def')]
no_idx = [tuple(), list()]
some_idx = [['abc'], ('abc',), [['abc']], (('abc',),)]

for idx, case in enumerate(already_idx + no_idx + some_idx):
class MPTTMeta:
tree_id_attr = 'abc'
left_attr = 'def'

class Meta:
index_together = case
app_label = 'myapp'

SomeModel = type(str('model__different_attr_{0}'.format(idx)), (MPTTModel,), {
'MPTTMeta': MPTTMeta,
'Meta': Meta,
'__module__': str(__name__)
})

self.assertIn(('abc', 'def'), SomeModel._meta.index_together)

0 comments on commit 9aa8e58

Please sign in to comment.