Should authentication_classes and permission_classes in Django REST Framework Views Be Defined with Lists or Tuples?
I'm trying to understand the best practice for setting authentication_classes and permission_classes in Django REST Framework's APIView. Specifically, I’ve seen both tuples and lists being used to define these attributes:
Using a tuple:
class Home(APIView):
authentication_classes = (JWTAuthentication,)
permission_classes = (permissions.IsAuthenticated,)
Using a list:
class Home(APIView):
authentication_classes = [JWTAuthentication]
permission_classes = [permissions.IsAuthenticated]
Both approaches seem to work correctly, but I'm unsure if there are any specific reasons to prefer one over the other. Should I use a list or a tuple in this scenario? Are there any implications for using one over the other in terms of performance, readability, or following Django REST Framework's best practices?
I tried using both tuples and lists for authentication_classes and permission_classes in Django REST Framework’s APIView. Both work fine, but I’m unsure which one is better or recommended. I was expecting to find clear guidance on this.
Both work equivalently. The Django REST Framework documentation seems to prefer lists, so maybe go with lists.
class ListUsers(APIView):
authentication_classes = [authentication.TokenAuthentication]
permission_classes = [permissions.IsAdminUser]
First, I will explain the process of performing authentication in DRF.
class APIView(View):
def initialize_request(self, request, *args, **kwargs):
"""
Returns the initial request object.
"""
parser_context = self.get_parser_context(request)
return Request(
request,
parsers=self.get_parsers(),
authenticators=self.get_authenticators(),
negotiator=self.get_content_negotiator(),
parser_context=parser_context
)
The function that actually performs authentication in DRF is the perform_authentication function, but before that, when the initialize_request method creates the request object, it calls the get_authenticators method and passes it to the argument.
class APIView(View):
...
def get_authenticators(self):
"""
Instantiates and returns the list of authenticators that this view can use.
"""
return [auth() for auth in self.authentication_classes]
...
Here, the get_authenticators function repeats the authentication_classes variable defined in View created by users to create and return each authentication class object.
In this process, the authentication_classes variable defined by the questioner is used.
And as you can see from the code above, it should be a repeatable object because it is used in the for statement. That's why arrangements and tuples are allowed.
The permission check is the same.
class APIView(View):
def dispatch(self, request, *args, **kwargs):
...
try:
self.initial(request, *args, **kwargs)
def initial(self, request, *args, **kwargs):
...
self.check_permissions(request)
...
def check_permissions(self, request):
for permission in self.get_permissions():
if not permission.has_permission(request, self):
...
def get_permissions(self):
"""
Instantiates and returns the list of permissions that this view requires.
"""
return [permission() for permission in self.permission_classes]
The process of permissions check in DRF APIView is as above.
The permissions_classes value set by the user is used in the get_permissions method and performs the same authentication process as previously seen.
That is, because permissions_classes or authentication_classes have to be used in repetitions, this is why you have to use an array or a tuple of these troublesome data types.
The reason why DRF has implemented the above logic for permissions and authentication is to allow the use of multiple authentication and permissions as well.
Straight to the point here: both tuple
s and list
s work. Basically, if you want to change the variable, then use lists. If you specifically do not want to and do not want anyone to change the variable, then use tuples. It's just the immutability vs mutability problem here.