11- vue django restful framework to create fresh supermarkets - user login and mobile phone registration (2)

Keywords: Python Django Mobile REST

Vue+Django REST framework

Set up a website of fresh supermarket with front and back ends separated
Django rtf completes mobile phone registration and user login (2)

user serializer and validator validation (registration function)

The registration page requires us to enter the phone number verification number and password.

django forms and model form s are used to validate the validity of fields submitted by users.

restful api is actually the operation of resources. Our registration resource is the user.

Because user registration must be crate model operation, inherit CreateModel Mixin

Prepare a complete set of Serilizers for viewset

Users/serializers.py (inherits modelSerializer, because fields are mandatory and all have one, although there is one more code field than user model)

With some tips, you can enjoy the benefits of model Serializer and break through its limitations

class UserRegSerializer(serializers.ModelSerializer):
    code = serializers.CharField(required=True, write_only=True, max_length=4, min_length=4,error_messages={
                                     "blank": "Please enter the validation code",
                                     "required": "Please enter the validation code",
                                     "max_length": "Verification code format error",
                                     "min_length": "Verification code format error"
                                 },
                                 help_text="Verification Code")

Customize error messages for each of our validations.

Verify that username exists:

    username = serializers.CharField(label="User name", help_text="User name", required=True, allow_blank=False,
                                     validators=[UniqueValidator(queryset=User.objects.all(), message="Users already exist")])
mark

drf provides us with unique field validation. together is a joint field validation, i.e. collection.
The relationship is unique.

UniqueValidator parameters:

  • queryset required - This is the queryset against which uniqueness should be enforced.
  • message - The error message that should be used when validation fails.
  • lookup - The lookup used to find an existing instance with the value being validated. Defaults to 'exact'.

What operations does lookup perform?

    username = serializers.CharField(label="User name", help_text="User name", required=True, allow_blank=False,
                                     validators=[UniqueValidator(queryset=User.objects.all(), message="Users already exist")])

Under the eclipse shortcut, the pycharm formatted shortcut is ctrl+shift+f

Add options that can be empty for mobile model.

    mobile = models.CharField(null=True, blank=True, max_length=11, verbose_name="Telephone")

Because the front end only has a cell phone number. This value also exists in username. So we need to blank out to ensure that the validation is successful.

Add a code field to Serilizer, which is a redundant field that will not be saved to the database

 def validate_code(self, code):

        # The difference between get and filter: get has two kinds of exceptions, one is multiple, the other is none.
        # try:
        #     verify_records = VerifyCode.objects.get(mobile=self.initial_data["username"], code=code)
        # except VerifyCode.DoesNotExist as e:
        #     pass
        # except VerifyCode.MultipleObjectsReturned as e:
        #     pass

        # If the validation code exists in the database, the user's value from the front-end post will be placed in initial_data and sorted (the latest one).
        verify_records = VerifyCode.objects.filter(mobile=self.initial_data["username"]).order_by("-add_time")
        if verify_records:
            # Get the latest article
            last_record = verify_records[0]

            # The validity period is five minutes.
            five_mintes_ago = datetime.now() - timedelta(hours=0, minutes=5, seconds=0)
            if five_mintes_ago > last_record.add_time:
                raise serializers.ValidationError("Verification code expiration")

            if last_record.code != code:
                raise serializers.ValidationError("Verification code error")

        else:
            raise serializers.ValidationError("Verification code error")

To verify the validation code, the possible errors have expired, and the validation code errors. Or records don't exist at all.

Delete the code field after verification

    # Verifiers without field names act on all fields. attrs is the total dict returned after the field validate
    def validate(self, attrs):
        attrs["mobile"] = attrs["username"]
        del attrs["code"]
        return attrs

Instantiating Serializer in User viewset in views

class UserViewset(CreateModelMixin, viewsets.GenericViewSet):
    """
    //user
    """
    serializer_class = UserRegSerializer

Configure the routing for the viewset

# Configure the url of users
router.register(r'users', UserViewset, base_name="users")

drf validates the default return format:

HTTP 400 Bad Request
Allow: POST, OPTIONS
Content-Type: application/json
Vary: Accept

{
    "username": [
        "Users already exist"
    ],
    "code": [
        "Verification code error"
    ]
}
  • Single field error: field + array
  • Joint field error: non_fields_error

Which is wrong and which is highlighted?

In general, our traditional development will like custom messages

{
    "status": 0,
    "msg": Verification code error
}

Bad points:

The front end has to do its own analysis to know which one corresponds to failure.
To do a single field prompt, the message needs to be in this format

{
    "status": 0,
    "msg":
    {
        moblie: [""],
        code: [""]  
    }
}

Status is almost the same as httpcode if it is used only to determine whether the user's status is correct or not.
rest api design pattern.

Pull-off: Excessive page requests, abnormal pages that have been abnormally displayed, but 200 status.
Affect seo. For Google

Developing based on http code allows you to make consistent judgments about incorrect and correct States

Perfect user registration, django semaphore to achieve user password modification.

Logic coding for user registration.

Authentication is performed after adding a user's SMS authentication code data in the background.

class UserViewset(CreateModelMixin, viewsets.GenericViewSet):
    """
    //user
    """
    serializer_class = UserRegSerializer
    queryset = User.objects.all()

http://www.django-rest-framework.org/api-guide/fields/

Error reporting information:

Got AttributeError when attempting to get a value for field `code` on serializer `UserRegSerializer`.
The serializer field might be named incorrectly and not match any attribute or key on the `UserProfile` instance.
Original exception text was: 'UserProfile' object has no attribute 'code'.

This is because we configure it.

        fields = ("username", "code", "mobile", "password")

There are four fields, and we have deleted the code during the processing validation of Serializer.

Solutions (important parameters):

Add write only=true to the code field. This field will not be serialized back to the front end.

The error above looks at part of the code for CreateModel Mixin in the source code. You can see that it executes save after verifying its validity. These are all fine, but when it returns Response, it returns the S rializer's data (as we configure in fields). This is because the fields in data are no longer consistent with those in model.

If it's a normal Serializer, it will serialize our post's past data and return it.

Password is also returned. This is unreasonable, adding write only =True parameter to password

Passwords are stored in plaintext.

    def create(self, validated_data):
        user = super(UserRegSerializer, self).create(validated_data=validated_data)
        user.set_password(validated_data["password"])
        user.save()
        return user

create method for overloading Serializer. It can be achieved.

Although the amount of overloaded code is very small, it may be difficult to understand, so we choose other solutions.

Appendix:

label is the left-hand word in the form shown below, and help text is below the input box.

django semaphore mechanism.

django post_save() method

When our model object operates, it emits a global signal. Capture and then do our own operations.

https://docs.djangoproject.com/en/2.0/ref/signals/

The semaphores of django, such as request_start and scrapy, are identical.

Do something before deleting to receive the pre_delete signal

post_save

http://www.django-rest-framework.org/api-guide/authentication/

Generating Tokens
By using signals
If you want every user to have an automatically generated Token, you can simply catch the User's post_save signal.

from django.conf import settings
from django.db.models.signals import post_save
from django.dispatch import receiver
from rest_framework.authtoken.models import Token

@receiver(post_save, sender=settings.AUTH_USER_MODEL)
def create_auth_token(sender, instance=None, created=False, **kwargs):
    if created:
        Token.objects.create(user=instance)

New file users/signals.py

# encoding: utf-8
__author__ = 'mtianyan'
__date__ = '2018/3/9 0009 09:29'

from django.conf import settings
from django.db.models.signals import post_save
from django.dispatch import receiver
from rest_framework.authtoken.models import Token

from django.contrib.auth import get_user_model
User = get_user_model()


# Which signal is received by parameter one and which model signal is received by parameter two
@receiver(post_save, sender=User)
def create_auth_token(sender, instance=None, created=False, **kwargs):
    # New or not, because post_save is also done when update
    if created:
        password = instance.password
        instance.set_password(password)
        instance.save()

Once you've done all this, you'll have to overload a configuration.

Otherwise, the password is not encrypted although there is no error message.

In apps.py.

    def ready(self):
        """
        Override this method in subclasses to run code when Django starts.
        """

This is a function we can customize in the subclass of AppConfig, which will run when django starts.

    def ready(self):
        import users.signals

The field of mobile we modified can be empty. It's useless to modify only in code. We also need to migrate.

In this way, we can add users directly in the background without requiring the mobile field.

mark

You can see that the password was successfully added and encrypted for us.

vue and registration function debugging

  • Look at the project structure of vue and find register.vue
                    <input class="btn btn-green" id="jsMobileRegBtn" @click="isRegister" type="button" value="Register and log in">

Clicking on registration and login calls the isRegister function.

isRegister(){
            var that = this;
            register({
                password:that.password,
                username:that.mobile ,
                code:that.code,
            }).then((response)=> {
              cookie.setCookie('name',response.data.username,7);
              cookie.setCookie('token',response.data.token,7)
              //Store in store
              // Update store data
              that.$store.dispatch('setInfo');
              //Jump to Home Page
              this.$router.push({ name: 'index'})

          })

A register's function interface is called.

export const register = parmas => { return axios.post(`${host}/users/`, parmas) }

It's actually pointing to our user url. Get the parameters. Change host to localhost for debugging

The parameters passed by register are password, username, code. These are the values from the front-end page post

Common registration logins include the completion of your own registration, and the completion of registration to help you automatically log in.

If the user is allowed to log in by himself, then comment out the two lines of cookie.setcookie. Let it jump directly to the home page.

But if it's automatic login, then we don't return jwt token to the front desk at this time.

Overload the create function in createmodelmixin.

Framework original modern code:

    def create(self, request, *args, **kwargs):
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        self.perform_create(serializer)
        headers = self.get_success_headers(serializer.data)
        return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)

    def perform_create(self, serializer):
        serializer.save()

The save in perform ance here is the saved current model (user). But it did not return to the model. If we want to get the user model, we have to rewrite it to return to the model.

Then insert our own logic after performing_create.

Analyze the source code implementation of jwt and find out which part of it is to generate token.

  • Entry from url. Point in get_jwt_token
    # token authentication of jwt
    path('login/', obtain_jwt_token )

Implementation code:

obtain_jwt_token = ObtainJSONWebToken.as_view()
class ObtainJSONWebToken(JSONWebTokenAPIView):
    """
    API View that receives a POST with a user's username and password.

    Returns a JSON Web Token that can be used for authenticated requests.
    """
    serializer_class = JSONWebTokenSerializer

At this point we can look at the inherited parent class: JSON WebToken APIView

In this class, the user comes after the post data.

 def post(self, request, *args, **kwargs):
        serializer = self.get_serializer(data=request.data)

        if serializer.is_valid():
            user = serializer.object.get('user') or request.user
            token = serializer.object.get('token')
            response_data = jwt_response_payload_handler(token, user, request)
            response = Response(response_data)
            if api_settings.JWT_AUTH_COOKIE:
                expiration = (datetime.utcnow() +
                              api_settings.JWT_EXPIRATION_DELTA)
                response.set_cookie(api_settings.JWT_AUTH_COOKIE,
                                    token,
                                    expires=expiration,
                                    httponly=True)
            return response

        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

The key is that token is obtained directly from Serializer, so the generation of token should be implemented in Serializer.

            token = serializer.object.get('token')

Find our Serializer

serializer = self.get_serializer(data=request.data)

    def get_serializer(self, *args, **kwargs):
        serializer_class = self.get_serializer_class()
        return self.serializer_class

Serializer is located in the ObtainJSON WebToken class rest_framework_jwt/views.py

    serializer_class = JSONWebTokenSerializer

Generation of token-related code in this class

                payload = jwt_payload_handler(user)

                return {
                    'token': jwt_encode_handler(payload),
                    'user': user
                }

Called jwt_encode_handler, which is set in api_setting

jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER

rest_framework_jwt/settings.py

DEFAULTS = {
    'JWT_ENCODE_HANDLER':
    'rest_framework_jwt.utils.jwt_encode_handler',
      'JWT_PAYLOAD_HANDLER':
    'rest_framework_jwt.utils.jwt_payload_handler',
    }

So we have found two important steps to generate token: payload and encode.

Implementation code:

from rest_framework_jwt.serializers import jwt_payload_handler, jwt_encode_handler

        re_dict = serializer.data
        payload = jwt_payload_handler(user)
        re_dict["token"] = jwt_encode_handler(payload)
        re_dict["name"] = user.name if user.name else user.username

         headers = self.get_success_headers(serializer.data)
        return Response(re_dict, status=status.HTTP_201_CREATED, headers=headers)

The token should be consistent with the front end. Note that Serializer.data, which was originally returned, is processed and returned.

Exit function

Exit functionality is much better because jwt token is not stored on the server side.

src/views/head/shophead.vue

Implementation code:

                <a @click="loginOut">Sign out</a>

The exit button calls the loginout function

Implementation code:

 loginOut(){
        cookie.delCookie('token');
        cookie.delCookie('name');
        //Retrigger store
        //Update store data
        this.$store.dispatch('setInfo');
        //Jump to login
        this.$router.push({name: 'login'})
      },

Empty token and send axios a notification. Jump to the login page.

Register page tests, align codes with the front end, and modify localhost

Posted by skorp on Sat, 15 Dec 2018 00:18:04 -0800