Custom Permissions in django-ninja which needs to use existing db objects

I am using django-ninja and django-ninja-extra for an api.

Currently I have some Schema like so

from ninja import schema
class SchemaA(Schema)
   fruit_id: int
   other_data: str

and a controller like so

class HasFruitAccess(permissions.BasePermission):
    def has_permission(self, request: HttpRequest, controller: ControllerBase):
        controller.context.compute_route_parameters()
        data = controller.context.kwargs.get('data')
        fruit = Fruit.objects.get(pk=data.fruit_id)
        if fruit.user.pk == request.user.pk:
           return True
        return False

@api_controller("/fruits", permissions=[IsAuthenticated])
class FruitController(ControllerBase):
    """Controller class for test runs."""

    @route.post("/", auth=JWTAuth(), response=str, permissions=[HasFruitAccess()])
    def do_fruity_labour(self, data: SchemaA) -> str:
       #Check fruit exists.
       fruit = get_object_or_404(Fruit, data.fruit_id)
       #do work
       return "abc"

And a model like

class Fruit(models.Model):
   user = models.ForeignKey(User)
   ...

What I wanted to do here was check the user is related to the fruit and then we authorize them to do whatever on this object. Is this a good idea, is this best practice or is it better to just validate in the api route itself? Because permissions will obviously run before we check if fruit is even a valid object in the db so I might be trying to "authorize" a user with invalid data. How can one go about authorizing users for a specific api route which relies on db models through permissions (I would prefer it if I could use permissions since I can reuse it for multiple routes or even controllers easily) or maybe this approach isn't what should be done?

Was going off https://eadwincode.github.io/django-ninja-extra/api_controller/api_controller_permission/#basic-route-context-usage

You’re mixing validation and authorization. Permissions in Ninja run before schema validation, so data.fruit_id isn’t guaranteed to exist or be valid yet, that’s why this pattern fails.

If you want to keep reusability, you’ve got two clean options:

  1. Do the DB check inside the route, after validation, safer, avoids wasted queries on bad input.

  2. Or write a decorator or mixin that runs after request parsing, not as a permission class.

In short: using DB objects inside has_permission() isn’t best practice. Permissions should only check request/user context, not depend on unvalidated input.

Вернуться на верх