Skip to content

Commit

Permalink
Merge pull request #365 from ghost/fix/37-subclass-selector-on-abstra…
Browse files Browse the repository at this point in the history
…ct-proxy-models

#37: Fix model subclass ___ selector for abstract/proxy models
  • Loading branch information
vdboor authored Jul 11, 2019
2 parents 25f4dbb + 14b31be commit ec3fb34
Show file tree
Hide file tree
Showing 5 changed files with 121 additions and 6 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')
34 changes: 28 additions & 6 deletions polymorphic/tests/test_orm.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,10 @@
RelationBC,
RelationBase,
RubberDuck,
SubclassSelectorAbstractBaseModel,
SubclassSelectorAbstractConcreteModel,
SubclassSelectorProxyBaseModel,
SubclassSelectorProxyConcreteModel,
TestParentLinkAndRelatedName,
UUIDArtProject,
UUIDPlainA,
Expand Down Expand Up @@ -859,21 +863,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 @@ -1024,3 +1028,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 ec3fb34

Please sign in to comment.