日志和rest_framework前后分离和重构

概念:是用来记录程序的运行情况,.log结尾
日志4组件:
Loggers:接收日志的入口
handlers:处理日志,并按照指定的格式保存
filters:过滤,过滤loggters对给handlers的日志信息
formatters:指定保存日志文件的格式

日志级别(从上到下递增):
DEBUG:用于调试目的的低级系统信息
INFO:一般系统信息
WARNING:表示出现一个较小的问题
ERROR:表示出现一个较大的问题
CRITICAL:表示出现一个致命的问题

日志级别等级CRITICAL > ERROR > WARNING > INFO > DEBUG > NOTSET

配置

settings.py中:

# 指定日志文件地址
# 此时的BASE_DIR是当前的工程路径
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))

# 此时的LOG_PATH等价于工程路径\log

LOG_PATH = os.path.join(BASE_DIR, 'log') --- 如果工程目录是D:\work\python那LOG_PATH就是D:\work\python\log

# isdir是判断是否为文件夹,os.mkdir,创建文件夹
if not os.path.isdir(LOG_PATH):
    os.mkdir(LOG_PATH)


# 配置日志文件信息
LOGGING = {
    # 必须是1
    'version':1,
    # disable_existing_loggers默认是True,如果是True,那么默认配置中的所有logger都将禁用
    'disable_existing_loggers': False,
    # 格式化日志
    'formatters':{
        # 定义simple格式化
        'simple':{
            'format':'%(created)s %(process)d %(message)s'
        }
    },
    # 接收日志信息
    'loggers':{
        # 指定handlers
        'dj':{
            'handlers':['dj_handler'],
            # 处理级别,表示INFO和INFO以上的级别
            'level': 'INFO',
        }
    },
    # 处理日志信息
    'handlers':{
        # 定义handler名字
        'dj_handler': {
            # 级别,表示大于DEBUG
            'level': 'DEBUG',
            # 指定文件类型,当日志文件的小大超过了maxBytes以后,就将文件进行切割
            'class': 'logging.handlers.RotatingFileHandler',
            # 存储的地址,在LOG_PATH下创建dj.log文件
            'filename': '%s/dj.log' % LOG_PATH,
            # 使用哪一个日志格式化的配置
            'formatter':'simple',
            # 指定日志文件的大小为5M,换算为1m=1024kb,1kb=1024b
            'maxBytes': 1024 * 1024 * 5,
        }
    },
}


使用日志

views中:
import logging
# 指定使用哪个loggers
logger = logging.getLogger('dj')

def hello(request):
    if request.method == 'GET':
        # 注意,日志级别只能使用loggers中设置包含的日志级别
        logger.info('HELLO')
        return HttpResponse('hello!')


使用装饰器包装日志
可以创建一个公共包,在公共包中创建.py文件,将日志装饰,然后在views中引用
在utils包中创建function.py文件
import logging

logger = logging.getLogger('dj')


def print_log(func):  # 定义装饰器

    def logs(request):
        logger.info('hello')
        return func(request)

    return logs
    
视图中:
from utils.functions import print_log

@print_log
def hello(request):
    if request.method == 'GET':
        # logger.info('HELLO')
        return HttpResponse('hello!')

前后分离

rest_fromework
用于前后分离,前端vue+后端api


MTV中T不包含在后端代码中,交给前端渲染

VUE + restframeworks

url定义:
    /app/student/?stu_id&name=张三
    
    /app/student/1/?name=张三
    

状态转移:
    GET:查询
    POST:创建  
    PUT:用于修改(修改全部变量)
    PATCH:用于修改(修改部分变量)
    DELETE:删除
    
请求方式POST,请求url: /app/student/ --- 创建student
请求方式DELETE, 请求url:/app/student/1/ --- 删除id为1的学生

    
多线程在计算密集型,比单线程慢
多线程在IO密集型,比单线程快
    

django中使用rest

pip install djangorestframework==3.4.6

<!--pip install django-filter-->

配置

INSTALLED_APPS中添加:'rest_framework'

使用

Serializer 允许复杂数据(比如 querysets 和 model 实例)转换成python数据类型,然后可以更容易的转换成 json 或 xml 等。同时,Serializer也提供了反序列化功能,允许解析数据转换成复杂数据类型

from rest_framework import serializers
class CommentSerializer(serializers.Serializer):
    email = serializers.EmailField()
    content = serializers.CharField(max_length=200)
    created = serializers.DateTimeField()
    
comment 是一个对象
serializer = CommentSerializer(comment)
serializer.data
# {'email': 'leila@example.com', 'content': 'foo bar', 'created': '2016-01-27T15:17:10.375877'}

在viewset中,只有5类Mixins,他们与http方法的对应如下:
mixins.ListModelMixin --- 定义list方法,返回一个Queryset的列表 --- GET
mixins.CreateModelMixin --- 定义一个create方法,创建一个实例 --- POST
mixins.RetrieveModelMixin --- 定义retrieve方法,返回一个具体的实例 -- GET
mixins.UpdateModelMixin --- 定义update方法,对某个实例进行更新 --- PUT/PATCH
mixins.DestroyModelMixin --- 定义delete方法,删除某个实例--- DELETE


例子:


在模型中创建一个Student模型

在应用下的url定义:
from rest_framework.routers import SimpleRouter
router = SimpleRouter() --- 创建一个实例
router.register('student', views.StudentSource)
urlpatterns = [
    url(r'^hello/', views.hello, name='hello'),
]
urlpatterns += router.urls --- 将urls添加


创建一个新的.py文件,用来指定序列化展示字段,在新建的.py文件中:
from rest_framework import serializers

from app.models import Student
# “ModelSerializer”只是一个普通的“序列化器,*提供了默认的“.create()”和“.update()”实现
class StudentSerializer(serializers.ModelSerializer):
    # 设置字段类型的对应错误的提示信息
    # CharField源代码在fields.py中,可以根据需求设置其他的错误提示信息。
    s_name = serializers.CharField(error_messages={'blank': '姓名不能为空'})


    class Meta:
        model = Student
        # 指定序列化展示字段
        fields = ['id', 's_name', 's_sex']
        
    # 同时你也可以给序列结果添加参数,重写父类方法
     def to_representation(self, instance):
        # instance是实例对象,一次循环拿到所有的对象
        # 调用父类,拿到序列化的结果
        data = super().to_representation(instance)
        # 对序列化结果进行添加参数
        data['address'] = '金科南路'
        return data

# CharField部分源代码
<!--class CharField(Field):-->
<!--    default_error_messages = {-->
<!--        'invalid': _('Not a valid string.'),-->
<!--        'blank': _('This field may not be blank.'),-->
<!--        'max_length': _('Ensure this field has no more than {max_length} characters.'),-->
<!--        'min_length': _('Ensure this field has at least {min_length} characters.')-->
<!--    }-->


views中:
from app.models import Student
from app.serializer import StudentSerializer
from rest_framework import mixins, viewsets
                    # 查询所有数据
class StudentSource(mixins.ListModelMixin,
                    # 查询指定某个数据 --- student/1/
                    mixins.RetrieveModelMixin,
                    # 使用POST创建数据
                    mixins.CreateModelMixin,
                    # 删除
                    mixins.DestroyModelMixin,
                    # 更新
                    mixins.UpdateModelMixin,
                    
                    viewsets.GenericViewSet):  # 继承父类,不继承会影响其他父类的使用,一个ViewSet类也是一个基础的View类,不提供任何请求处理函数,如get或post,只提供类似list和created这样的方法
    # 查询资源的所有数据
    queryset = Student.objects.all()
    queryset = Student.objects.filter(is_del=0) ---此时通过请求获得的数据是经过筛选的,is_del=0的才会被显示
    # 序列化
    serializer_class = StudentSerializer


配置完成可以使用Postman进行数据的增删改查。此时还不能进行筛选查询,想要筛选查询见下:

配置过滤查询-----------------------------------------------

安装第三方库 --- pip install django-filter 

在对应的应用下创建用于设置筛选的.py文件(例如:app_filter.py)
在创建的.py文件中:
# 设置在Pastman查询数据表student时中可以按照s_name查询
import django_filters

from rest_framework import filters

from app.models import Student


class StudentFilter(filters.FilterSet):
    # 过滤s_name参数,精确过滤, lookup_expr='contains'为模糊查询,
    # 默认大小写敏感,可以加i让其大小写不敏感
    s_name = django_filters.CharFilter('s_name', lookup_expr='contains')
    # 过滤年龄字段大于某个数值的数据 --- http://127.0.0.1:8081/app/student/?s_age_min=20
    s_age_min = django_filters.NumberFilter('s_age', lookup_expr='gt')
    # 过滤年龄字段小于某个数值的数据 --- http://127.0.0.1:8081/app/student/?s_age_max=20
    # http://127.0.0.1:8081/app/student/?s_age_min=20&age_max=24 --- 查询年龄大于20小于24的数据
    s_age_max = django_filters.NumberFilter('s_age', lookup_expr='lt')
    

    class Meta:
        model = Student
        fields = ['s_name',]
        

        
在views中:
    # 查询资源的所有数据
    queryset = Student.objects.filter(is_del=0)
    # 序列化
    serializer_class = StudentSerializer
    # 过滤
    filter_class = StudentFilter
    # 自定义过滤
    # def get_queryset(self):
    #     queryset=self.queryset
    #     return queryset.filter(s_name__contains='小')
    # 定义以后用GET搜索到的数据就是s_name中带有小的数据
    
在settings.py中的REST_FRAMEWORK添加
REST_FRAMEWORK ={
    'DEFAULT_FILTER_BACKENDS': ('rest_framework.filters.DjangoFilterBackend',
                                'rest_framework.filters.SearchFilter'),
}

配置完成后可以用定义的变量名用以下方式查询:
http://127.0.0.1:8081/test1/user/?username=张珊
http://127.0.0.1:8081/app/student/?s_age_min=25
http://127.0.0.1:8081/app/student/?s_age_min=20&s_age_max=25
    

rese返回的结果操作

rest返回结果:状态码,请求状态,数据

原生返回结果
{
    "id": 6,
    "s_name": "GSF",
    "s_sex": true
}


rese的返回结果需要修改:
{
    "code": 0,
    "msg": "请求成功",
    "data": {
        "id": 6,
        "s_name": "GSF",
        "s_sex": true
    }
}

在公共修改文件夹中创建.py修改文件:
在.py文件中
from rest_framework.renderers import JSONRenderer

class MyJSONRenderer(JSONRenderer):
    """
    {
        code:0
        msg:'请求成功',
        data:''
    }
    """
    def render(self, data, accepted_media_type=None, renderer_context=None):
        # 返回来的数据中包含了code和mag,可以通过pop拿出来
        if isinstance(data, dict):
            code = data.pop('code',200)
            msg = data.pop('msg','请求成功')
        else:
            code = 0
            msg = '请求成功'

        res = {
            'code': code,
            'msg': msg,
            'data': data
        }

        return super().render(res, accepted_media_type=None,renderer_context=None)


settings中:
REST_FRAMEWORK = {
    'DEFAULT_RENDERER_CLASSES':(
        'utils.ReturnRenderer.MyJSONRenderer', --- utils文件夹下的ReturnRenderer.py文件的MyJSONRenderer类
    ),
}

注意两个问题:
1、此时我们获取data数据格式应该改变
{
    "code": 0,
    "msg": "请求成功",
    "data": [
        {
            "id": 5,
            "s_name": "小明",
            "s_sex": false
        },
        {
            "id": 6,
            "s_name": "GSF",
            "s_sex": true
        }
    ]
}
$.get('/app/student/',function(data){
            console.log(data['data'])
            }



2、存在第二个问题,当我们请求数据失败时(例如数据库中没有id=20的数据),此时返回的结果和返回成功的状态码是一样的,需要进行重构。


class StudentSource(mixins.ListModelMixin,
                    # 查询指定某个数据 --- student/1/
                    mixins.RetrieveModelMixin,
                    # 使用POST创建数据
                    mixins.CreateModelMixin,
                    # 删除
                    mixins.DestroyModelMixin,
                    # 更新
                    mixins.UpdateModelMixin,
                    viewsets.GenericViewSet):
    # 此时重构的是mixins.RetrieveModelMixin的retrieve方法
    def retrieve(self, request, *args, **kwargs):
        try:
            instance = self.get_object()
            serializer = self.get_serializer(instance)
            return Response(serializer.data)
        except:
            data = {
                'code': 1001,
                'mag': '学生不存在'
            }
        return Response(data)


分页:
settings中:
REST_FRAMEWORK = {
    # 重构返回数据结构
    'DEFAULT_RENDERER_CLASSES':(
        'utils.ReturnRenderer.MyJSONRenderer',

    ),
    # 定义分页信息 --- 配置以后,ajax通过GET信息只能得到第一页的信息
    'DEFAULT_PAGINATION_CLASS':'rest_framework.pagination.PageNumberPagination',
    'PAGE_SIZE':2,
}

返回结果:
{
    "code": 0,
    "msg": "请求成功",
    "data": {
        "count": 3,  # 总数据量
        "next": "http://127.0.0.1:8081/app/student/?page=2", --- 写一页的地址
        "previous": null,
        "results": [
            {
                "id": 5,
                "s_name": "小小明",
                "s_sex": false
            },
            {
                "id": 6,
                "s_name": "GSF",
                "s_sex": true
            }
        ]
    }
}

# 在ajax中获取data数据应用
    $.get('/app/student/',function(data){
            console.log(data['data']['results'].length)


REST_FRAMEWORK = {
    # 重构返回数据结构
    'DEFAULT_RENDERER_CLASSES': (
        'utils.ReturnRenderer.MyJSONRenderer',

    ),
    # 定义分页信息
    'DEFAULT_PAGINATION_CLASS':'rest_framework.pagination.PageNumberPagination',
    'PAGE_SIZE': 2,
    # 配置过滤
    'DEFAULT_FILTER_BACKENDS': ('rest_framework.filters.DjangoFilterBackend',
                                'rest_framework.filters.SearchFilter'),

}

装饰器

面向切面编程

def is_login(func):

    def check_user():
        return func()
        
    return check_user --- 返回内层函数的结果

utils --- 创建一个公共方法的包文件夹(有__init__.py的文件夹),所有的公共方法都可以写在其中
    
@is_login
def my_login()
    pass
    
@is_login --- 加了一个装饰器以后,所有的相关函数都要加装饰器,不然装饰器的限制对其他函数没有发挥作用
def index():
    pass

前端VUE

通过ajax渲染前端页面


# 获取所有学生 --- GET
function all_student(){
            <!--alert('1234')-->
            $.get('/app/student/',function(data){
            <!--console.log(data)-->
                s = '<table><tr><td>编号</td><td>姓名</td><td>操作</td></tr>'
                for(var i=0; i < data.length; i++){
                    s +=  '<tr><td>' + data[i].id + '</td><td>' + data[i].s_name
                    s += '</td><td>'
                    s += '<a onclick="del_student(' + data[i].id + ');">删除 </a>'
                    s += '<a href="data[i].id ">编辑</a>'

                    s += '</td></tr>'
                }
                s += '</table>'
                $('#stu_div').html(s)
            });

        }
        
# 删除 -- DELETE
function del_student(id){
            $.ajax({
                url:'/app/student/' + id + '/',
                type:'DELETE',
                dateType:'json',
                success: function(data){
                    alert('删除成功')
                },
                error: function(data){
                    alert('删除失败')
                }
            })
        }

# 添加 -- POST --- 在使用POST时,会出现403错误,可通过下列标注1和标注2解决
# 或者可以在处理的views的函数上添加装饰符@csrf_exempt决
<script src="https://code.jquery.com/jquery-3.1.1.min.js"></script>
    <script type="text/javascript">
    function add_student(){
        var s_name = $('#s_name').val()
        var s_age = $('#s_age').val()
        var s_sex = $('#s_sex').val()
        <!--标注1 -->
        var csrf = $('input[name="csrfmiddlewaretoken"]').val()

        $.ajax({
            url:'/app/student/',
            type:'POST',
            dataType:'json',
            # 将data的数据通过POST传到/app/student/中
            data:{
                's_name':s_name,
                's_age':s_age,
                's_sex':s_sex
            },
            <!-- 标注2 -->
            headers:{'X-CSRFToken':csrf},
            success:function(data){
                alert('添加成功')
            },
            error:function(data){
                alert('添加失败')
            }

        }
        )
    }
    </script>

# PUT或者PACTH
html中:
s += '<a onclick="update_student(' + data['data']['results'][i].id + ');">编辑</a>'

function update_student(id){
            <!--alert(id)-->
            location.href="/app/update_student/" + id + "/"
        }
        
urls中:
url(r'^update_student/(\d+)/$',views.update_student,name='update_student'),

views:
def update_student(request,id):
    if request.method == 'GET':
        stu = Student.objects.get(id=id)
        return render(request, 'update.html',{'stu':stu})
        
在跳转的HTML页面中:
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>编辑页面</title>
    <script src="https://code.jquery.com/jquery-3.1.1.min.js"></script>
    <script type="text/javascript">
        function update_tj(id){
            var s_name = $('#s_name').val()
            var s_age = $('#s_age').val()
            var s_sex = $('#s_sex').val()
            alert(s_name)
            $.ajax({
                url:'/app/student/'+ id + '/',
                type:'PATCH',
                dataType:'json',
                data:{
                    's_name':s_name,
                    's_age':s_age,
                    's_sex':s_sex
                },
                success:function(){
                    alert('请求成功')
                },
                error:function(){
                    alert('请求失败')
                },

            })
        }
    </script>

</head>
<body>
<form action="" method="post">
    <span>姓名:</span><input type="text" name="s_name" value="{{ stu.s_name }}" id="s_name"><br>
    <span>年龄:</span><input type="text" name="s_age" value="{{ stu.s_age }}" id="s_age"><br>
    <span>性别:</span><input type="text" name="s_sex" value="{{ stu.s_sex }}" id="s_sex"><br>
    <input type="button" value="提交" onclick="update_tj({{ stu.id }});">
</form>

</body>
</html>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值