概念:是用来记录程序的运行情况,.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>