Skip to content

Commit

Permalink
add select_related and partial prefetch_related support
Browse files Browse the repository at this point in the history
implement support for a single query for select related base fetches across
polymorphic models.

adds a polymorphic QuerySet Mixin to enable non polymorphic models to fetch
related models.

Fixes: jazzband#198 jazzband#436 jazzband#359 jazzband#244
Possible Fixes:
  jazzband#498: support for prefetch_related cannot fetch attributes not on all child models or via class names
Related: jazzband#531
  • Loading branch information
pgammans committed Jun 26, 2023
1 parent 6990167 commit 9cd5200
Show file tree
Hide file tree
Showing 7 changed files with 1,401 additions and 261 deletions.
11 changes: 0 additions & 11 deletions polymorphic/base.py
Expand Up @@ -57,12 +57,6 @@ class PolymorphicModelBase(ModelBase):
def __new__(self, model_name, bases, attrs, **kwargs):
# print; print '###', model_name, '- bases:', bases

polymorphic__proxy = None
if "Meta" in attrs:
if hasattr(attrs["Meta"], "polymorphic__proxy"):
polymorphic__proxy = attrs["Meta"].polymorphic__proxy
del attrs["Meta"].polymorphic__proxy

# Workaround compatibility issue with six.with_metaclass() and custom Django model metaclasses:
if not attrs and model_name == "NewBase":
return super().__new__(self, model_name, bases, attrs, **kwargs)
Expand All @@ -89,11 +83,6 @@ def __new__(self, model_name, bases, attrs, **kwargs):
# for __init__ function of this class (monkeypatching inheritance accessors)
new_class.polymorphic_super_sub_accessors_replaced = False

if polymorphic__proxy is not None:
new_class._meta.polymorphic__proxy = polymorphic__proxy
else:
new_class._meta.polymorphic__proxy = not new_class._meta.proxy

# determine the name of the primary key field and store it into the class variable
# polymorphic_primary_key_name (it is needed by query.py)
for f in new_class._meta.fields:
Expand Down
2 changes: 1 addition & 1 deletion polymorphic/managers.py
Expand Up @@ -28,7 +28,7 @@ def from_queryset(cls, queryset_class, class_name=None):

def get_queryset(self):
qs = self.queryset_class(self.model, using=self._db, hints=self._hints)
if not self.model._meta.polymorphic__proxy:
if self.model._meta.proxy:
qs = qs.instance_of(self.model)
return qs

Expand Down
13 changes: 11 additions & 2 deletions polymorphic/models.py
Expand Up @@ -198,9 +198,18 @@ def __init__(self, *args, **kwargs):
self.__class__.polymorphic_super_sub_accessors_replaced = True

def create_accessor_function_for_model(model, accessor_name):
NOT_PROVIDED = object()

def accessor_function(self):
objects = getattr(model, "_base_objects", model.objects)
attr = objects.get(pk=self.pk)
attr = NOT_PROVIDED
try:
attr = self._state.fields_cache[accessor_name]
pass
except KeyError:
pass
if attr is NOT_PROVIDED:
objects = getattr(model, "_base_objects", model.objects)
attr = objects.get(pk=self.pk)
return attr

return accessor_function
Expand Down

0 comments on commit 9cd5200

Please sign in to comment.