DRF APIView submit request validation for submit method using request.data

I created a basic api view that extends from APIView , where I log response time, log request and other common materials.

Now I also want to add request validation here using the Serializer defined in the Views subclass. I thought a suitable place is to put the dispatch() method. But before I call the API.dispatch() method, request.data not prepared. So this will not work. Can someone help me in the right direction regarding how to transfer the check to one place?

Here is the class structure:

 class BaseView(APIView): validation_serializer = None def dispatch(self, request, *args, **kwargs): # Some code here # How to use `validation_serializer` here, to validate request data? # `request.data` is not available here. response = super(BaseView, self).dispatch(request, *args, **kwargs) # Some code here return response class MyView(BaseView): validation_serializer = ViewValidationSerializer def post(self, request, *args, **kwargs): pass 

I thought a different approach could be used by the decorator at the top of the post() method. But if there was only a cleaner way than creating decorators throughout the project?

Note. This is similar to the question here: Django - DRF is a stream of dispatch methods . But as suggested, I don't want to just copy the entire dispatch method from the DRF source code.

+5
source share
3 answers

The method that processes the django request in the DRF request (and adds the request.data property) is APIView.initialize_request . The APIView.dispatch() method calls it , and then proceeds to calling the appropriate method handler (post / patch / put).

You can try to do this yourself by calling it and using the returned object:

 class BaseView(APIView): validation_serializer = None def dispatch(self, request, *args, **kwargs): request = self.initialize_request(request, *args, **kwargs) kwargs['context'] = self.get_serializer_context() serializer = self.validation_serializer(data=request.data, *args, **kwargs) # use `raise_exception=True` to raise a ValidationError serializer.is_valid(raise_exception=True) response = super(BaseView, self).dispatch(request, *args, **kwargs) return response 

However, I would suggest against this, since other dispatch() functions should probably be executed before validation processing; so you could move the above logic to the appropriate post / patch / put methods.

In these methods, you can also use self.request directly, since it has already been initialized by dispatch() .

+1
source

I think drf-tracking does what you are looking for. You can check it out.

0
source

I don’t think you will get better. The best way to register a request is by checking in your authentication class and adding an audit log to the request.

You can then use APIView to register the rendering time, against the AuditLog generated in the authentication class.


Here's an example of using Token authentication if each request has an Authorization: Bearer <Token> header.

settings.py

 ... REST_FRAMEWORK = { 'DEFAULT_AUTHENTICATION_CLASSES': ( 'common.authentication.MyTokenAuthenticationClass' ), ..., } 

generic /authentication.py

 from django.utils import timezone from django.utils.translation import ugettext_lazy as _ from ipware.ip import get_real_ip from rest_framework import authentication from rest_framework import exceptions from accounts.models import Token, AuditLog class MyTokenAuthenticationClass(authentication.BaseAuthentication): def authenticate(self, request): # Grab the Athorization Header from the HTTP Request auth = authentication.get_authorization_header(request).split() if not auth or auth[0].lower() != b'bearer': return None # Check that Token header is properly formatted and present, raise errors if not if len(auth) == 1: msg = _('Invalid token header. No credentials provided.') raise exceptions.AuthenticationFailed(msg) elif len(auth) > 2: msg = _('Invalid token header. Credentials string should not contain spaces.') raise exceptions.AuthenticationFailed(msg) try: token = Token.objects.get(token=auth[1]) # Using the `ipware.ip` module to get the real IP (if hosted on ElasticBeanstalk or Heroku) token.last_ip = get_real_ip(request) token.last_login = timezone.now() token.save() # Add the saved token instance to the request context request.token = token except Token.DoesNotExist: raise exceptions.AuthenticationFailed('Invalid token.') # At this point, insert the Log into your AuditLog table and add to request: request.audit_log = AuditLog.objects.create( user_id=token.user, request_payload=request.body, # Additional fields ... ) # Return the Authenticated User associated with the Token return (token.user, token) 

You now have access to AuditLog in your request. Thus, you can register everything before and after verification.

0
source

Source: https://habr.com/ru/post/1260027/


All Articles