[PYTHON] Miscellaneous notes about the Django REST framework

http://www.django-rest-framework.org/

About authentication process using token

I wanted to issue a token when I first authenticated with a user ID and password, and then include that token in the request, so I implemented it as follows (use Django's User table as it is) ..

New registration process of user information

When registering user information in the User table, only the password is hashed using the Django library and registered. https://docs.djangoproject.com/en/1.8/_modules/django/contrib/auth/hashers/#make_password

serializers.py


class UserSerializer(serializers.HyperlinkedModelSerializer):
    class Meta:
        model = User
        fields = ('id', 'username', 'password')
        write_only_fields = ('password')
        read_only_fields = ('id')
        
        def create(self, validated_data):
          """
Register after hashing password(django use default library)
          """
          password = validated_data.get('password')
          validated_data['password'] = make_password(password)
          return User.objects.create(**validated_data)
        
        # ......

Token generation

I used the TokenAuthentication function of Django REST framework as it is. http://www.django-rest-framework.org/api-guide/authentication/

settings.py


INSTALLED_APPS = (
    # .......
    'rest_framework.authtoken',
)

Add a process to create a Token table

models.py


@receiver(post_save, sender=settings.AUTH_USER_MODEL)
def create_auth_token(sender, instance=None, created=False, **kwargs):
    """
When a new user is created, TOKEN is automatically issued.
    """
    if created:
        Token.objects.create(user=instance)

With the above settings, when a user is registered in the User table, a token will be issued and stored in the Token table.

Issuance of tokens

Assign obtain_auth_token to any URI and create an endpoint for the client to get the token.

urls.py


from rest_framework.authtoken import views as auth_views
urlpatterns = patterns('',
    url(r'^api-token-auth/', auth_views.obtain_auth_token),
)

If you POST the json file containing username and password as shown below, the token corresponding to username will be returned.

$ curl -v -H "Accept: application/json" -H "Content-type: application/json" -X POST -d '{"username":"test","password":"111111"}' https://127.0.0.1:8000/api/api-token-auth/

When making a request from the client to a URI that requires a token, it is OK if the token is packed in the Authorization of Http Header as shown below.

$ curl -X GET http://127.0.0.1:8000/api/example/ -H 'Authorization: Token 9944b09199c62bcf9418ad846dd0e4bbdfc6ee4b'

Authentication process using token

If it is a normal authentication process, if you add the following settings to settings.py, it will check the contents of the token and perform the authentication process when a request comes.

settings.py


REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'rest_framework.authentication.TokenAuthentication',
    )
}

However, if this setting is made, token authentication will be performed for all URI requests by default, so if you want to change the authentication method for some URIs, use your own Authentication as shown in the example below. You can define a class, write the authentication process in it, and specify that class in the view class.

By the way, note that the Http Header field name specified on the client is automatically prefixed with "HTTP_". It looks like Django's specifications.

authentications.py


class FooAuthentication(authentication.BaseAuthentication):
    def authenticate(self, request):
        #Evaluate the default fixed token during POST
        if request.method == 'POST':
            default_token = request.META.get('HTTP_DEFAULT_TOKEN')
            try:
                token = System.objects.get(key='HTTP_DEFAULT_TOKEN')
            except Token.DoesNotExist:
                raise exceptions.AuthenticationFailed('error')

            if default_token != token.value:
                raise exceptions.AuthenticationFailed('error')

            return None
        #If you can do something other than POST, evaluate the authentication token issued for each registered value.
        else:
            auth_token = request.META.get('HTTP_AUTHORIZATION')
            if not auth_token:
                raise exceptions.AuthenticationFailed('Authentication token is none')
            try:
                user = Token.objects.get(key=auth_token.replace('Token ', ''))
            except Token.DoesNotExist:
                raise exceptions.AuthenticationFailed('error')

            return (user.user, None)

view.py


class FooViewSet(viewsets.ModelViewSet):
    queryset = Foo.objects.none()
    serializer_class = FooSerializer
    authentication_classes = (FooAuthentication, )
    
    # .......

On the contrary, if you want to authenticate only with some URIs, you should specify TokenAuthentication in authentication_classes of view class.

About the test code

The test code used the REST framework API Client. http://www.django-rest-framework.org/api-guide/testing/ Like this.

tests.py


class UserTests(APITestCase):
    def setUp(self):
        """
        setUp for testing
        """
        User.objects.create(username='user1', password='user1')
        User.objects.create(username='user2', password='user2')
        self.user1 = User.objects.get(username='user1')
        self.user2 = User.objects.get(username='user2')

    def test_user_list_normal1(self):
        """
        user-list: normal pattern
        """
        url = reverse('user-list')
        expected_data = {
            "count": 1,
            "next": None,
            "previous": None,
            "results": [{
                "id": 1,
                "username": "user1"
            }]
        }
        token = Token.objects.get(user=self.user1).key
        #Set Token for Authorization
        self.client.credentials(HTTP_AUTHORIZATION='Token ' + token)
        response = self.client.get(url, None, format='json')
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        #Confirm that json is returned as expected
        self.assertEqual(response.data, expected_data)

Recommended Posts

Miscellaneous notes about the Django REST framework
Miscellaneous notes about deploying the django app on Heroku
Understand the benefits of the Django Rest Framework
Django REST framework basics
Django Rest Framework Tips
Create a Todo app with the Django REST framework
Learning notes for the migrations feature in the Django framework (3)
[Django Rest Framework] Customize the filter function using Django-Filter
Learning notes for the migrations feature in the Django framework (1)
Django REST framework stumbling block
Django REST framework with Vue.js
Login with django rest framework
How to write custom validations in the Django REST Framework
[Django] Use MessagePack with Django REST framework
django notes
Create a REST API to operate dynamodb with the Django REST Framework
Django notes
Create RESTful APIs with Django Rest Framework
Logical deletion in Django, DRF (Django REST Framework)
ng-admin + Django REST framework ready-to-create administration tool
CRUD GET with Nuxt & Django REST Framework ②
CRUD POST with Nuxt & Django REST Framework
CRUD GET with Nuxt & Django REST Framework ①
Django REST Framework + Clean Architecture Design Consideration
CRUD PUT, DELETE with Nuxt & Django REST Framework
About the test
[Django] as_view () notes
Notes about with
Solution when Not Found appears when hitting the Django REST Framework API from the outside
Django Template notes
Django REST framework A little useful to know.
Implement JWT login functionality in Django REST framework
Notes about pytorch
Knowledge notes needed to understand the Python framework
[Django] JWT notes
Implementing authentication in Django REST Framework with djoser
About the queue
A little addictive information about Cliff, the CLI framework
Create a Todo app with Django REST Framework + Angular
More new user authentication methods with Django REST Framework
[CRUD] [Django] Create a CRUD site using the Python framework Django ~ 1 ~
Create APIs around user authentication with Django REST Framework
When you want to filter with Django REST framework
List method for nested resources in Django REST framework
Implement APIs at explosive speed using Django REST Framework
[CRUD] [Django] Create a CRUD site using the Python framework Django ~ 2 ~
Personal notes about the integration of vscode and anaconda
[CRUD] [Django] Create a CRUD site using the Python framework Django ~ 3 ~
[CRUD] [Django] Create a CRUD site using the Python framework Django ~ 4 ~
[CRUD] [Django] Create a CRUD site using the Python framework Django ~ 5 ~
Implement hierarchical URLs with drf-nested-routers in Django REST framework
Django python web framework
Creating an API that returns negative-positive inference results using BERT in the Django REST framework
Celery notes on Django
[Personal notes] Python, Django
About the Unfold function
About the service command
The Common Clk Framework
First Python miscellaneous notes
[Django] Rename the project
About the confusion matrix