Django REST-Framework(DRF) Advancement (Getting Started)

Posted by Bill H on Mon, 03 Jan 2022 15:22:53 +0100

Since I first came into contact with DRF, I have been able to understand how DRF is written, generally similar to the django MVT framework, except that Template is replaced by Serializer.

#Ps: Small white talks to himself, big guys please ignore it!!

Goal Establishment

Before we get started, we need to decide two things: what to do? How do you do it? However, I didn't find any interesting projects (or I wasn't good enough...), so I did the simplest:

  • Logon function
  • Comment function
  • Focus on functionality

Use the database as: MySQL

requirement analysis

Now that you have a simple need, you should consider how to do the above functions. Clearly, all three are user-related.

Secondly, the premise of the comment function is that there is so much to comment on, so we add a Tweet-like feature to it: Picture + Text + Comment + Concern.

The general steps are as follows:

  1. Configuration data model
  2. Logon function
  3. Pictures, Long Text and Praise
  4. follow

Start Time

Before we get started, we need three app s (not counting merchandise information):

Once created, it will probably look like the following image (python manage startapp xxx x)*3

friendships is the focus function app, mysite is the login function, comment function app, scenery is the image upload, with emotional app.

 

1. Configuration data model

1.1 Rewrite User Model

The User model that comes with Django is somewhat unsatisfactory to do with a Tweet-like function, so we add the data features we want to build on it, such as phone number and gender.

'''
position : mysite/models
auther : Sthons
'''

...
from django.contrib.auth.models import AbstractUser,BaseUserManager
class UserManage(BaseUserManager):

    def _create_user(self,phone,username,password,**kwargs):
        if not phone:
            raise ValueError('Please enter your mobile number')
        if not password:
            raise ValueError('Please input a password')
        user = self.model(phone=phone,username=username,**kwargs)
        user.set_password(password)
        user.save()
        return user

    def create_user(self,phone,username,password,**kwargs):
        kwargs['is_superuser'] = False
        return self._create_user(phone=phone,username=username,password=password,**kwargs)

    def create_superuser(self,phone,username,password,**kwargs):
        kwargs['is_superuser'] = True
        return self._create_user(phone=phone, username=username, password=password, **kwargs)

...

...

class User(AbstractUser):
    gender=(
        ('male','male'),
        ('female','female'),
        ('default','secrecy')
    )
    id = models.AutoField(primary_key=True)
    username = models.CharField(max_length=20, unique=True)
    sex = models.CharField(max_length=20, choices=gender, default='default')
    phone = models.CharField(max_length=25, unique=True)
    USERNAME_FIELD = 'username'
    REQUIRED_FIELDS = ['phone',]
    objects = UserManage()
    def __str__(self):
        return f'Id: {self.id},Username: {self.username}'

...
  • USERNAME_FIELD: Set the authentication identity, defaulting to the user name when admin logs in. Since we override the User model, we need to define it manually. # Ps: You can also change to something else, such as a mailbox number, a mobile number, etc.
  • REQUIRED_FIELDS: When a user is created through the create superuser administration command, a list of field names to prompt for is shown below.

1.2 Configure other data models

'''
position : mysite/models
auther : Sthons
'''


class Message(models.Model):
    id = models.AutoField(primary_key=True)
    user = models.ForeignKey(User,on_delete=models.CASCADE)
    content = models.TextField(max_length=400)
    scenery = models.ForeignKey(Scenery, null=True, on_delete=models.SET_NULL)
    created_at = models.DateTimeField(auto_now=True)
    thumbs = models.IntegerField(default=0)

# -----------------------------------------------------------------------------------

'''
position : scenery/models
auther : Sthons
'''

from django.db import models

class Scenery(models.Model):
    id = models.AutoField(primary_key=True)
    title = models.CharField(max_length=140,null=True)
    img = models.ImageField(upload_to='scenery/images/')
    text = models.TextField(max_length=280)
    thumbs = models.IntegerField(default=0)

    def __str__(self):
        return f'Landscape Name:{self.title}'


# -----------------------------------------------------------------------------------

'''
position : friendships/models
auther : Sthons
'''


class Friendship(models.Model):
    # Focuser
    from_user = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, related_name='follower_friendship_set')
    # Focused
    to_user = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, related_name='following_friendship_set')
    created_at = models.DateTimeField(
        auto_now_add=True
    )

    class Meta:
        index_together = (
            ('from_user_id', 'created_at'),
            ('to_user_id', 'created_at'),
        )
        unique_together = (('from_user_id', 'to_user_id'),)

    def __str__(self):
        return '{} followed {}'.format(self.from_user_id, self.to_user_id)

1.3 Migrate Database

  • python manage makemigrations
  • python manage migrate

2. Realization of login registration function

2.1 Registration Function

Create a serializers in mysite. Py file, where you enter the following to build our first serialization model.

'''
position : mysite/serializer
auther : Sthons
'''
...
from rest_framework import serializers
from mysite.models import User
...


...
class UserSerializer(serializers.ModelSerializer):
    class Meta:
        model = User
        fields = (
            'id',
            'username',
            'password',
            'phone',
            'email',
            'sex',
            'date_joined'
        )
...


...
class SignupSerializer(serializers.ModelSerializer):
    class Meta:
        model = User
        fields = ('username','password','email','phone','sex')

    username = serializers.CharField(max_length=14)
    password = serializers.CharField(max_length=256)
    email = serializers.EmailField()
    phone = serializers.CharField(max_length=256)


    def validate(self, attrs):
        if User.objects.filter(username=attrs['username']).exists():
            raise ValidationError({
                "message": [
                    "The user name is already registered"
                ]
            })

        if User.objects.filter(email=attrs['email']).exists():
            raise ValidationError({
                "message": [
                    "This mailbox is already in use"
                ]
            })

        if User.objects.filter(phone=attrs['phone']).exists():
            raise ValidationError({
                "message": [
                    "The mobile number is already in use"
                ]
            })
        return attrs

    def create(self, validated_data):
        username = validated_data['username']
        password = validated_data['password']
        email = validated_data['email']
        sex = validated_data['sex']
        phone = validated_data['phone']
        user = User.objects.create(
            username=username,
            password=password,
            email=email,
            phone=phone,
            sex=sex
        )
        return user

...

Ideas:

Write a function validate to validate, then raise error s to send error information

Validated_in create Data can be any variable name (it doesn't conflict with other function names) to accept parameters

Write mysite/views later. Py content allows it to be presented in an intuitive way:

'''
position : mysite/views
auther : Sthons
'''

class MysiteViewSet(viewsets.ModelViewSet):
    queryset = User.objects.all()
    serializer_class = UserSerializer
    permission_classes = (AllowAny,)
    # Logon status
    @action(methods=['GET'], detail=False)
    def login_status(self, request):
        data = {}
        data['Login_status'] = request.user.is_authenticated
        if request.user.is_authenticated:
            data['user'] = UserSerializer(request.user).data
        return Response(data)

    # register
    @action(methods=["POST"], detail=False, serializer_class=SignupSerializer)
    def signup(self, request):
        data = SignupSerializer(data=request.data)
        if not data.is_valid():
            return Response({
                "success": False,
                "message": "Please check input",
                "errors": data.errors,
            }, status=400)
        user = data.save()
        user.set_password(data.validated_data['password'])
        user.save()
        login(request, user)
        return Response({
            "success": True,
            "user": UserSerializer(user).data
        }, status=201)

Ideas:

* Serialize the data in the request through the serializer

Via is_valid test exception

Submit data to the database via save

Since the password we entered manually is not encrypted, set_is used here The password built-in function encrypts the password and submits it to the database again

Login built-in function for user login

* Last returned json data with status code 201

Last URLs in the root directory. Register it in the pyfile and add it to the url:

'''
position : Django_vue_Project1/urls.py
auther : Sthons

'''

...
site_router = routers.DefaultRouter()
...

...
site_router.register(r'mysite', MysiteViewSet, basename='mysite')
...

...
urlpatterns = [
    path('admin/', admin.site.urls),
    path('',include(site_router.urls)),
    path('api-auth/', include('rest_framework.urls', namespace='rest_framework')),
]
...

Design sketch:

2.2 Logon Function

        

The code for login and registration is written in the same way as before, which I've just written here.

'''
position : mysite/views
auther : Sthons
'''

...
    @action(methods=['POST'], detail=False, serializer_class=LoginSerializer)
    def login(self, request):
        serializer = LoginSerializer(data=request.data)
        if not serializer.is_valid():
            return Response({
                "success": False,
                "message": "Please check input.",
                "errors": serializer.errors,
            }, status=400)

        username = serializer.validated_data['username']
        password = serializer.validated_data['password']

        user = authenticate(request, username=username, password=password)

        if not user:
            return Response({
                "success": False,
                "message": "Username and password does not match."
            }, status=400)

        login(request, user)
        return Response({
            "success": True,
            "user": UserSerializer(instance=user).data,
        })
...
'''
position : mysite/serializer
auther : Sthons
'''

...
from rest_framework import serializers
from rest_framework.exceptions import ValidationError
from mysite.models import User
...

...
# Write an extra serialization here, which you'll use later
class SimpleUserSerializer(serializers.ModelSerializer):
    class Meta:
        model = User
        fields = ('id','username')
...


...
class LoginSerializer(serializers.ModelSerializer):
    class Meta:
        model = User
        fields = ('username','password')

    username = serializers.CharField(max_length=14)
    password = serializers.CharField(max_length=256)

    def validate(self, attrs):
        if not User.objects.filter(username=attrs['username']).exists():
            raise ValidationError({
                "message": [
                    "user name does not exist"
                ]
            })

        return attrs

...

#Ps: Actually, this is the end. After all, the code is written in almost the same way, mainly with ideas.

3. Picture upload, long text and compliments

3.1 Picture, Message

        

'''
position : scenery/views
auther : Sthons
'''

class SceneryViewSet(viewsets.ModelViewSet):
    queryset = Scenery.objects.all()
    permission_classes = (IsAuthenticated,)
    serializer_class = ScenerySerializer


    @action(methods=['POST'],detail=False,serializer_class = AddPicSerializer)
    def addScenery(self,request):
        serializer = AddPicSerializer(data=request.data)
        if not serializer.is_valid():
            return Response({
                "success":False,
                "message":"Please check your input"
            },status=400)
        scenery = serializer.save()
        return Response({
            "success": True,
            "scenery": ScenerySerializer(scenery).data
        },status=200)
'''
position : scenery/serializer
auther : Sthons
'''

from rest_framework import serializers

from mysite.models import Message
from mysite.serializers import *
from scenery.models import Scenery

class SimpleScenerySerializer(serializers.ModelSerializer):
    class Meta:
        model = Scenery
        fields = ('id','title')

class ScenerySerializer(serializers.ModelSerializer):

    class Meta:
        model = Scenery
        fields = ('id', 'title', 'img', 'text', 'thumbs')



class AddPicSerializer(serializers.ModelSerializer):
    class Meta:
        model = Scenery
        fields = ('title', 'img', 'text')

    title = serializers.CharField(max_length=140)
    img = serializers.ImageField()
    text = serializers.CharField(max_length=280)


    def create(self, validated_data):
        title = validated_data['title']
        img = validated_data['img']
        text = validated_data['text']

        scenery = Scenery.objects.create(
            title=title,
            img=img,
            text=text,
            thumbs=0
        )
        return scenery

3.2 Comment Module

First of all, the contents of the serializer:

'''
position : scenery/serializer
auther : Sthons
'''


class SimpleEssaySerializer(serializers.ModelSerializer):
    class Meta:
        model = Message
        fields = '__all__'

class EssaySerializer(serializers.ModelSerializer):
    user = SimpleUserSerializer()
    scenery = SimpleScenerySerializer()
    class Meta:
        model = Message
        fields = ('id', 'user', 'scenery', 'content', 'thumbs', 'created_at')

# Show all content
class AllEssaySerializer(serializers.ModelSerializer):
    scenery = SimpleScenerySerializer()
    class Meta:
        model = Message
        fields = ('id', 'scenery', 'content', 'thumbs', 'created_at')

# Add Content
class AddEssaySerializer(serializers.ModelSerializer):

    scenery = ScenerySerializer
    class Meta:
        model = Message
        fields = ('id','scenery','content')

    content = serializers.CharField(max_length=400)

    def create(self, validated_data):
        content = validated_data['content']
        user = self.context['request'].user
        scenery = validated_data['scenery']
        essay = Message.objects.create(
            user=user,
            content=content,
            scenery=scenery
        )
        return essay

Note here that fields can affect the json parameter characteristics you pass in

#Ps: The name of the serializer here is not very good. The simple prefix should have only one or three data characteristics according to my naming habits, but overall it's not a big problem.

        scenery/views.py

'''
position : scenery/views
auther : Sthons
'''

class EssayViewSet(viewsets.ModelViewSet):
    queryset = Message.objects.all()
    permission_classes = (IsAuthenticated,)
    serializer_class = AddEssaySerializer

    def create(self, request, *args, **kwargs):
        if not request.user.is_authenticated:
            return Response({
                "message":"Please sign in before speaking"
            },status=403)

        serializer = AddEssaySerializer(data=request.data, context={'request': request})
        if not serializer.is_valid():
            return Response({
                "message" : "Please check",
                "errors" : serializer.errors
            },status=400)

        user = EssaySerializer(serializer.save()).data
        return Response(
            user, 
            status=201
        )



    def list(self, request, *args, **kwargs):

        essay = Message.objects.filter(
            user_id=request.user.id
        ).order_by('-created_at')
        serializer = EssaySerializer(essay, many=True).data

        return Response({
            "essay": serializer
        })

DRF built-in licensing parameters are used here:

  • IsAuthenticated: Authenticated user
  • AllowAny: Everyone
  • IsAdmin: Administrator

Let's get a general idea:

  • The first step is to determine whether a user is logged in through the IsAuthenticated that comes with django.
  • Then serialize the data and pass it back to the background with the context. Of course, it's okay to leave the context blank. I'm just here to make it easier to add functionality later
  • Submit data to database after passing data validation and return json data

Last in urls. Register the view just written in py:

...
site_router.register(r'scenery', SceneryViewSet, basename='scenery')
site_router.register(r'context', EssayViewSet, basename='context')
...

See the effect:

First of all, the picture and message modules, you can see that the data has been uploaded successfully, and as for how the pictures are displayed, I don't think of a solution at this time (real white behavior)

Following is the comment:

        

 

You can see whether this part of the information is heavy, first according to the foreign key settings of our database: models.SET_NULL, set the value to empty when the data pointed to by the foreign key is deleted, in order to restore the effects of "this content is not available", "cracking".

And the Scenery checkbox below works because:

If parentheses are the details and no parentheses are the references, it is up to us to define what the style is:

 

 

 

4. Focus on Functions

#Ps: Before you write the code for this module, make sure you know who is watching and who is watching.

4.1 Concern

Add code first:

'''
position : friendships/views
auther : Sthons
'''

...
class FriendshipViewSet(viewsets.GenericViewSet):
    queryset = User.objects.all()
    serializer_class = FriendshipSerializerForCreate

    @action(methods=["POST"], detail=True, permission_classes=[IsAuthenticated, ])
    def follow(self, request, pk):
        if Friendship.objects.filter(from_user=request.user, to_user=pk).exists():
            return Response({
                "success": True,
                "duplicate": True
            }, status=201)
        serializer = FriendshipSerializerForCreate(data={
            "from_user_id": request.user.id,
            "to_user_id": pk
        })
        if not serializer.is_valid():
            return Response({
                "success": False,
                "errors": serializer.errors
            }, status=400)
        serializer.save()
        return Response({
            'success': True
        }, status=201)
...
'''
position : friendships/serializer
auther : Sthons
'''

...
from friendships.models import Friendship
from rest_framework import serializers
from rest_framework.exceptions import ValidationError
from mysite.serializers import UserSerializer
...

...
class FriendshipSerializerForCreate(serializers.ModelSerializer):
    from_user_id = serializers.CharField(max_length=60)
    to_user_id = serializers.CharField(max_length=60)

    class Meta:
        model = Friendship
        fields = ("from_user_id", "to_user_id")

    def validate(self, data):
        if data["from_user_id"] == data["to_user_id"]:
            raise ValidationError({
                "message": [
                    "You can't care about yourself!!!"
                ]
            })
        return data

    def create(self, data):
        from_user_id = data["from_user_id"]
        to_user_id = data["to_user_id"]
        user = Friendship.objects.create(
            from_user_id=from_user_id,
            to_user_id=to_user_id
        )
        return user
...

In fact, it is not difficult to focus on modules. The real difficulty lies in the disordered thinking in the programming process. In fact, the problem is mainly the named pot. Anything can be done as long as the name is obtained well.

Again here:

from_user => follower

to_user => watched

When the follower is you and the follower is Li Si

It's like you paid attention to Li Si.

4.2 Check your attention and your fans

With that foundation, the rest is simple:

'''
position : friendships/serializer
auther : Sthons
'''

...
# Your concern
class FollowingSerializer(serializers.ModelSerializer):
    user = UserSerializer(source="to_user")

    class Meta:
        model = Friendship
        fields = ('user', 'created_at',)

# Your fans
class FollowerSerializer(serializers.ModelSerializer):
    user = UserSerializer(source="from_user")

    class Meta:
        model = Friendship
        fields = ('user', 'created_at',)
...
'''
position : friendships/views
auther : Sthons
'''

...
    # Your concern
    @action(methods=["GET"], detail=True, permission_classes=[AllowAny, ])
    def followings(self, request, pk):

        user = Friendship.objects.filter(from_user_id=pk).order_by('-created_at')
        serializer = FollowingSerializer(user, many=True).data
        return Response({
            "followings": serializer
        }, status=200)

    # Your fans
    @action(methods=['GET'], detail=True)
    def followers(self, request, pk):

        follower = Friendship.objects.filter(to_user_id=pk).order_by('-created_at')
        return Response({
            "followers": FollowerSerializer(follower, many=True).data
        })
...

Finally, close:

'''
position : friendships/views
auther : Sthons
'''

...
    @action(methods=['POST'], detail=True)
    def unfollow(self, request, pk):
        # write your code here
        if request.user.id == pk:
            return Response({
                "success": False,
                "message": "?????"
            })

        if not Friendship.objects.filter(to_user_id=pk, from_user_id=request.user.id).exists():
            return Response({
                "success": True,
                "deleted": 0
            })

        unuser = Friendship.objects.filter(to_user_id=pk, from_user_id=request.user.id)
        unuser.delete()

        return Response({
            "success": True,
            "deleted": 1
        })
...

The idea is simple: just judge if you have a relationship with someone you care about=>him, and if so, delete the relationship from the database.

Last paste using method:

Reference to the following figure:

Person with ID number 2

follow: * Focus

followers: What you care about

following:: Focus on your

unfollow: * Close

 

Well, that's all for this article. Although I know nobody's watching it, I still hope the handsome brothers and beautiful sisters I've seen will point out a compliment.

(╯‵□′)╯︵┻━┻