Skip to content

Commit 76c1eee

Browse files
committed
change polymorphic__proxy --> polymorphic_proxy and make it auto toggle on proxy
1 parent c626431 commit 76c1eee

File tree

7 files changed

+82
-14
lines changed

7 files changed

+82
-14
lines changed

docs/changelog.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ Changelog
44
v4.3.0 (202X-XX-XX)
55
-------------------
66

7+
* Implemented `Add polymorphic_proxy flag to enable polymorphic queries from proxy models. <https://github.com/jazzband/django-polymorphic/pull/676>`_
78
* Fixed `Resolve primary key name correctly. <https://github.com/jazzband/django-polymorphic/pull/620>`_
89
* Implemented `Include get_child_inlines() hook in stacked inline admin forms. <https://github.com/jazzband/django-polymorphic/pull/681>`_
910
* Fixed `multi-database support in inheritance accessors. <https://github.com/jazzband/django-polymorphic/pull/550>`_

docs/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,7 @@ Advanced topics
119119
formsets
120120
migrating
121121
managers
122+
proxy
122123
advanced
123124
changelog
124125
api/index

docs/proxy.rst

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
Proxy Models
2+
============
3+
4+
:pypi:`django-polymorphic` has supported :ref:`proxy models <django:proxy-models>` since they were
5+
introduced in Django but the default implementation is unintuitive. If a row is created from the
6+
proxy model the :class:`~django.contrib.contenttypes.models.ContentType` of the proxy class is
7+
recorded. This allows patterns that need to alter behavior based on which class was used to create
8+
the row.
9+
10+
**By default the queryset on proxy models will filter on instance_of(ProxyModel) which will exclude
11+
any rows that were not created from the proxy.**
12+
13+
.. code-block:: python
14+
15+
from polymorphic.models import PolymorphicModel
16+
17+
class MyModel(PolymorphicModel):
18+
...
19+
20+
class MyProxy(MyModel):
21+
class Meta:
22+
proxy = True
23+
24+
25+
MyModel.objects.create()
26+
MyProxy.objects.create()
27+
28+
assert MyModel.objects.count() == 2
29+
assert MyProxy.objects.count() == 1
30+
31+
This behavior may be unexpected for typical uses of proxy models which involves creating from the
32+
concrete class then accessing from a proxy in the context where you need the modified proxy
33+
interface. There is a
34+
`discussion <https://github.com/jazzband/django-polymorphic/discussions/689>`_ if this should
35+
continue to be the default behavior in version 5+.
36+
37+
Polymorphic Proxy Queries
38+
-------------------------
39+
40+
.. versionadded:: 4.3
41+
42+
If you wish for your proxy model querysets to behave polymorphically by default
43+
(include all rows created by the proxy and concrete models in the class's inheritance tree) then
44+
instead of (or in additon to) :attr:`~django.db.models.Options.proxy` set a
45+
:attr:`Meta.polymorphic_proxy` attribute to True:
46+
47+
.. code-block:: python
48+
49+
from polymorphic.models import PolymorphicModel
50+
51+
class MyModel(PolymorphicModel):
52+
...
53+
54+
class MyProxy(MyModel):
55+
class Meta:
56+
polymorphc_proxy = True
57+
58+
59+
MyModel.objects.create()
60+
MyProxy.objects.create()
61+
62+
assert MyModel.objects.count() == 2
63+
assert MyProxy.objects.count() == 2
64+
65+
for proxy in MyProxy.objects.all():
66+
assert isinstance(proxy, MyProxy) #

src/polymorphic/base.py

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -55,9 +55,11 @@ class PolymorphicModelBase(ModelBase):
5555
def __new__(self, model_name, bases, attrs, **kwargs):
5656
polymorphic__proxy = None
5757
if "Meta" in attrs:
58-
if hasattr(attrs["Meta"], "polymorphic__proxy"):
59-
polymorphic__proxy = attrs["Meta"].polymorphic__proxy
60-
del attrs["Meta"].polymorphic__proxy
58+
if hasattr(attrs["Meta"], "polymorphic_proxy"):
59+
polymorphic__proxy = attrs["Meta"].polymorphic_proxy
60+
if polymorphic__proxy:
61+
attrs["Meta"].proxy = True
62+
del attrs["Meta"].polymorphic_proxy
6163

6264
# Workaround compatibility issue with six.with_metaclass() and custom Django model metaclasses:
6365
if not attrs and model_name == "NewBase":
@@ -84,9 +86,9 @@ def __new__(self, model_name, bases, attrs, **kwargs):
8486
new_class.polymorphic_super_sub_accessors_replaced = False
8587

8688
if polymorphic__proxy is not None:
87-
new_class._meta.polymorphic__proxy = polymorphic__proxy
89+
new_class._meta.polymorphic_proxy = polymorphic__proxy
8890
else:
89-
new_class._meta.polymorphic__proxy = not new_class._meta.proxy
91+
new_class._meta.polymorphic_proxy = not new_class._meta.proxy
9092

9193
# determine the name of the primary key field and store it into the class variable
9294
# polymorphic_primary_key_name (it is needed by query.py)

src/polymorphic/managers.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ def from_queryset(cls, queryset_class, class_name=None):
2828

2929
def get_queryset(self):
3030
qs = self.queryset_class(self.model, using=self._db, hints=self._hints)
31-
if not self.model._meta.polymorphic__proxy:
31+
if not self.model._meta.polymorphic_proxy:
3232
qs = qs.instance_of(self.model)
3333
return qs
3434

src/polymorphic/tests/models.py

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -385,15 +385,14 @@ class NonProxyChild(ProxyBase):
385385
# base(poly) -> proxy(poly) -> proxy(Traditional Django)
386386
class TradProxyOnProxyChild(ProxyChild):
387387
class Meta:
388-
proxy = True
389-
polymorphic__proxy = True
388+
polymorphic_proxy = True
390389

391390

392391
# base(poly) -> proxy(Traditional Django)
393392
class TradProxyChild(ProxyBase):
394393
class Meta:
395394
proxy = True
396-
polymorphic__proxy = True
395+
polymorphic_proxy = True
397396

398397

399398
# base(poly) -> proxy(Traditional Django) -> proxy(poly)
@@ -404,7 +403,7 @@ class Meta:
404403
class AliasOfNonProxyChild(NonProxyChild):
405404
class Meta:
406405
proxy = True
407-
polymorphic__proxy = True
406+
polymorphic_proxy = True
408407

409408

410409
# base(poly) -> proxy(Traditional Django) -> proxy(poly)
@@ -416,15 +415,14 @@ class Meta:
416415
# base(poly) -> proxy(poly)
417416
class AliasProxyChild(ProxyBase):
418417
class Meta:
419-
proxy = True
420-
polymorphic__proxy = True
418+
polymorphic_proxy = True
421419

422420

423421
# child(poly) -> proxy(poly)
424422
class NonAliasNonProxyChild(NonProxyChild):
425423
class Meta:
426424
proxy = True
427-
polymorphic__proxy = False
425+
polymorphic_proxy = False
428426

429427

430428
class ProxiedBase(ShowFieldTypeAndContent, PolymorphicModel):

src/polymorphic/tests/test_orm.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -927,7 +927,7 @@ def test_convert_back_to_django_style_from_polymorphic_stops_at_concrete(self):
927927
self.assertEqual(2, AliasOfNonProxyChild.objects.count())
928928

929929
def test_revert_back_to_polymorphic_proxy(self):
930-
self.assertFalse(ProxyChildAliasProxy._meta.polymorphic__proxy)
930+
self.assertFalse(ProxyChildAliasProxy._meta.polymorphic_proxy)
931931

932932
def test_proxy_get_real_instance_class(self):
933933
"""

0 commit comments

Comments
 (0)