Skip to content

Commit

Permalink
fix: mypy typing
Browse files Browse the repository at this point in the history
  • Loading branch information
bradenmacdonald committed Feb 20, 2025
1 parent 4f50022 commit 4e1c2d7
Show file tree
Hide file tree
Showing 6 changed files with 49 additions and 70 deletions.
16 changes: 6 additions & 10 deletions openedx_learning/apps/authoring/components/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
"""
from __future__ import annotations

from typing import ClassVar

from django.db import models

from ....lib.fields import case_sensitive_char_field, immutable_uuid_field, key_field
Expand Down Expand Up @@ -76,7 +78,7 @@ def __str__(self):
return f"{self.namespace}:{self.name}"


class Component(PublishableEntityMixin): # type: ignore[django-manager-missing]
class Component(PublishableEntityMixin):
"""
This represents any Component that has ever existed in a LearningPackage.
Expand Down Expand Up @@ -120,14 +122,12 @@ class Component(PublishableEntityMixin): # type: ignore[django-manager-missing]
Make a foreign key to the Component model when you need a stable reference
that will exist for as long as the LearningPackage itself exists.
"""
# Tell mypy what type our objects manager has.
# It's actually PublishableEntityMixinManager, but that has the exact same
# interface as the base manager class.
objects: models.Manager[Component] = WithRelationsManager(
# Set up our custom manager. It has the same API as the default one, but selects related objects by default.
objects: ClassVar[WithRelationsManager[Component]] = WithRelationsManager( # type: ignore[assignment]
'component_type'
)

with_publishing_relations: models.Manager[Component] = WithRelationsManager(
with_publishing_relations = WithRelationsManager(
'component_type',
'publishable_entity',
'publishable_entity__draft__version',
Expand Down Expand Up @@ -201,10 +201,6 @@ class ComponentVersion(PublishableEntityVersionMixin):
This holds the content using a M:M relationship with Content via
ComponentVersionContent.
"""
# Tell mypy what type our objects manager has.
# It's actually PublishableEntityVersionMixinManager, but that has the exact
# same interface as the base manager class.
objects: models.Manager[ComponentVersion]

# This is technically redundant, since we can get this through
# publishable_entity_version.publishable.component, but this is more
Expand Down
16 changes: 9 additions & 7 deletions openedx_learning/apps/authoring/containers/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ def create_next_defined_list(
new_entity_list: EntityList,
entity_pks: list[int],
entity_version_pks: list[int | None],
) -> EntityListRow:
) -> EntityList:
"""
[ 🛑 UNSTABLE ]
Create new entity list rows for an entity list.
Expand Down Expand Up @@ -384,7 +384,7 @@ def create_container_and_version(
title: str,
publishable_entities_pks: list[int],
entity_version_pks: list[int | None],
) -> ContainerEntityVersion:
) -> tuple[ContainerEntity, ContainerEntityVersion]:
"""
[ 🛑 UNSTABLE ]
Create a new container and its first version.
Expand Down Expand Up @@ -490,10 +490,12 @@ def get_entities_in_published_container(
# different?
entity_list = []
for row in cev.defined_list.entitylistrow_set.order_by("order_num"):
entity_list.append(ContainerEntityListEntry(
entity_version=row.entity_version or row.entity.published.version,
pinned=row.entity_version is not None,
))
entity_version = row.entity_version or row.entity.published.version
if entity_version is not None: # As long as this hasn't been soft-deleted:
entity_list.append(ContainerEntityListEntry(
entity_version=entity_version,
pinned=row.entity_version is not None,
))
return entity_list


Expand Down Expand Up @@ -536,7 +538,7 @@ def contains_unpublished_changes(
):
try:
child_container = row.entity.containerentity
except PublishableEntity.containerentity.RelatedObjectDoesNotExist: # pylint: disable=no-member
except PublishableEntity.containerentity.RelatedObjectDoesNotExist: # type: ignore[attr-defined] # pylint: disable=no-member
child_container = None
if child_container:
child_container = row.entity.containerentity
Expand Down
30 changes: 7 additions & 23 deletions openedx_learning/apps/authoring/containers/models_mixin.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
Mixins for models that implement containers
"""
from __future__ import annotations
from typing import ClassVar, Self

from django.db import models
from django.db.models.query import QuerySet
Expand All @@ -14,6 +15,7 @@
PublishableEntityMixin,
PublishableEntityVersionMixin,
)
from openedx_learning.lib.managers import WithRelationsManager

__all__ = [
"ContainerEntityMixin",
Expand All @@ -30,17 +32,8 @@ class ContainerEntityMixin(PublishableEntityMixin):
If you use this class, you *MUST* also use ContainerEntityVersionMixin
"""

class ContainerEntityMixinManager(models.Manager):
def get_queryset(self) -> QuerySet:
return (
super()
.get_queryset()
.select_related(
"container_entity",
)
)

objects: models.Manager[ContainerEntityMixin] = ContainerEntityMixinManager()
# select these related entities by default for all queries
objects: ClassVar[WithRelationsManager[Self]] = WithRelationsManager("container_entity") # type: ignore[assignment]

container_entity = models.OneToOneField(
ContainerEntity,
Expand Down Expand Up @@ -68,18 +61,9 @@ class ContainerEntityVersionMixin(PublishableEntityVersionMixin):
If you use this class, you *MUST* also use ContainerEntityMixin
"""

class ContainerEntityVersionMixinManager(models.Manager):
def get_queryset(self) -> QuerySet:
return (
super()
.get_queryset()
.select_related(
"container_entity_version",
)
)

objects: models.Manager[ContainerEntityVersionMixin] = (
ContainerEntityVersionMixinManager()
# select these related entities by default for all queries
objects: ClassVar[WithRelationsManager[Self]] = WithRelationsManager( # type: ignore[assignment]
"container_entity_version",
)

container_entity_version = models.OneToOneField(
Expand Down
36 changes: 13 additions & 23 deletions openedx_learning/apps/authoring/publishing/model_mixins.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@
from __future__ import annotations

from functools import cached_property
from typing import ClassVar, Self

from django.core.exceptions import ImproperlyConfigured
from django.db import models
from django.db.models.query import QuerySet

from openedx_learning.lib.managers import WithRelationsManager

from .models import PublishableEntity, PublishableEntityVersion

Expand All @@ -28,17 +30,12 @@ class PublishableEntityMixin(models.Model):
the publishing app's api.register_content_models (see its docstring for
details).
"""

class PublishableEntityMixinManager(models.Manager):
def get_queryset(self) -> QuerySet:
return super().get_queryset() \
.select_related(
"publishable_entity",
"publishable_entity__published",
"publishable_entity__draft",
)

objects: models.Manager[PublishableEntityMixin] = PublishableEntityMixinManager()
# select these related entities by default for all queries
objects: ClassVar[WithRelationsManager[Self]] = WithRelationsManager(
"publishable_entity",
"publishable_entity__published",
"publishable_entity__draft",
)

publishable_entity = models.OneToOneField(
PublishableEntity, on_delete=models.CASCADE, primary_key=True
Expand Down Expand Up @@ -294,17 +291,10 @@ class PublishableEntityVersionMixin(models.Model):
details).
"""

class PublishableEntityVersionMixinManager(models.Manager):
def get_queryset(self) -> QuerySet:
return (
super()
.get_queryset()
.select_related(
"publishable_entity_version",
)
)

objects: models.Manager[PublishableEntityVersionMixin] = PublishableEntityVersionMixinManager()
# select these related entities by default for all queries
objects: ClassVar[WithRelationsManager[Self]] = WithRelationsManager(
"publishable_entity_version",
)

publishable_entity_version = models.OneToOneField(
PublishableEntityVersion, on_delete=models.CASCADE, primary_key=True
Expand Down
14 changes: 8 additions & 6 deletions openedx_learning/apps/authoring/units/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ def create_unit_version(
entity_version_pks: list[int | None],
created: datetime,
created_by: int | None = None,
) -> Unit:
) -> UnitVersion:
"""
[ 🛑 UNSTABLE ] Create a new unit version.
Expand Down Expand Up @@ -100,7 +100,7 @@ def create_next_unit_version(
components: list[Component | ComponentVersion],
created: datetime,
created_by: int | None = None,
) -> Unit:
) -> UnitVersion:
"""
[ 🛑 UNSTABLE ] Create the next unit version.
Expand Down Expand Up @@ -237,11 +237,13 @@ def get_components_in_draft_unit(

def get_components_in_published_unit(
unit: Unit,
) -> list[UnitListEntry]:
) -> list[UnitListEntry] | None:
"""
[ 🛑 UNSTABLE ]
Get the list of entities and their versions in the published version of the
given container.
Returns None if the unit was never published (TODO: should it throw instead?).
"""
assert isinstance(unit, Unit)
published_entities = container_api.get_entities_in_published_container(unit)
Expand Down Expand Up @@ -275,10 +277,10 @@ def get_components_in_published_unit_as_of(
ancestors of every modified PublishableEntity in the publish.
"""
assert isinstance(unit, Unit)
unit_pub_entity_version = get_published_version_as_of(unit.publishable_entity, publish_log_id)
unit_pub_entity_version = get_published_version_as_of(unit.publishable_entity_id, publish_log_id)
if unit_pub_entity_version is None:
return None # This unit was not published as of the given PublishLog ID.
unit_version = unit_pub_entity_version.unitversion
unit_version = unit_pub_entity_version.unitversion # type: ignore[attr-defined]

entity_list = []
rows = unit_version.container_entity_version.defined_list.entitylistrow_set.order_by("order_num")
Expand All @@ -290,7 +292,7 @@ def get_components_in_published_unit_as_of(
else:
# Unpinned component - figure out what its latest published version was.
# This is not optimized. It could be done in one query per unit rather than one query per component.
pub_entity_version = get_published_version_as_of(row.entity, publish_log_id)
pub_entity_version = get_published_version_as_of(row.entity_id, publish_log_id)
if pub_entity_version:
entity_list.append(UnitListEntry(component_version=pub_entity_version.componentversion, pinned=False))
return entity_list
7 changes: 6 additions & 1 deletion openedx_learning/lib/managers.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
"""
Custom Django ORM Managers.
"""
from __future__ import annotations
from typing import TypeVar

from django.db import models
from django.db.models.query import QuerySet

M = TypeVar('M', bound=models.Model)


class WithRelationsManager(models.Manager):
class WithRelationsManager(models.Manager[M]):
"""
Custom Manager that adds select_related to the default queryset.
Expand Down

0 comments on commit 4e1c2d7

Please sign in to comment.