Coding

  • Django OAuth Toolkit: Allow access token expiration date per user

    In this tutorial, I will demonstrate how to implement a per-user access token expiration for Django OAuth Toolkit. Setup OAuth Toolkit Override OAUTH2_VALIDATOR_CLASS in settings.py OAUTH2_PROVIDER = { 'ACCESS_TOKEN_EXPIRE_SECONDS': 1800, # 30 minutes 'REFRESH_TOKEN_EXPIRE_SECONDS': 3600, # 1 hour 'OAUTH2_VALIDATOR_CLASS': 'py_app.validator.MyOAuth2Validator', }   Custom Validator Create the custom validator Create MyOAuth2Validator.py inside <app-root>/py_app/validator *Create validator folder if it doesn't exist yet   Override save_bearer_token method to check for our custom expiration field and use it if there's any. from oauth2_provider.oauth2_validators import OAuth2Validator from oauth2_provider.models import AccessToken class MyOAuth2Validator(OAuth2Validator): """ Primarily extend the functionality of token generation """ def save_bearer_token(self, token, request, *args, **kwargs): from datetime import datetime, timedelta super(MyOAuth2Validator, self).save_bearer_token(token, request, *args, **kwargs) ip = self.get_client_ip(request) accessToken = AccessToken.objects.get(token=token.get('access_token')) if accessToken.user.detail.session_expire_in is not None: accessToken.expires = datetime.now() + timedelta(seconds=accessToken.user.detail.session_expire_in) accessToken.save()   Custom field We first need to create a model that we can link to our user model. We can call it UserDetail. Create this class on a dedicated django app or on any existing app model. class UserDetail(models.Model): user = models.OneToOneField(USER_MODEL, related_name='detail', on_delete=models.CASCADE, null=True, blank=True) session_expire_in = models.IntegerField(default=None, null=True, blank=True)   And that's it.. Run the makemigrations, migrate and runserver.   Have fun!
  • drf

    Fix Django REST framework AttributeError: 'Request' object has no attribute 'accepted_renderer'

    Below are ways to fix AttributeError: 'Request' object has no attribute 'accepted_renderer' error.   1. Install pyyaml pip install pyyaml   OR   2. Revert Django REST Framework (DRF) pip install djangorestframework==3.8.0   OR   3. Ignore decode on your ViewSet class MyCoolViewset(viewsets.ModelViewSet): def _clean_data(self, data): if isinstance(data, bytes): data = data.decode(errors='ignore') return super(MyCoolViewset, self)._clean_data(data)  
  • Fix Django Rest Framework Tracking (drf-tracking) errors with Django model FileField

    I was working recently on logging Django Rest Framework (DRF) requests and responses and also creating a download endpoint so I can protect files from being accessed by other users with the ability log requests to see who's downloading what. So I came across this awesome DRF plugin called drf-tracking. It so easy to use that you just need to add rest_framework_tracking.mixins.LoggingMixin to your existing DRF views like the code below: from rest_framework import generics from rest_framework.response import Response from rest_framework_tracking.mixins import LoggingMixin class LoggingView(LoggingMixin, generics.GenericAPIView): def get(self, request): return Response('with logging') And you should see the logs in your django admin page. Upon testing, I noticed that I get an exception when I'm sending a POST request with a file and also have the decoding error on my download api. So I have to override the mixin methods. from rest_framework import viewsets, renderers from rest_framework.response import Response from rest_framework.decorators import detail_route from django.http import FileResponse, HttpResponse from rest_framework_tracking.mixins import LoggingMixin from .models import File class BinaryFileRenderer(renderers.BaseRenderer): media_type = 'application/octet-stream' format = None charset = None render_style = 'binary' def render(self, data, media_type=None, renderer_context=None): return data class FileViewSet(LoggingMixin, viewsets.ModelViewSet): queryset = File.objects.all() # Override finalize_response to check if response is FileResponse then I will return the rendered_content response without # processing it to avoid decode exceptions def finalize_response(self, request, response, *args, **kwargs): if response.__class__.__name__ == 'FileResponse': mixinResponse = super(FileViewSet, self).finalize_response(request, response, *args, **kwargs) if hasattr(response, 'rendered_content'): rendered_content = response.rendered_content else: rendered_content = response.getvalue() return HttpResponse(rendered_content) return super(FileViewSet, self).finalize_response(request, response, *args, **kwargs) # Override _clean_data to decode data with ignore instead of replace to ignore # errors in decode so when the logger inserts the data to db, it will not hit # any decoding/encoding issues def _clean_data(self, data): if isinstance(data, bytes): data = data.decode(errors='ignore') return super(FileViewSet, self)._clean_data(data) @detail_route(methods=['get'], renderer_classes=(BinaryFileRenderer,)) def download(self, request, pk=None): queryset = File.objects.get(id=pk) documentFile = queryset.document file_handle = documentFile.open() # send file response = FileResponse(file_handle) return response  
  • django rest framework

    Saving foreign key ID with Django REST framework serializer

    If you are using Django REST framework on serving your APIs, you probably did the below code in returning the related object in your serializer. class MyTableSerializer(serializers.ModelSerializer): user = UserSerializer(many=False, read_only=True) class Meta: fields = '__all__' model = MyTable But doing this will not allow your API to pass the foreign key id. Instead, you need to include the field name in the serializer like the code below: class MyTableSerializer(serializers.ModelSerializer): user = UserSerializer(many=False, read_only=True) user_id = serializers.IntegerField(write_only=True) class Meta: fields = '__all__' model = MyTable  
  • playstation network

    Display Playstation Network (PSN) Public Trophies on your Drupal 8 site

    I've been recently searching for a Drupal 8 module that can integrate and display my Playstation profile but I can't find one. So, I created one. Feel free to use it on your own Drupal 8 sites. Here's how: Manual Download Download the Drupal 8 module https://www.drupal.org/project/psn_public_trophies and put inside your project's module folder. Run  composer require vhin0210/php-psnpublictrophies from your project's root folder. Download using Composer Run  composer require drupal/psn_public_trophies from your project's root folder. Install the module by either: Using the admin page Drush command  drush en psn_public_trophies   Connect to PSN NOTE: Make sure your PSN account 2-Step Verification is enabled. Go to Configuration > PSN Public Trophies Settings Enter your PSN ID and Password and hit "Save Configuration" Get Ticket UUID and Verification Code Click the Get Ticket UUID link - This will open the PSN login form in a new tab. Login on the form. When successful login, it will redirect you to enter the verification code sent by SMS. Do not enter the verification code in this PSN form. Copy the whole URL of the verify form/page and paste it in the "Ticket UUID URL" field. Enter the verification code that you get in the "Verification Code" field. Click the "Connect" button Add the Block Look for "PSN Public Trophies Profile Block".  
  • drupal 8

    I'm now running in Drupal 8!

    After years of waiting and contemplating, I finally jumped in.  I still think Drupal 8 is lacking a lot of things but I think we are almost there. I even have created some simple modules for Drupal 8 to play around with it. Hopefully more and more modules will become more stable especially the important ones. I spent a lot of my time using Drupal 7 and I still think that Drupal 7 is still a good CMS but I think it's time to move on to the new one. I really like that now we can run Drupal with Composer and of course, it's running on top of Symfony framework. There's a huge learning curve but I guess it will pay off. I hope to have a good relationship with Drupal 8 just like what I had with Drupal 7. ?