django Rest Framework Series 4 - Authentication & Permissions

Keywords: Django REST Python Database

Official address: http://www.django-rest-framework.org/tutorial/4-authentication-and-permissions/

Currently, our API does not limit who can edit or delete snippets code. We need some more advanced behavior to ensure that:
snippets code is always associated with the Creator
Only authenticated users can create snippets
Only the creator of snippet can update or delete information
Unauthenticated requests can only have read-only access

1. Adding information to the model
Make some changes in our Snippet model class. First, add some fields. One of the fields is used to represent the user who created the snippet. Another field will be used to store a highlighted HTML representation of the code.
Add two fields to the model file models.py of Snippet et

owner = models.ForeignKey('auth.User', related_name='snippets', on_delete=models.CASCADE)
highlighted = models.TextField()

We also need to make sure that when the model is saved, we fill the highlighted fields with the pygments highlighted code base.
We need extended imports:

from pygments.lexers import get_lexer_by_name
from pygments.formatters.html import HtmlFormatter
from pygments import highlight

Now we can add the. save() method to our modles class:

def save(self, *args, **kwargs):
    """
    Use the `pygments` library to create a highlighted HTML
    representation of the code snippet.
    """
    lexer = get_lexer_by_name(self.language)
    linenos = self.linenos and 'table' or False
    options = self.title and {'title': self.title} or {}
    formatter = HtmlFormatter(style=self.style, linenos=linenos,
                              full=True, **options)
    self.highlighted = highlight(self.code, lexer, formatter)
    super(Snippet, self).save(*args, **kwargs)

When all additions are complete, we need to update our database tables. Normally we need to migrate through the database, but this is not the purpose of this section. Here we delete the database and rebuild it directly.

rm -f tmp.db db.sqlite3
rm -r snippets/migrations
python manage.py makemigrations snippets
python manage.py migrate

You may also need to create a number of different users to test the API. The fastest way is to use the create super command


python manage.py createsuperuser

2. Adding User Module Side
Now that we have some users, we'd better add those user descriptions to our API. Add in serializers.py:

from django.contrib.auth.models import User

class UserSerializer(serializers.ModelSerializer):
    snippets = serializers.PrimaryKeyRelatedField(many=True, queryset=Snippet.objects.all())

    class Meta:
        model = User
        fields = ('id', 'username', 'snippets')

Since'snippets'is a reverse association of the User model, it is not included by default when using the ModelSerializer class, so we need to add an explicit field for it.
We also need to add some views to views.py. We only provide read-only views for users. so we will use ListAPIView and RetrieveAPIView general base class views:

from django.contrib.auth.models import User


class UserList(generics.ListAPIView):
    queryset = User.objects.all()
    serializer_class = UserSerializer


class UserDetail(generics.RetrieveAPIView):
    queryset = User.objects.all()
    serializer_class = UserSerializer

Ensure that the UserSerializer class is imported

from snippets.serializers import UserSerializer

Finally, we need to add these views to the API by referring to the URL configuration and adding the following information to urls.py:

url(r'^users/$', views.UserList.as_view()),
url(r'^users/(?P<pk>[0-9]+)/$', views.UserDetail.as_view()),



3. Associate Snippet and User
Now, if we create a snippet snippet snippet snippet snippet snippet snippet snippet snippet snippet snippet snippet snippet snippet snippet snippet snippet Users are not sent as part of a serialized representation, but as attributes of Request.
Our approach is to override the. perform_create() method in the snippet view, allowing us to modify how to manage instance saves and handle any information implicit in incoming requests or request URL s.
In the SnippetetList class view, add the following method:

def perform_create(self, serializer):
    serializer.save(owner=self.request.user)

In our serialization method, create() will pass through an additional owner field, with the value of the user in the request data

4. Update Serializer
Now that snippets are associated with the user created, update our Snippet Serializer to verify. Add the following fields to the serializers.py file

owner = serializers.ReadOnlyField(source='owner.username')

Note: Make sure you have added'owner'to the field of the internal Meta class
This field does something interesting. The source parameter controls the properties of the filled field and can perform any properties of the serialized instance. It can also use the dotted lines shown above, in which case it will traverse a given property in a similar way as the Django template language.
The field we added is a typed ReadOnlyField class. Contrary to other types of fields, such as CharField, BooleanField, etc., typed ReadOnlyField is always read-only and will be used for serialization representations. It cannot be used for deserialization when updating an instance of the model. We can also use CharField(read_only=True) here.

5. Add the required permissions to the view
Now that snippets snippets snippets snippets snippets snippets snippets snippets snippets snippets snippets snippets snippets snippets snippets snippets snippets snippets snippets snippet
The REST framework contains many permission classes that we can use to restrict who can access a given view. What we need to look at here is the IsAuthenticated OrReadOnly module, which ensures that requests get read and write access.
First, add the following modules in the views view:

from rest_framework import permissions

Then, add the following attributes in the SnippetetList and SnippetetDetail class views

permission_classes = (permissions.IsAuthenticatedOrReadOnly,)



6. Add login browsing API
If you open a browser to browse the API at this time, you will find that you can't create a new code segment. In order to achieve this operation, we need to log in as a user.
We can add the login view to browse the API, and edit the URL configuration at the urls.py file level in our project
Import the following modules at the top:

from django.conf.urls import include

Then, at the end of the file, add a rule that contains views for login and logout

urlpatterns += [
    url(r'^api-auth/', include('rest_framework.urls',
                               namespace='rest_framework')),
]

In the above configuration, the r^api-auth / section can be any URL you want to use. The only restriction is that the namespace of the include part must use rest_framework, and in Django 1.9+, the REST framework will set the namespace, so you can delete it.
Now if you open the browser and refresh it, you will see a'Login'connection at the top of the right side of the page. If you log in with the user you created earlier, you can create code snippets again.
Once you have created some code snippets, navigate to / users /, in the browser address bar, and display a list of code snippets associated with each user, in the snippets field of each user

7. Object-level permissions
In fact, we want all the code snippets to be visible to anyone. But make sure that only the user who created the snippet can update or delete it.
In order to achieve this function.
We need to create a custom permission
In snippets APP, create a new file permissions.py

from rest_framework import permissions

class IsOwnerOrReadOnly(permissions.BasePermission):
    """
    Custom permission to only allow owners of an object to edit it.
    """

    def has_object_permission(self, request, view, obj):
        # Read permissions are allowed to any request,
        # so we'll always allow GET, HEAD or OPTIONS requests.
        if request.method in permissions.SAFE_METHODS:
            return True

        # Write permissions are only allowed to the owner of the snippet.
        return obj.owner == request.user

Now we can add custom permissions to our snippet instance by editing the permission_classes attribute to the SnippetDetail class view:

permission_classes = (permissions.IsAuthenticatedOrReadOnly,
                      IsOwnerOrReadOnly,)

Make sure that the IsOwnerOrReadOnly class has been imported:

from snippets.permissions import IsOwnerOrReadOnly

Now, if you open the browser again, you will find that the `DELETE'and `PUT' actions only appear behind the code segment of the user who logged in and created the code segment.


Pass API certification
Because we now have permissions in the API, we need to authenticate the request if we need to edit snippets. We haven't set up any authentication classes yet, so we use the default application Session Authentication and Basic Authentication.
When we interact with the API through a browser, we can log in, and then the browser session will provide the required authentication for the request.
If we create a snippet without authentication, an error will be returned:

http POST http://127.0.0.1:8000/snippets/ code="print 123"

{
    "detail": "Authentication credentials were not provided."
}

We can successfully post-request by creating a user's username and password.

http -a tom:password123 POST http://127.0.0.1:8000/snippets/ code="print 789"

{
    "id": 1,
    "owner": "tom",
    "title": "foo",
    "code": "print 789",
    "linenos": false,
    "language": "python",
    "style": "friendly"
}

summary
We now have a fairly elaborate set of privileges on our Web API and provide endpoint s for users of the system and the code segments they create.

Posted by jzimmerlin on Tue, 08 Jan 2019 16:21:09 -0800