Django
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!0Read moreFix 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)Error pip install mysqlclient on MacOSX
If you have the similar issue as below ERROR: Command errored out with exit status 1: command: /.../bin/python -u -c 'import sys, setuptools, tokenize; sys.argv[0] = '"'"'/private/var/folders/53/l74q51892zjgsql64hkvhw180000gn/T/pip-install-e_x8dx0d/mysqlclient/setup.py'"'"'; __file__='"'"'/private/var/folders/53/l74q51892zjgsql64hkvhw180000gn/T/pip-install-e_x8dx0d/mysqlclient/setup.py'"'"';f=getattr(tokenize, '"'"'open'"'"', open)(__file__);code=f.read().replace('"'"'\r\n'"'"', '"'"'\n'"'"');f.close();exec(compile(code, __file__, '"'"'exec'"'"'))' bdist_wheel -d /private/var/folders/53/l74q51892zjgsql64hkvhw180000gn/T/pip-wheel-8pw_7779 cwd: /private/var/folders/53/l74q51892zjgsql64hkvhw180000gn/T/pip-install-e_x8dx0d/mysqlclient/ Complete output (30 lines): running bdist_wheel running build running build_py creating build creating build/lib.macosx-10.9-x86_64-3.7 creating build/lib.macosx-10.9-x86_64-3.7/MySQLdb copying MySQLdb/__init__.py -> build/lib.macosx-10.9-x86_64-3.7/MySQLdb copying MySQLdb/_exceptions.py -> build/lib.macosx-10.9-x86_64-3.7/MySQLdb copying MySQLdb/compat.py -> build/lib.macosx-10.9-x86_64-3.7/MySQLdb copying MySQLdb/connections.py -> build/lib.macosx-10.9-x86_64-3.7/MySQLdb copying MySQLdb/converters.py -> build/lib.macosx-10.9-x86_64-3.7/MySQLdb copying MySQLdb/cursors.py -> build/lib.macosx-10.9-x86_64-3.7/MySQLdb copying MySQLdb/release.py -> build/lib.macosx-10.9-x86_64-3.7/MySQLdb copying MySQLdb/times.py -> build/lib.macosx-10.9-x86_64-3.7/MySQLdb creating build/lib.macosx-10.9-x86_64-3.7/MySQLdb/constants copying MySQLdb/constants/__init__.py -> build/lib.macosx-10.9-x86_64-3.7/MySQLdb/constants copying MySQLdb/constants/CLIENT.py -> build/lib.macosx-10.9-x86_64-3.7/MySQLdb/constants copying MySQLdb/constants/CR.py -> build/lib.macosx-10.9-x86_64-3.7/MySQLdb/constants copying MySQLdb/constants/ER.py -> build/lib.macosx-10.9-x86_64-3.7/MySQLdb/constants copying MySQLdb/constants/FIELD_TYPE.py -> build/lib.macosx-10.9-x86_64-3.7/MySQLdb/constants copying MySQLdb/constants/FLAG.py -> build/lib.macosx-10.9-x86_64-3.7/MySQLdb/constants running build_ext building 'MySQLdb._mysql' extension creating build/temp.macosx-10.9-x86_64-3.7 creating build/temp.macosx-10.9-x86_64-3.7/MySQLdb gcc -Wno-unused-result -Wsign-compare -Wunreachable-code -fno-common -dynamic -DNDEBUG -g -fwrapv -O3 -Wall -arch x86_64 -g -Dversion_info=(1,4,2,'post',1) -D__version__=1.4.2.post1 -I/usr/local/Cellar/mysql/8.0.19/include/mysql -I/Library/Frameworks/Python.framework/Versions/3.7/include/python3.7m -c MySQLdb/_mysql.c -o build/temp.macosx-10.9-x86_64-3.7/MySQLdb/_mysql.o gcc -bundle -undefined dynamic_lookup -arch x86_64 -g build/temp.macosx-10.9-x86_64-3.7/MySQLdb/_mysql.o -L/usr/local/Cellar/mysql/8.0.19/lib -lmysqlclient -lssl -lcrypto -o build/lib.macosx-10.9-x86_64-3.7/MySQLdb/_mysql.cpython-37m-darwin.so ld: library not found for -lssl clang: error: linker command failed with exit code 1 (use -v to see invocation) error: command 'gcc' failed with exit status 1 ---------------------------------------- ERROR: Failed building wheel for mysqlclient The easiest and fastest solution to try is: Update your ~/.bash_profile or ~/.bashrc and add this line: export PATH="/usr/local/opt/openssl/bin:$PATH" Reload ~/.bash_profile or ~/.bashrc source ~/.bash_profileHow to upgrade all Python packages using pip
There's no built-in command or parameter yet, but you can use: pip list --outdated --format=freeze | grep -v '^\-e' | cut -d = -f 1 | xargs -n1 pip install -U In older version of pip, you can use: pip freeze --local | grep -v '^\-e' | cut -d = -f 1 | xargs -n1 pip install -U There are infinite potential variations for this. I'm trying to keep this answer short and simple, but please suggest variations in the comments!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 response0Read moreSaving 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