diff --git a/club/api.py b/club/api.py index 2ad0f5c8..147f6379 100644 --- a/club/api.py +++ b/club/api.py @@ -1,13 +1,15 @@ from typing import Annotated from annotated_types import MinLen +from ninja.security import SessionAuth from ninja_extra import ControllerBase, api_controller, paginate, route from ninja_extra.pagination import PageNumberPaginationExtra from ninja_extra.schemas import PaginatedResponseSchema +from apikey.auth import ApiKeyAuth from club.models import Club from club.schemas import ClubSchema -from core.auth.api_permissions import CanAccessLookup +from core.auth.api_permissions import CanAccessLookup, HasPerm @api_controller("/club") @@ -20,3 +22,14 @@ class ClubController(ControllerBase): @paginate(PageNumberPaginationExtra, page_size=50) def search_club(self, search: Annotated[str, MinLen(1)]): return Club.objects.filter(name__icontains=search).values() + + @route.get( + "/{int:club_id}", + response=ClubSchema, + auth=[SessionAuth(), ApiKeyAuth()], + permissions=[HasPerm("club.view_club")], + ) + def fetch_club(self, club_id: int): + return self.get_object_or_exception( + Club.objects.prefetch_related("members", "members__user"), id=club_id + ) diff --git a/club/schemas.py b/club/schemas.py index 7969f119..b0601af8 100644 --- a/club/schemas.py +++ b/club/schemas.py @@ -1,9 +1,10 @@ from ninja import ModelSchema -from club.models import Club +from club.models import Club, Membership +from core.schemas import SimpleUserSchema -class ClubSchema(ModelSchema): +class SimpleClubSchema(ModelSchema): class Meta: model = Club fields = ["id", "name"] @@ -21,3 +22,19 @@ class ClubProfileSchema(ModelSchema): @staticmethod def resolve_url(obj: Club) -> str: return obj.get_absolute_url() + + +class ClubMemberSchema(ModelSchema): + class Meta: + model = Membership + fields = ["start_date", "end_date", "role", "description"] + + user: SimpleUserSchema + + +class ClubSchema(ModelSchema): + class Meta: + model = Club + fields = ["id", "name", "logo", "is_active", "short_description", "address"] + + members: list[ClubMemberSchema] diff --git a/club/tests/test_club_controller.py b/club/tests/test_club_controller.py new file mode 100644 index 00000000..e48a4513 --- /dev/null +++ b/club/tests/test_club_controller.py @@ -0,0 +1,16 @@ +import pytest +from model_bakery import baker +from ninja_extra.testing import TestClient +from pytest_django.asserts import assertNumQueries + +from club.api import ClubController +from club.models import Club, Membership + + +@pytest.mark.django_db +def test_fetch_club(): + club = baker.make(Club) + baker.make(Membership, club=club, _quantity=10, _bulk_create=True) + with assertNumQueries(3): + res = TestClient(ClubController).get(f"/{club.id}") + assert res.status_code == 200 diff --git a/club/widgets/ajax_select.py b/club/widgets/ajax_select.py index 36ad3e9a..ddcc820f 100644 --- a/club/widgets/ajax_select.py +++ b/club/widgets/ajax_select.py @@ -1,7 +1,7 @@ from pydantic import TypeAdapter from club.models import Club -from club.schemas import ClubSchema +from club.schemas import SimpleClubSchema from core.views.widgets.ajax_select import ( AutoCompleteSelect, AutoCompleteSelectMultiple, @@ -13,7 +13,7 @@ _js = ["bundled/club/components/ajax-select-index.ts"] class AutoCompleteSelectClub(AutoCompleteSelect): component_name = "club-ajax-select" model = Club - adapter = TypeAdapter(list[ClubSchema]) + adapter = TypeAdapter(list[SimpleClubSchema]) js = _js @@ -21,6 +21,6 @@ class AutoCompleteSelectClub(AutoCompleteSelect): class AutoCompleteSelectMultipleClub(AutoCompleteSelectMultiple): component_name = "club-ajax-select" model = Club - adapter = TypeAdapter(list[ClubSchema]) + adapter = TypeAdapter(list[SimpleClubSchema]) js = _js diff --git a/counter/schemas.py b/counter/schemas.py index 978422a5..6ee9f8b1 100644 --- a/counter/schemas.py +++ b/counter/schemas.py @@ -5,7 +5,7 @@ from django.urls import reverse from ninja import Field, FilterSchema, ModelSchema, Schema from pydantic import model_validator -from club.schemas import ClubSchema +from club.schemas import SimpleClubSchema from core.schemas import GroupSchema, SimpleUserSchema from counter.models import Counter, Product, ProductType @@ -82,7 +82,7 @@ class ProductSchema(ModelSchema): ] buying_groups: list[GroupSchema] - club: ClubSchema + club: SimpleClubSchema product_type: SimpleProductTypeSchema | None url: str diff --git a/sith/urls.py b/sith/urls.py index 98608e14..fb3643d9 100644 --- a/sith/urls.py +++ b/sith/urls.py @@ -12,7 +12,6 @@ # OR WITHIN THE LOCAL FILE "LICENSE" # # - from django.conf import settings from django.conf.urls.static import static from django.contrib import admin @@ -26,8 +25,7 @@ js_info_dict = {"packages": ("sith",)} handler403 = "core.views.forbidden" handler404 = "core.views.not_found" handler500 = "core.views.internal_servor_error" - -api = NinjaExtraAPI(version="0.2.0", urls_namespace="api", csrf=True) +api = NinjaExtraAPI(title="Sith API", version="0.2.0", urls_namespace="api", csrf=True) api.auto_discover_controllers() urlpatterns = [