diff --git a/bananas/admin/api/schemas/decorators.py b/bananas/admin/api/schemas/decorators.py new file mode 100644 index 0000000..bf20788 --- /dev/null +++ b/bananas/admin/api/schemas/decorators.py @@ -0,0 +1,19 @@ +from functools import wraps + +def navigation(view): + @wraps(view) + def wrapper(*args, **kwargs): + return view(*args, **kwargs) + wrapper.navigation = True + return wrapper + + +def tags(include=None, exclude=None): + def wrapped(view): + @wraps(view) + def wrapper(*args, **kwargs): + return view(*args, **kwargs) + wrapper.include_tags = include + wrapper.exclude_tags = exclude + return wrapper + return wrapped diff --git a/bananas/admin/api/schemas/yasg.py b/bananas/admin/api/schemas/yasg.py index 2c29f7d..fb23c5a 100644 --- a/bananas/admin/api/schemas/yasg.py +++ b/bananas/admin/api/schemas/yasg.py @@ -97,13 +97,20 @@ def get_summary(self): def get_tags(self, operation_keys): view = self.view meta = self.view.get_admin_meta() - tags = ["app:{label}".format(label=meta.app_label)] + tags = {"app:{label}".format(label=meta.app_label)} if self.is_navigation(): - tags.append("navigation") + tags.add("navigation") if issubclass(view.__class__, viewsets.ModelViewSet): - tags.append("crud") + tags.add("crud") + + view_method = getattr(view, view.action, None) + if view_method: + include_tags = set(getattr(view_method, "include_tags", None) or []) + exclude_tags = set(getattr(view_method, "exclude_tags", None) or []) + tags |= include_tags + tags -= exclude_tags return [tag for tag in tags if tag not in meta.exclude_tags] @@ -111,10 +118,14 @@ def is_navigation(self): if not hasattr(self, "_is_navigation"): self._is_navigation = False try: + view_method = getattr(self.view, self.view.action, None) + is_custom_list_route = is_custom_action(self.view.action) and view_method and not view_method.detail + allows_navigation = getattr(view_method, "navigation", False) + url_name = view_method.url_name if is_custom_list_route else self.view.action if self.method == "GET" and ( - self.view.action == "list" or not hasattr(self.view, "list") + self.view.action == "list" or not hasattr(self.view, "list") or (is_custom_list_route and allows_navigation) ): - self.view.reverse_action("list") + self.view.reverse_action(url_name) self._is_navigation = True except NoReverseMatch: pass diff --git a/example/example/api.py b/example/example/api.py index e8f7bd0..72afc0d 100644 --- a/example/example/api.py +++ b/example/example/api.py @@ -9,6 +9,7 @@ from bananas.admin.api.views import BananasAPI from bananas.compat import reverse from bananas.lazy import lazy_title +from bananas.admin.api.schemas.decorators import tags class UserDetailsSerializer(serializers.HyperlinkedModelSerializer): @@ -136,8 +137,20 @@ class AppleViewSet(BananasAPI, viewsets.ModelViewSet): name = lazy_title(_("apple")) + #@tags(exclude=["navigation"]) def list(self, request): return Response() + + @tags(["navigation"]) + @action(detail=False) + def red_delicious(self, request): + return Response() + + @tags(["navigation"]) + @action(detail=False) + def granny_smith(self, request): + return Response() + class Admin: app_label = "fruit"