Add GET /api/club/{club_id} to fetch details about a club

This commit is contained in:
imperosol 2025-05-20 18:17:48 +02:00
parent b2c6b4f747
commit 6f550c605c
6 changed files with 55 additions and 11 deletions

View File

@ -1,13 +1,15 @@
from typing import Annotated from typing import Annotated
from annotated_types import MinLen from annotated_types import MinLen
from ninja.security import SessionAuth
from ninja_extra import ControllerBase, api_controller, paginate, route from ninja_extra import ControllerBase, api_controller, paginate, route
from ninja_extra.pagination import PageNumberPaginationExtra from ninja_extra.pagination import PageNumberPaginationExtra
from ninja_extra.schemas import PaginatedResponseSchema from ninja_extra.schemas import PaginatedResponseSchema
from apikey.auth import ApiKeyAuth
from club.models import Club from club.models import Club
from club.schemas import ClubSchema from club.schemas import ClubSchema
from core.auth.api_permissions import CanAccessLookup from core.auth.api_permissions import CanAccessLookup, HasPerm
@api_controller("/club") @api_controller("/club")
@ -20,3 +22,14 @@ class ClubController(ControllerBase):
@paginate(PageNumberPaginationExtra, page_size=50) @paginate(PageNumberPaginationExtra, page_size=50)
def search_club(self, search: Annotated[str, MinLen(1)]): def search_club(self, search: Annotated[str, MinLen(1)]):
return Club.objects.filter(name__icontains=search).values() 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
)

View File

@ -1,9 +1,10 @@
from ninja import ModelSchema 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: class Meta:
model = Club model = Club
fields = ["id", "name"] fields = ["id", "name"]
@ -21,3 +22,19 @@ class ClubProfileSchema(ModelSchema):
@staticmethod @staticmethod
def resolve_url(obj: Club) -> str: def resolve_url(obj: Club) -> str:
return obj.get_absolute_url() 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]

View File

@ -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

View File

@ -1,7 +1,7 @@
from pydantic import TypeAdapter from pydantic import TypeAdapter
from club.models import Club from club.models import Club
from club.schemas import ClubSchema from club.schemas import SimpleClubSchema
from core.views.widgets.ajax_select import ( from core.views.widgets.ajax_select import (
AutoCompleteSelect, AutoCompleteSelect,
AutoCompleteSelectMultiple, AutoCompleteSelectMultiple,
@ -13,7 +13,7 @@ _js = ["bundled/club/components/ajax-select-index.ts"]
class AutoCompleteSelectClub(AutoCompleteSelect): class AutoCompleteSelectClub(AutoCompleteSelect):
component_name = "club-ajax-select" component_name = "club-ajax-select"
model = Club model = Club
adapter = TypeAdapter(list[ClubSchema]) adapter = TypeAdapter(list[SimpleClubSchema])
js = _js js = _js
@ -21,6 +21,6 @@ class AutoCompleteSelectClub(AutoCompleteSelect):
class AutoCompleteSelectMultipleClub(AutoCompleteSelectMultiple): class AutoCompleteSelectMultipleClub(AutoCompleteSelectMultiple):
component_name = "club-ajax-select" component_name = "club-ajax-select"
model = Club model = Club
adapter = TypeAdapter(list[ClubSchema]) adapter = TypeAdapter(list[SimpleClubSchema])
js = _js js = _js

View File

@ -5,7 +5,7 @@ from django.urls import reverse
from ninja import Field, FilterSchema, ModelSchema, Schema from ninja import Field, FilterSchema, ModelSchema, Schema
from pydantic import model_validator from pydantic import model_validator
from club.schemas import ClubSchema from club.schemas import SimpleClubSchema
from core.schemas import GroupSchema, SimpleUserSchema from core.schemas import GroupSchema, SimpleUserSchema
from counter.models import Counter, Product, ProductType from counter.models import Counter, Product, ProductType
@ -82,7 +82,7 @@ class ProductSchema(ModelSchema):
] ]
buying_groups: list[GroupSchema] buying_groups: list[GroupSchema]
club: ClubSchema club: SimpleClubSchema
product_type: SimpleProductTypeSchema | None product_type: SimpleProductTypeSchema | None
url: str url: str

View File

@ -12,7 +12,6 @@
# OR WITHIN THE LOCAL FILE "LICENSE" # OR WITHIN THE LOCAL FILE "LICENSE"
# #
# #
from django.conf import settings from django.conf import settings
from django.conf.urls.static import static from django.conf.urls.static import static
from django.contrib import admin from django.contrib import admin
@ -26,8 +25,7 @@ js_info_dict = {"packages": ("sith",)}
handler403 = "core.views.forbidden" handler403 = "core.views.forbidden"
handler404 = "core.views.not_found" handler404 = "core.views.not_found"
handler500 = "core.views.internal_servor_error" handler500 = "core.views.internal_servor_error"
api = NinjaExtraAPI(title="Sith API", version="0.2.0", urls_namespace="api", csrf=True)
api = NinjaExtraAPI(version="0.2.0", urls_namespace="api", csrf=True)
api.auto_discover_controllers() api.auto_discover_controllers()
urlpatterns = [ urlpatterns = [