python-restful-02-组件(序列化\认证)

参考: rest-framework框架基础组件, 基于Token的WEB后台认证机制rest-framework之序列化组件

restful组件-1

一、序列化

对比:
	自己写的 for循环处理 (相对麻烦)
    django自带序列化工具 (不可控, 字段太多, 会造成网络拥堵问题)
    restful工具可控,但配置比较多, 后续会轻松些

1.1、django自带serializers

  • 使用说明
# 1、导入    from django.core import serializers
# 2、实例化   res = serializers.serialize('json', queryset)
  • 例子
from app01.models import Authers
from django.core import serializers
from rest_framework.views import APIView
from django.http.response import JsonResponse

class Mydjangoser(APIView):
    def get(self, reuqest):
        auther = Authers.objects.all()
        ser = serializers.serialize('json', auther)
        # safe=False 如果不设置这个 当字典中还有列表时返回就会报错
        return JsonResponse(ser, safe=False)
    
# 访问,因为序列化已经是字符串了,所以看的结果就是ascii
"[{\"model\": \"app01.authers\", \"pk\": 1, \"fields\": {\"name\": \"xiong\", \"pwd\": \"12\"}}, {\"model\": \"app01.authers\", \"pk\": 2, \"fields\": {\"name\": \"we\", \"pwd\": \"321\"}}]"

# 可以直接返回 HttpResponse  return HttpResponse(ser)
[{"model": "app01.authers", "pk": 1, "fields": {"name": "xiong", "pwd": "12"}}, {"model": "app01.authers", "pk": 2,
"fields": {"name": "we", "pwd": "321"}}]

# 字典中的字段无用的就太多了会给网络带来拥堵

1.2、drf的Serializer

  • 使用说明
# 1、先在 setting.py中注册 restful
# 2、导入 from rest_framework import serializers
# 3、引用 res = AutherSerializer(instance=queryset对象, many=True)
# 4、返回数据 res.data
  • 继承serializers.Serializers

    # 想序列化哪一个模型就将字段序列化
    class AutherSerializer(serializers.Serializer):
        name = serializers.CharField()
        age = serializers.CharField()
    
  • 类使用

class Mydjangoser(APIView):
    def get(self, reuqest):
        auther = Authers.objects.all()

        # 初始化方法 def __init__(self, instance=None, data=empty, **kwargs)
        # many 如果传true说明queryset对象中有多条,   kwargs.pop('many', None)
        # many 传false说明就只有一条字典

        res = AutherSerializer(instance=auther, many=True)
        # res.data 获取序列化的数据
        return JsonResponse(res.data, safe=False)

# 返回结果 [{"name": "xiong","pwd": "12"},{"name": "we","pwd": "321"}]
1.2.1、Serializer-source
# 模型: 
    class Userinfo(models.Model):
        id = models.AutoField(primary_key=True)
        name = models.CharField(max_length=32)
        pwd = models.CharField(max_length=64)
        userdetail = models.OneToOneField(to="Userdetail", to_field="id", on_delete=models.CASCADE)

    class Userdetail(models.Model):
        id = models.AutoField(primary_key=True)
        phone = models.IntegerField(max_length=13)
        email = models.EmailField()

# 视图
    class MyUser(APIView):
        def get(self, request, *args, **kwargs):
            user_list = Userinfo.objects.all()
            users = UserSerializers(instance=user_list, many=True)
            return JsonResponse(users.data, safe=False)
  • 正常

    # 序列化
    from rest_framework import serializers
    
    class UserSerializers(serializers.Serializer):
        name = serializers.CharField()
        pwd = serializers.CharField()
        userdetail = serializers.CharField()
    
    [{"name": "xiong","pwd": "123","userdetail": "Userdetail object (1)"},
      {"name": "hello","pwd": "321","userdetail": "Userdetail object (2)"}]
    
  • **source属性-1 修改名称 **

    # 将name修改为hello
    class UserSerializers(serializers.Serializer):
        hello = serializers.CharField(source="name")
        pwd = serializers.CharField()
        userdetail = serializers.CharField()
    
    [{"hello": "xiong","pwd": "123","userdetail": "Userdetail object (1)"},
      {"hello": "hello","pwd": "321","userdetail": "Userdetail object (2)"}]
    
  • source属性-2 一对一

class UserSerializers(serializers.Serializer):
    hello = serializers.CharField(source="name")
    pwd = serializers.CharField()
    userdetail = serializers.CharField(source="userdetail.email")

[{"hello": "xiong","pwd": "123","userdetail": "22@qq.com"},
  {"hello": "hello","pwd": "321","userdetail": "33@qq.com"}]
1.2.2、SerializerMethodField
  • 正常写法
class UserSerializers(serializers.Serializer):
    hello = serializers.CharField(source="name")
    pwd = serializers.CharField()
    userdetail = serializers.SerializerMethodField()
    
    # 这里的obj 其实就是 userinfo 的对象
    def get_userdetail(self, obj):
        return obj.userdetail.email

[{"hello": "xiong","pwd": "123","userdetail": "22@qq.com"},
  {"hello": "hello","pwd": "321","userdetail": "33@qq.com"}]
  • 返回关连表的全部, 像这样就需要一个一个的拼接比较麻烦
class UserSerializers(serializers.Serializer):
    hello = serializers.CharField(source="name")
    pwd = serializers.CharField()
    userdetail = serializers.SerializerMethodField()
    def get_userdetail(self, obj):
        # obj就是一个一个的对象, 类似于已经  .first了
        return {"email": obj.userdetail.email, "phone": obj.userdetail.phone}
  • 多表关连 - 写法1
# 单表没有.all属性,切记切记, 
class UserSerializers(serializers.Serializer):
    name = serializers.CharField()
    user = serializers.CharField()
    publish = serializers.SerializerMethodField()
    def get_publish(self, obj):
        xx = obj.publish.all()
        return [{"name": x.name} for x in xx]
  • 多表关连- 写法2
class PublishSerializers(serializers.Serializer):
    name = serializers.CharField()

class UserSerializers(serializers.Serializer):
    name = serializers.CharField()
    user = serializers.CharField()
    publish = serializers.SerializerMethodField()
    def get_publish(self, obj):
        xx = obj.publish.all()
        xy = PublishSerializers(instance=xx, many=True)
        return xy.data
1.2.3、ModelSerializer
  • 获取全部属性的方法
from app01.models import *
from rest_framework import serializers

class BookSerializer(serializers.ModelSerializer):
    # 元属性, 固定格式如下
    class Meta:
        model = Book
        fields = "__all__"

# 返回结果为 [{"id": 1, "name": "linux", "user": 1, "publish": [ 1 ]},.....]
  • 只显示某个值(类型于白名单)
class BookSerializer(serializers.ModelSerializer):
    class Meta:
        model = Book
        fields = ["name", "user"]
# 返回结果: [{"name": "linux", "user": 1},.....]
  • 不显示某些值(黑名单)
class BookSerializer(serializers.ModelSerializer):
    class Meta:
        model = Book
        exclude = ["id"]
    user = serializers.CharField(source="user.name")
# 返回结果为 [{"name": "linux", "user": 1, "publish": [ 1 ]},.....]
  • depth 深度

    class BookSerializer(serializers.ModelSerializer):
        class Meta:
            model = Book
            exclude = ["id"]
    		depth = 3		# 官方建议是10层,但如果表的字段很多会造成性能问题,建议2-3层
    
  • 配合source使用

    class BookSerializer(serializers.ModelSerializer):
        class Meta:
            model = Book
            # fields = "__all__"
            fields = ["name", "user"]
        user = serializers.CharField(source="user.name")
    # 返回结果: [{"name": "linux", "user": "xiong"},.....]
    
  • 配合 ModelSerializer

class BookSerializer(serializers.ModelSerializer):
    class Meta:
        model = Book
        exclude = ["id"]
    user = serializers.CharField(source="user.name")
    publish = serializers.SerializerMethodField()
    def get_publish(self, publish_obj):
        publish_all = publish_obj.publish.all()
        print(publish_all)	# <QuerySet [<Publish: Publish object (1)>]>
        return PublishSerializers(publish_all, many=True).data

# 返回结果
    [{"user": "xiong", "publish": [{"name": " 上海出版"}],"name": "linux"},...]
1.2.4、HyperlinkedIdentityField

鸡肋,并没有什么用,URL前台拼就行

  • 序列化

    # class BookSerializer(serializers.Serializer):
    #     name = serializers.CharField()
    #     publish = serializers.HyperlinkedIdentityField(view_name="publish",
    #                                                    lookup_url_kwarg="pk",
    #                                                    lookup_field="publish")
    
    class BookSerializer(serializers.ModelSerializer):
        class Meta:
            model = Book
            exclude = ["id"]
        publish = serializers.HyperlinkedIdentityField(view_name="publish",
                                                       lookup_url_kwarg="pk",
                                                       lookup_field="pk")
    
    # view_name:  视图别名, lookup_url_kwarg: url后传的参数, 
    # lookup_field: 本表的字段ID
    # lookup_url_kwarg 相当于是key,  lookup_field相当是value
    
  • 路由函数

from django.urls import re_path
from app01.views import *

urlpatterns = [
    re_path("publish/(?P<pk>\d+)", Mypublish.as_view(), name="publish"),
]
  • 视图

    class Mypublish(APIView):
        def get(self, request, pk):
            response = {"status": 1000, "msg": None}
            res = Publish.objects.filter(pk=pk).first()
            if res:
                res_ser = PublishSerializers(instance=res, many=False)
                response["msg"] = res_ser.data
            else:
                response["status"] = 1002
                response["msg"] = "没有这个字段"
            return JsonResponse(response, safe=False)
    
  • error_messages

# 错误字段显示为中文
    name = serializers.CharField(error_messages={"required": "必填字段"})

1.3、反序列化

保存和修改, 必须是继承了ModelSerializer的类才能使用

  • 保存
class Mybook(APIView):
    def get(self, request, *args, **kwargs):
        book_list = Book.objects.all()
        books = BookSerializer(instance=book_list, many=True, context={'request': request})
        return JsonResponse(books.data, safe=False)

    def post(self, request, *args, **kwargs):
        response = {"status": 1000, "msg": None}
        data = request.data
        
        # 数据检验
        ret = BookSerializer(data=data, many=False)
        # 检验数据是否正常
        if ret.is_valid():
            # 如果 是多对多表,这里需要做一下检验,比如是不是列表,是否为空
            xx = ret.save()  # 保存类型: <class 'app01.models.Book'>
            publists = data.get("publish")
            if publists:
                if isinstance(publists, list):
                    for p_num in publists:
                        p = Publish.objects.filter(pk=p_num).first()
                        xx.publish.add(p)
                else:
                    p = Publish.objects.filter(pk=data.get("publish")).first()
                    xx.publish.add(p)
            response["msg"] = "添加成功"
        else:
            response["status"] = 1002
            response["msg"] = "没有这个字段"
        return JsonResponse(response, safe=False)
  • 修改

    class Mybookdetail(APIView):
        def get(self, request, pk, *args, **kwargs):
            response = {"status": 1000, "msg": None}
    
            book_detail = Book.objects.filter(pk=pk).first()
            if book_detail:
                ret = BookSerializer(instance=book_detail, many=False)
                response["msg"] = ret.data
            return JsonResponse(response, safe=False)
    
        def put(self, request, pk, *args, **kwargs):
            response = {"status": 1000, "msg": None}
            book_detail = Book.objects.filter(pk=pk).first()
            if book_detail:
                # BookSerializer(data=book_detail, )
                ret = BookSerializer(data=request.data, instance=book_detail)
                data = request.data
    
                # ==================== 与添加一样
                # 检验数据是否正常
                if ret.is_valid():
                    # 如果 是多对多表,这里需要做一下检验,比如是不是列表,是否为空
                    xx = ret.save()  # 保存类型: <class 'app01.models.Book'>
                    publists = data.get("publish")
                    if publists:
                        if isinstance(publists, list):
                            for p_num in publists:
                                p = Publish.objects.filter(pk=p_num).first()
                                xx.publish.add(p)
                        else:
                            p = Publish.objects.filter(pk=publists).first()
                            xx.publish.add(p)
                    response["msg"] = "添加成功"
                else:
                    response["status"] = 1001
                    response["msg"] = ret.errors
                return JsonResponse(response, safe=False)
    
  • 总结

# 添加: 获取数据并检验,最后给它保存
        ret = BookSerializer(data=data, many=False)
        if ret.is_valid(): ret.save()

# 修改: 与添加不同的是, 获取数据前,需要先前修改的数据取出,然后在做检验最后保存
        ret = BookSerializer(data=request.data, instance=book_detail)
        if ret.is_valid(): ret.save()

1.4、钩子函数

1.4.1、源码分析

if ret.is_valid(): ret.save() # 直接从属性方法这进入到检验中, 需要注意的是 self.的都需要从第一级重新开始找, MRO

  • 检验方法is_valid
def is_valid(self, raise_exception=False):
    if not hasattr(self, '_validated_data'):
        try:
            # 这里是获取到全局的属性值
            self._validated_data = self.run_validation(self.initial_data)
        .....
    if self._errors and raise_exception:
        raise ValidationError(self.errors)

    return not bool(self._errors)
  • self.run_validation

    self.run_validation, 已知self是 BookSerializer,从父属性开始找
        # 找到 Serializer/run_validation
        
        def run_validation(self, data=empty):
    		# 这里返回的是每个参数的值
            value = self.to_internal_value(data)
            try:
                self.run_validators(value)
                value = self.validate(value)
                assert value is not None, '.validate() should return the validated data'
            except (ValidationError, DjangoValidationError) as exc:
                raise ValidationError(detail=as_serializer_error(exc))
    
            return value
    
  • value = self.validate(value), 全局钩子函数

    # value = self.validate(value), 获取到这个属性,就是全局钩子函数,我们可以使用派生修改它
        def validate(self, attrs):
            return attrs		# 返回的结果就是每个序列化的值, 全局钩子函数
    
  • self.to_internal_value(data)

# 这里返回的是每个参数的值
        value = self.to_internal_value(data)

# 局部钩子函数
	def to_internal_value(self, data):
        # filds 就是 user = serializers.CharField, 将其变为字典
        for field in fields:
            # 'validate_' + field.field_name 拼接的属性,这里就是局部的钩子函数
            # 如果没有就是 none, 使用的restful自带的检验
            validate_method = getattr(self, 'validate_' + field.field_name, None)
            primitive_value = field.get_value(data)
			....
        return ret
1.4.2、钩子
class BookSerializer(serializers.ModelSerializer):
    class Meta:
        model = Book
        exclude = ["id"]

    # 局部钩子
    def validate_name(self, data):
        if len(data) >=10:
            raise ValidationError("长度不能大于10")
        return data
    #  错误显示: "name": [ "长度不能大于10" ]


    # 全局钩子
    def validate(self, data):
        print("全局钩子")
        return data		# 必须要返回data
      # assert value is not None, '.validate() should return the validated data'

二、认证

2.1、认证源码分析

  • 从url开始 path("atest/", Atest.as_view()),

  • 进入到 ApiView/as_view()函数中

    @classmethod
    def as_view(cls, **initkwargs):
		# 继承了父类的 as_view方法
        view = super().as_view(**initkwargs)
        view.cls = cls
        view.initkwargs = initkwargs
        return csrf_exempt(view)
  • 此时是父类的 View/as_view

        @classonlymethod
        def as_view(cls, **initkwargs):
            ......
            def view(request, *args, **kwargs):
                .....
                # 注意,这里的self是 函数也就是Atest, 又从 ApiView重新开始找
                return self.dispatch(request, *args, **kwargs)
    
  • 进入到 ApiView/dispatch中

    def dispatch(self, request, *args, **kwargs):
        self.args = args
        self.kwargs = kwargs
        # 在这里重新封装了 request,进入查看
        request = self.initialize_request(request, *args, **kwargs)
        self.request = request
        self.headers = self.default_response_headers  # deprecate?

        try:
            self.initial(request, *args, **kwargs)
            ......
        except Exception as exc:
            response = self.handle_exception(exc)

        self.response = self.finalize_response(request, response, *args, **kwargs)
        return self.response
  • request = self.initialize_request(request, *args, **kwargs)

        def initialize_request(self, request, *args, **kwargs):
            """
            Returns the initial request object.
            """
            parser_context = self.get_parser_context(request)
    		# 返回的是重新封装好的Request
            return Request(
                request,
                parsers=self.get_parsers(),
                # authenticators, 认证模块
                authenticators=self.get_authenticators(),
                negotiator=self.get_content_negotiator(),
                parser_context=parser_context
            )
    
  • authenticators=self.get_authenticators(),

    # 也就是说  authenticators 返回的是一个个的列表套对象  [对象内存地址1,2,3 ]
    	def get_authenticators(self):
            return [auth() for auth in self.authentication_classes]
    
    # 默认的配置文件
    authentication_classes = api_settings.DEFAULT_AUTHENTICATION_CLASSES
  • self.initial(request, *args, **kwargs)

        def initial(self, request, *args, **kwargs):
            """
            Runs anything that needs to occur prior to calling the method handler.
            """
            self.format_kwarg = self.get_format_suffix(**kwargs)
    
            # Perform content negotiation and store the accepted info on the request
            neg = self.perform_content_negotiation(request)
            request.accepted_renderer, request.accepted_media_type = neg
    
            # Determine the API version, if versioning is in use.
            version, scheme = self.determine_version(request, *args, **kwargs)
            request.version, request.versioning_scheme = version, scheme
    
            # Ensure that the incoming request is permitted
            self.perform_authentication(request)
            self.check_permissions(request)
            self.check_throttles(request)
    
  • self.perform_authentication(request)

        def perform_authentication(self, request):
    		# 最终返回的就是一个request.user
            request.user
    
  • 继续往下看 request.user属性 from rest_framework.request import Request

# 进入Request中找着 user方法
    @property
    def user(self):
        if not hasattr(self, '_user'):
            # 错误处理
            with wrap_attributeerrors():
                self._authenticate()
        return self._user
  • 进入认证 self._authenticate()

    # 需要注意的是def user(self)的这个self, 其实是request参数
    
        def _authenticate(self):
            '''
            	循环 self.authenticators其实就是 request.authenticators,相当于是
                def get_authenticators(self):
                    return [auth() for auth in self.authentication_classes]
            '''
            for authenticator in self.authenticators:
                try:
                    user_auth_tuple = authenticator.authenticate(self)
                except exceptions.APIException:
                    self._not_authenticated()
                    raise
    			....
    
  • user_auth_tuple = authenticator.authenticate(self)

    def authenticate(self, request):
        return (self.force_user, self.force_token)

2.2、入门示例

  • 模型
class Users(models.Model):
    username = models.CharField(max_length=32)
    pwd = models.CharField(max_length=12)


class Token(models.Model):
    token = models.CharField(max_length=64)
    user = models.OneToOneField(to=Users, to_field="id", on_delete=models.CASCADE)
  • 登陆
class Login(APIView):
    def post(self, request, *args, **kwargs):
        response = {"status": 1000, "msg": "登陆成功"}
        username = request.data.get("uesrname")
        pwd = request.data.get("pwd")
        print(request.data)
        ret = Users.objects.filter(username=username, pwd=pwd).first()
        if not ret:
            token = uuid.uuid4()
            Token.objects.create(token=token, user=ret)
            response["token"] = token
        else:
            response["status"] = 1001
            response["msg"] = "请先登陆"
        return JsonResponse(response, safe=False)
  • 认证
class Auth:
    # 源码中的这一个 user_auth_tuple = authenticator.authenticate(self)
    # self就是需要传递的参数
    def authenticate(self, request):
        token = request.query_params.get("token")
        ret = Token.objects.filter(token=token).first()
        if not ret:
            raise exceptions.APIException("请先登陆")
        return ret.user, ret
  • 认证的模型
# 在需要被定义认证的类下中写, 需要注意的是,这里最多只能写三个,
# 并且如果AUth有返回值,后面的认证组件就不会在执行
class Mybookdetail(APIView):
    # 在需要认证的模型上添加
    authentication_classes = [Auth, ]

2.3、使用

2.3.1、全局使用
# # settings.py文件中定义, 所有的组件都可以放在这里
REST_FRAMEWORK = {
    "DEFAULT_AUTHENTICATION_CLASSES": ["app01.rest_auth.Auth"]
}
# 需要注意的是,如果定义了全局,那么直接使用login也会受到限制
2.3.2、局部使用\禁用
# 局部禁用
class Mybookdetail(APIView):
    authentication_classes = []  # 直接设置为空就行

# 局部使用
class Mybookdetail(APIView):
    authentication_classes = [PostAuth, ]
2.3.4、BaseAuthentication
# 继承基础组件, 也可以不继承
from rest_framework.authentication import BaseAuthentication
# 这个基础组件其实啥也没写, 关键在于就是继承authenticate_header头部类

class BaseAuthentication(object):
    def authenticate(self, request):
        raise NotImplementedError(".authenticate() must be overridden.")

    def authenticate_header(self, request):
        pass

2.4、忽略GET

# 1、先配置全局使用
# 2、允许GET属性查看, 其它的方法都需要登陆才能操作
class PostAuth(BaseAuthentication):
    def authenticate(self, request):
        http_method_names = ['post', 'put', 'patch', 'delete', 'head', 'options', 'trace']
        print(request.method)
        if request.method.lower() in http_method_names:
            token = request.query_params.get("token")
            ret = Token.objects.filter(token=token).first()
            if not ret:
                raise exceptions.APIException("请先登陆")
            return ret.user, ret
        return

3、demo

公共使用

  • setting.py
# 让全局生效
REST_FRAMEWORK = {
    "DEFAULT_AUTHENTICATION_CLASSES": ["app01.appAuthenticate.App01_authent", ]
}
  • urls.py
from django.contrib import admin
from django.urls import path
from app01 import views

urlpatterns = [
    path('admin/', admin.site.urls),
    path("mybook/", views.Mybook.as_view(), name="mybook"),
    path("login/", views.Login.as_view(), name="login"),
    path("logout/", views.logout, name="logout"),
]
  • logout

    def logout(request):
        auth.logout(request)
        return JsonResponse({"status": 1000, "msg": "退出成功"})
    
    class Mybook(APIView):
        def get(self, request, *args, **kwargs):
            response = {"status": 1000, "msg": None}
            book_list = Book.objects.all()
            if all([book_list, ]):
                ser = BookSerializer(book_list, many=True)
                response["data"] = ser.data
                response["msg"] = "查询成功"
            else:
                response["msg"] = "查询列表为空"
    
            return JsonResponse(response)
    

3.1、token判断

  • 模型
from django.db import models
from django.contrib.auth.models import User

class UserToken(models.Model):
    token = models.CharField(max_length=64)
    user = models.OneToOneField(to=User, to_field="id", on_delete=models.CASCADE)
  • view
from django.shortcuts import render, HttpResponse
from rest_framework.views import APIView
from app01.appSerializer import BookSerializer
from app01.models import *
from django.http import JsonResponse
from django.contrib import auth
import uuid


class Login(APIView):
    authentication_classes = []

    def get(self, request):
        return render(request, "test.html")

    def post(self, request):
        response = {"status": 1000, "msg": "登陆成功"}
        username = request.data.get("username")
        pwd = request.data.get("pwd")
        ret = auth.authenticate(request, username=username, password=pwd)
        if ret:
            auth.login(request, ret)
            token = uuid.uuid4()
            # 存到session中进行判断 
            request.session["token"] = str(token)
            UserToken.objects.update_or_create(user=ret, defaults={"token": token})
        else:
            response["msg"] = "登陆失败"
        return JsonResponse(response)
  • 认证
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#
from rest_framework.authentication import BaseAuthentication
from rest_framework import exceptions
from app01.models import *


# Create your views here.

class App01_authent(BaseAuthentication):
    def authenticate(self, request):
        try:
            token = request.session["token"]
            if token:
                ret = UserToken.objects.filter(token=token).first()
                if not ret:
                    raise exceptions.APIException("认证失败")
                return ret.user, token
        except Exception:
            raise exceptions.APIException("请先登陆")

3.2、session表中判断

在自定义token减少操作

  • App01_authent

    class App01_authent(BaseAuthentication):
        def authenticate(self, request):
            try:
                # 取出用户的 session, 如果没有登陆数据库中不会保存
                token = request.session.session_key
                if token:
                    ret = Session.objects.filter(session_key=token).first()
                    if not ret:
                        raise exceptions.APIException("认证失败")
                    # 返回用户,跟token, 
                    return auth.get_user(request), token
            except Exception:
                raise exceptions.APIException("请先登陆")
    
  • views

class Login(APIView):
    authentication_classes = []

    def get(self, request):
        return render(request, "test.html")

    def post(self, request):
        response = {"status": 1000, "msg": "登陆成功"}
        username = request.data.get("username")
        pwd = request.data.get("pwd")
        ret = auth.authenticate(request, username=username, password=pwd)
        if ret:
            auth.login(request, ret)
        else:
            response["msg"] = "登陆失败"
        return JsonResponse(response)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值