• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

charettes / django-polymodels / 14162717205

31 Mar 2025 02:19AM UTC coverage: 99.112%. Remained the same
14162717205

push

github

charettes
Drop support for Django < 4.2 and Python < 3.9.

128 of 137 branches covered (93.43%)

335 of 338 relevant lines covered (99.11%)

4.96 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

98.53
/polymodels/managers.py
1
from functools import partial
5✔
2
from operator import methodcaller
5✔
3

4
from django.core.exceptions import ImproperlyConfigured
5✔
5
from django.db import models
5✔
6
from django.db.models.query import ModelIterable
5✔
7

8
type_cast_iterator = partial(map, methodcaller("type_cast"))
5✔
9
type_cast_prefetch_iterator = partial(
5✔
10
    map, methodcaller("type_cast", with_prefetched_objects=True)
11
)
12

13

14
class PolymorphicModelIterable(ModelIterable):
5✔
15
    def __init__(self, queryset, type_cast=True, **kwargs):
5✔
16
        self.type_cast = type_cast
5✔
17
        super().__init__(queryset, **kwargs)
5✔
18

19
    def __iter__(self):
5✔
20
        iterator = super().__iter__()
5✔
21
        if self.type_cast:
5✔
22
            iterator = type_cast_iterator(iterator)
5✔
23
        return iterator
5✔
24

25

26
class PolymorphicQuerySet(models.query.QuerySet):
5✔
27
    def select_subclasses(self, *models):
5✔
28
        if issubclass(self._iterable_class, ModelIterable):
5!
29
            self._iterable_class = PolymorphicModelIterable
5✔
30
        related_lookups = set()
5✔
31
        accessors = self.model.subclass_accessors
5✔
32
        if models:
5✔
33
            subclasses = set()
5✔
34
            for model in models:
5✔
35
                if not issubclass(model, self.model):
5!
36
                    raise TypeError("%r is not a subclass of %r" % (model, self.model))
×
37
                subclasses.update(model.subclass_accessors)
5✔
38
            # Collect all `select_related` required lookups
39
            for subclass in subclasses:
5✔
40
                # Avoid collecting ourself and proxy subclasses
41
                related_lookup = accessors[subclass].related_lookup
5✔
42
                if related_lookup:
5✔
43
                    related_lookups.add(related_lookup)
5✔
44
            queryset = self.filter(**self.model.content_type_lookup(*tuple(subclasses)))
5✔
45
        else:
46
            # Collect all `select_related` required relateds
47
            for accessor in accessors.values():
5✔
48
                # Avoid collecting ourself and proxy subclasses
49
                related_lookup = accessor.related_lookup
5✔
50
                if related_lookup:
5✔
51
                    related_lookups.add(related_lookup)
5✔
52
            queryset = self
5✔
53
        if related_lookups:
5✔
54
            queryset = queryset.select_related(*related_lookups)
5✔
55
        return queryset
5✔
56

57
    def exclude_subclasses(self):
5✔
58
        return self.filter(**self.model.content_type_lookup())
5✔
59

60
    def _fetch_all(self):
5✔
61
        # Override _fetch_all in order to disable PolymorphicModelIterable's
62
        # type casting when prefetch_related is used because the latter might
63
        # crash or disfunction when dealing with a mixed set of objects.
64
        prefetch_related_objects = (
5✔
65
            self._prefetch_related_lookups and not self._prefetch_done
66
        )
67
        type_cast = False
5✔
68
        if self._result_cache is None:
5✔
69
            iterable_class = self._iterable_class
5✔
70
            if issubclass(iterable_class, PolymorphicModelIterable):
5✔
71
                type_cast = bool(prefetch_related_objects)
5✔
72
                iterable_class = partial(iterable_class, type_cast=not type_cast)
5✔
73
            self._result_cache = list(iterable_class(self))
5✔
74
        if prefetch_related_objects:
5✔
75
            self._prefetch_related_objects()
5✔
76
            if type_cast:
5!
77
                self._result_cache = list(
5✔
78
                    type_cast_prefetch_iterator(self._result_cache)
79
                )
80

81

82
class PolymorphicManager(models.Manager.from_queryset(PolymorphicQuerySet)):
5✔
83
    def contribute_to_class(self, model, name):
5✔
84
        # Avoid circular reference
85
        from .models import BasePolymorphicModel
5✔
86

87
        if not issubclass(model, BasePolymorphicModel):
5✔
88
            raise ImproperlyConfigured(
5✔
89
                "`%s` can only be used on "
90
                "`BasePolymorphicModel` subclasses." % self.__class__.__name__
91
            )
92
        return super().contribute_to_class(model, name)
5✔
93

94
    def get_queryset(self):
5✔
95
        queryset = super().get_queryset()
5✔
96
        model = self.model
5✔
97
        if model._meta.proxy:
5✔
98
            # Select only associated model and its subclasses.
99
            queryset = queryset.filter(**self.model.subclasses_lookup())
5✔
100
        return queryset
5✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc