Skip to content

Commit 7c4354a

Browse files
feat: typed PK identifiers for Units/Subsections/Sections
1 parent 421ba1f commit 7c4354a

File tree

9 files changed

+50
-24
lines changed

9 files changed

+50
-24
lines changed

src/openedx_content/applets/sections/api.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
from typing import Iterable
99

1010
from ..containers import api as containers_api
11-
from ..containers.models import Container, ContainerVersion
11+
from ..containers.models import ContainerVersion
1212
from ..publishing.models import LearningPackage
1313
from ..subsections.models import Subsection, SubsectionVersion
1414
from .models import Section, SectionVersion
@@ -24,7 +24,7 @@
2424
]
2525

2626

27-
def get_section(section_id: Container.PK, /):
27+
def get_section(section_id: Section.PK, /):
2828
"""Get a section"""
2929
return Section.objects.select_related("container").get(pk=section_id)
3030

@@ -61,7 +61,7 @@ def create_section_and_version(
6161

6262

6363
def create_next_section_version(
64-
section: Section | Container.PK,
64+
section: Section | Section.PK,
6565
*,
6666
title: str | None = None,
6767
subsections: Iterable[Subsection | SubsectionVersion] | None = None,

src/openedx_content/applets/sections/models.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,13 @@
22
Models that implement sections
33
"""
44

5-
from typing import override
5+
from typing import NewType, TypeAlias, cast, override
66

77
from django.core.exceptions import ValidationError
88
from django.db import models
99

1010
from ..containers.api import get_container_subclass_of
11-
from ..containers.models import Container, ContainerVersion
11+
from ..containers.models import Container, ContainerPK, ContainerVersion
1212
from ..publishing.models import PublishableEntity
1313
from ..subsections.models import Subsection
1414

@@ -17,6 +17,8 @@
1717
"SectionVersion",
1818
]
1919

20+
SectionPK = NewType("SectionPK", ContainerPK)
21+
2022

2123
@Container.register_subclass
2224
class Section(Container):
@@ -27,6 +29,8 @@ class Section(Container):
2729
entities and can be added to other containers.
2830
"""
2931

32+
PK: TypeAlias = SectionPK
33+
3034
type_code = "section"
3135
olx_tag_name = "chapter" # Serializes to OLX as `<chapter>...</chapter>`.
3236

@@ -37,6 +41,10 @@ class Section(Container):
3741
primary_key=True,
3842
)
3943

44+
@property
45+
def id(self) -> SectionPK:
46+
return cast(SectionPK, self.publishable_entity_id)
47+
4048
@override
4149
@classmethod
4250
def validate_entity(cls, entity: PublishableEntity) -> None:

src/openedx_content/applets/subsections/api.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
from typing import Iterable
99

1010
from ..containers import api as containers_api
11-
from ..containers.models import Container, ContainerVersion
11+
from ..containers.models import ContainerVersion
1212
from ..publishing.models import LearningPackage
1313
from ..units.models import Unit, UnitVersion
1414
from .models import Subsection, SubsectionVersion
@@ -24,7 +24,7 @@
2424
]
2525

2626

27-
def get_subsection(subsection_id: Container.PK, /):
27+
def get_subsection(subsection_id: Subsection.PK, /):
2828
"""Get a subsection"""
2929
return Subsection.objects.select_related("container").get(pk=subsection_id)
3030

@@ -61,7 +61,7 @@ def create_subsection_and_version(
6161

6262

6363
def create_next_subsection_version(
64-
subsection: Subsection | Container.PK,
64+
subsection: Subsection | Subsection.PK,
6565
*,
6666
title: str | None = None,
6767
units: Iterable[Unit | UnitVersion] | None = None,

src/openedx_content/applets/subsections/models.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,13 @@
22
Models that implement subsections
33
"""
44

5-
from typing import override
5+
from typing import NewType, TypeAlias, cast, override
66

77
from django.core.exceptions import ValidationError
88
from django.db import models
99

1010
from ..containers.api import get_container_subclass_of
11-
from ..containers.models import Container, ContainerVersion
11+
from ..containers.models import Container, ContainerPK, ContainerVersion
1212
from ..publishing.models import PublishableEntity
1313
from ..units.models import Unit
1414

@@ -17,6 +17,8 @@
1717
"SubsectionVersion",
1818
]
1919

20+
SubsectionPK = NewType("SubsectionPK", ContainerPK)
21+
2022

2123
@Container.register_subclass
2224
class Subsection(Container):
@@ -27,6 +29,8 @@ class Subsection(Container):
2729
entities and can be added to other containers.
2830
"""
2931

32+
PK: TypeAlias = SubsectionPK
33+
3034
type_code = "subsection"
3135
olx_tag_name = "sequential" # Serializes to OLX as `<sequential>...</sequential>`.
3236

@@ -37,6 +41,10 @@ class Subsection(Container):
3741
primary_key=True,
3842
)
3943

44+
@property
45+
def id(self) -> SubsectionPK:
46+
return cast(SubsectionPK, self.publishable_entity_id)
47+
4048
@override
4149
@classmethod
4250
def validate_entity(cls, entity: PublishableEntity) -> None:

src/openedx_content/applets/units/api.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010
from ..components.models import Component, ComponentVersion
1111
from ..containers import api as containers_api
12-
from ..containers.models import Container, ContainerVersion
12+
from ..containers.models import ContainerVersion
1313
from ..publishing.models import LearningPackage
1414
from .models import Unit, UnitVersion
1515

@@ -24,7 +24,7 @@
2424
]
2525

2626

27-
def get_unit(unit_id: Container.PK, /):
27+
def get_unit(unit_id: Unit.PK, /):
2828
"""Get a unit"""
2929
return Unit.objects.select_related("container").get(pk=unit_id)
3030

@@ -61,7 +61,7 @@ def create_unit_and_version(
6161

6262

6363
def create_next_unit_version(
64-
unit: Unit | Container.PK,
64+
unit: Unit | Unit.PK,
6565
*,
6666
title: str | None = None,
6767
components: Iterable[Component | ComponentVersion] | None = None,

src/openedx_content/applets/units/models.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,21 @@
22
Models that implement units
33
"""
44

5-
from typing import override
5+
from typing import NewType, TypeAlias, cast, override
66

77
from django.core.exceptions import ValidationError
88
from django.db import models
99

10-
from ..containers.models import Container, ContainerVersion
10+
from ..containers.models import Container, ContainerPK, ContainerVersion
1111
from ..publishing.models import PublishableEntity
1212

1313
__all__ = [
1414
"Unit",
1515
"UnitVersion",
1616
]
1717

18+
UnitPK = NewType("UnitPK", ContainerPK)
19+
1820

1921
@Container.register_subclass
2022
class Unit(Container):
@@ -25,6 +27,8 @@ class Unit(Container):
2527
entities and can be added to other containers.
2628
"""
2729

30+
PK: TypeAlias = UnitPK
31+
2832
type_code = "unit"
2933
olx_tag_name = "vertical" # Serializes to OLX as `<unit>...</unit>`.
3034

@@ -35,6 +39,10 @@ class Unit(Container):
3539
primary_key=True,
3640
)
3741

42+
@property
43+
def id(self) -> UnitPK:
44+
return cast(UnitPK, self.publishable_entity_id)
45+
3846
@override
3947
@classmethod
4048
def validate_entity(cls, entity: PublishableEntity) -> None:

tests/openedx_content/applets/sections/test_api.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
from django.core.exceptions import ValidationError
99

1010
import openedx_content.api as content_api
11-
from openedx_content.models_api import Container, Section, SectionVersion, Subsection, SubsectionVersion
11+
from openedx_content.models_api import Section, SectionVersion, Subsection, SubsectionVersion
1212

1313
from ..components.test_api import ComponentTestCase
1414

@@ -140,14 +140,14 @@ def test_get_section(self) -> None:
140140

141141
def test_get_section_nonexistent(self) -> None:
142142
"""Test `get_section()` when the subsection doesn't exist"""
143-
FAKE_ID = cast(Container.PK, -500)
143+
FAKE_ID = cast(Section.PK, -500)
144144
with pytest.raises(Section.DoesNotExist):
145145
content_api.get_section(FAKE_ID)
146146

147147
def test_get_section_other_container_type(self) -> None:
148148
"""Test `get_section()` when the provided PK is for a non-Subsection container"""
149149
with pytest.raises(Section.DoesNotExist):
150-
content_api.get_section(self.unit_1.id)
150+
content_api.get_section(self.unit_1.id) # type: ignore[arg-type]
151151

152152
def test_section_queries(self) -> None:
153153
"""

tests/openedx_content/applets/subsections/test_api.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
"""
22
Basic tests for the subsections API.
33
"""
4+
45
from typing import cast
56

67
import pytest
78
from django.core.exceptions import ValidationError
89

910
import openedx_content.api as content_api
10-
from openedx_content.models_api import Container, Subsection, SubsectionVersion, Unit, UnitVersion
11+
from openedx_content.models_api import Subsection, SubsectionVersion, Unit, UnitVersion
1112

1213
from ..components.test_api import ComponentTestCase
1314

@@ -117,14 +118,14 @@ def test_get_subsection(self) -> None:
117118

118119
def test_get_subsection_nonexistent(self) -> None:
119120
"""Test `get_subsection()` when the subsection doesn't exist"""
120-
FAKE_ID = cast(Container.PK, -500)
121+
FAKE_ID = cast(Subsection.PK, -500)
121122
with pytest.raises(Subsection.DoesNotExist):
122123
content_api.get_subsection(FAKE_ID)
123124

124125
def test_get_subsection_other_container_type(self) -> None:
125126
"""Test `get_subsection()` when the provided PK is for a non-Subsection container"""
126127
with pytest.raises(Subsection.DoesNotExist):
127-
content_api.get_subsection(self.unit_1.id)
128+
content_api.get_subsection(self.unit_1.id) # type: ignore[arg-type]
128129

129130
def test_subsection_queries(self) -> None:
130131
"""

tests/openedx_content/applets/units/test_api.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
"""
22
Basic tests for the units API.
33
"""
4+
45
from typing import cast
56

67
import pytest
78
from django.core.exceptions import ValidationError
89

910
import openedx_content.api as content_api
10-
from openedx_content.models_api import Component, ComponentVersion, Container, Unit, UnitVersion
11+
from openedx_content.models_api import Component, ComponentVersion, Unit, UnitVersion
1112
from tests.test_django_app.models import TestContainer
1213

1314
from ..components.test_api import ComponentTestCase
@@ -111,7 +112,7 @@ def test_get_unit(self) -> None:
111112

112113
def test_get_unit_nonexistent(self) -> None:
113114
"""Test `get_unit()` when the unit doesn't exist"""
114-
FAKE_ID = cast(Container.PK, -500)
115+
FAKE_ID = cast(Unit.PK, -500)
115116
with pytest.raises(Unit.DoesNotExist):
116117
content_api.get_unit(FAKE_ID)
117118

@@ -125,7 +126,7 @@ def test_get_unit_other_container_type(self) -> None:
125126
container_cls=TestContainer,
126127
)
127128
with pytest.raises(Unit.DoesNotExist):
128-
content_api.get_unit(other_container.id)
129+
content_api.get_unit(other_container.id) # type: ignore[arg-type]
129130

130131
def test_unit_queries(self) -> None:
131132
"""

0 commit comments

Comments
 (0)