Skip to content

Commit

Permalink
#37: Fix model subclass ___ selector for abstract/proxy models
Browse files Browse the repository at this point in the history
  • Loading branch information
lzdun committed Sep 29, 2018
1 parent 6d27bb3 commit 14b31be
Show file tree
Hide file tree
Showing 5 changed files with 122 additions and 8 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,4 @@ build/
dist/
docs/_build/
htmlcov/
venv/
2 changes: 2 additions & 0 deletions polymorphic/query_translate.py
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,8 @@ def _create_base_path(baseclass, myclass):
return myclass.__name__.lower()
path = _create_base_path(baseclass, b)
if path:
if b._meta.abstract or b._meta.proxy:
return myclass.__name__.lower()
return path + '__' + myclass.__name__.lower()
return ''

Expand Down
62 changes: 62 additions & 0 deletions polymorphic/tests/migrations/0001_initial.py
Original file line number Diff line number Diff line change
Expand Up @@ -1130,4 +1130,66 @@ class Migration(migrations.Migration):
},
bases=('tests.duck',),
),
migrations.CreateModel(
name='SubclassSelectorAbstractBaseModel',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('base_field', models.CharField(default='test_bf', max_length=10)),
],
options={
'abstract': False,
'base_manager_name': 'objects',
},
),
migrations.CreateModel(
name='SubclassSelectorProxyBaseModel',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('base_field', models.CharField(default='test_bf', max_length=10)),
('polymorphic_ctype', models.ForeignKey(editable=False, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='polymorphic_tests.subclassselectorproxybasemodel_set+', to='contenttypes.ContentType')),
],
options={
'abstract': False,
'base_manager_name': 'objects',
},
),
migrations.CreateModel(
name='SubclassSelectorAbstractConcreteModel',
fields=[
('subclassselectorabstractbasemodel_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='tests.SubclassSelectorAbstractBaseModel')),
('abstract_field', models.CharField(default='test_af', max_length=10)),
('concrete_field', models.CharField(default='test_cf', max_length=10)),
],
options={
'abstract': False,
},
bases=('tests.subclassselectorabstractbasemodel',),
),
migrations.AddField(
model_name='subclassselectorabstractbasemodel',
name='polymorphic_ctype',
field=models.ForeignKey(editable=False, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='polymorphic_tests.subclassselectorabstractbasemodel_set+', to='contenttypes.ContentType'),
),
migrations.CreateModel(
name='SubclassSelectorProxyModel',
fields=[
],
options={
'proxy': True,
'indexes': [],
},
bases=('tests.subclassselectorproxybasemodel',),
),
migrations.CreateModel(
name='SubclassSelectorProxyConcreteModel',
fields=[
('subclassselectorproxybasemodel_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='tests.SubclassSelectorProxyBaseModel')),
('concrete_field', models.CharField(default='test_cf', max_length=10)),
],
options={
'abstract': False,
'base_manager_name': 'objects',
},
bases=('tests.subclassselectorproxymodel',),
),
]
28 changes: 28 additions & 0 deletions polymorphic/tests/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -450,3 +450,31 @@ class MultiTableBase(PolymorphicModel):

class MultiTableDerived(MultiTableBase):
field2 = models.CharField(max_length=10)


class SubclassSelectorAbstractBaseModel(PolymorphicModel):
base_field = models.CharField(max_length=10, default='test_bf')


class SubclassSelectorAbstractModel(SubclassSelectorAbstractBaseModel):
abstract_field = models.CharField(max_length=10, default='test_af')

class Meta:
abstract = True


class SubclassSelectorAbstractConcreteModel(SubclassSelectorAbstractModel):
concrete_field = models.CharField(max_length=10, default='test_cf')


class SubclassSelectorProxyBaseModel(PolymorphicModel):
base_field = models.CharField(max_length=10, default='test_bf')


class SubclassSelectorProxyModel(SubclassSelectorProxyBaseModel):
class Meta:
proxy = True


class SubclassSelectorProxyConcreteModel(SubclassSelectorProxyModel):
concrete_field = models.CharField(max_length=10, default='test_cf')
37 changes: 29 additions & 8 deletions polymorphic/tests/test_orm.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from django.contrib.contenttypes.models import ContentType
from django.db import models
from django.db.models import Case, Count, Q, When
from django.test import TestCase, TransactionTestCase
from django.test import TransactionTestCase
from django.utils import six

from polymorphic import query_translate
Expand Down Expand Up @@ -68,13 +68,16 @@
ProxyModelA,
ProxyModelB,
ProxyModelBase,
QuerySet,
RedheadDuck,
RelationA,
RelationB,
RelationBC,
RelationBase,
RubberDuck,
SubclassSelectorAbstractBaseModel,
SubclassSelectorAbstractConcreteModel,
SubclassSelectorProxyBaseModel,
SubclassSelectorProxyConcreteModel,
TestParentLinkAndRelatedName,
UUIDArtProject,
UUIDPlainA,
Expand Down Expand Up @@ -852,21 +855,21 @@ def test_proxy_model_inheritance(self):
self.assertEqual(ProxyModelB.objects.model, ProxyModelB)

# Create objects
ProxyModelA.objects.create(name="object1")
ProxyModelB.objects.create(name="object2", field2="bb")
object1_pk = ProxyModelA.objects.create(name="object1").pk
object2_pk = ProxyModelB.objects.create(name="object2", field2="bb").pk

# Getting single objects
object1 = ProxyModelBase.objects.get(name='object1')
object2 = ProxyModelBase.objects.get(name='object2')
self.assertEqual(repr(object1), '<ProxyModelA: id 1, name (CharField) "object1", field1 (CharField) "">')
self.assertEqual(repr(object2), '<ProxyModelB: id 2, name (CharField) "object2", field2 (CharField) "bb">')
self.assertEqual(repr(object1), '<ProxyModelA: id %i, name (CharField) "object1", field1 (CharField) "">' % object1_pk)
self.assertEqual(repr(object2), '<ProxyModelB: id %i, name (CharField) "object2", field2 (CharField) "bb">' % object2_pk)
self.assertIsInstance(object1, ProxyModelA)
self.assertIsInstance(object2, ProxyModelB)

# Same for lists
objects = list(ProxyModelBase.objects.all().order_by('name'))
self.assertEqual(repr(objects[0]), '<ProxyModelA: id 1, name (CharField) "object1", field1 (CharField) "">')
self.assertEqual(repr(objects[1]), '<ProxyModelB: id 2, name (CharField) "object2", field2 (CharField) "bb">')
self.assertEqual(repr(objects[0]), '<ProxyModelA: id %i, name (CharField) "object1", field1 (CharField) "">' % object1_pk)
self.assertEqual(repr(objects[1]), '<ProxyModelB: id %i, name (CharField) "object2", field2 (CharField) "bb">' % object2_pk)
self.assertIsInstance(objects[0], ProxyModelA)
self.assertIsInstance(objects[1], ProxyModelB)

Expand Down Expand Up @@ -1006,3 +1009,21 @@ def test_bulk_create_unsupported_multi_table_inheritance(self):
MultiTableDerived.objects.bulk_create([
MultiTableDerived(field1='field1', field2='field2')
])

def test_can_query_using_subclass_selector_on_abstract_model(self):
obj = SubclassSelectorAbstractConcreteModel.objects.create(concrete_field='abc')

queried_obj = SubclassSelectorAbstractBaseModel.objects.filter(
SubclassSelectorAbstractConcreteModel___concrete_field='abc'
).get()

self.assertEqual(obj.pk, queried_obj.pk)

def test_can_query_using_subclass_selector_on_proxy_model(self):
obj = SubclassSelectorProxyConcreteModel.objects.create(concrete_field='abc')

queried_obj = SubclassSelectorProxyBaseModel.objects.filter(
SubclassSelectorProxyConcreteModel___concrete_field='abc'
).get()

self.assertEqual(obj.pk, queried_obj.pk)

0 comments on commit 14b31be

Please sign in to comment.