Django项目开发,XSS攻击,图片防盗链,图片验证码,kindeditor编辑器
一、Django项目开发
1. 项目开发流程
- 创建django项目,创建templates文件夹(pycharm创建django项目时,会自动创建templates文件夹和到settings文件中添加其路径)
- models文件中创建模型表类
- settings文件中注册app,添加静态文件路径,添加templates文件夹路径。
- urls文件中配置视图函数或视图类的路由
- views文件中书写业务逻辑
- templates文件夹中创建并书写前端页面
2. auth模块的补充
(1)django的admin可视化管理页面
-
只能是超级用户才能登陆操作
-
在django项目中,我们可以使用django自带的后台管理界面,进行管理员的后台的管理。
-
要想使用admin可视化管理页面,我们需要先将想要管理的模型表到对应的app文件夹的admin.py文件中注册
-
注册方法:
from django.contrib import admin from app01 import models admin.site.register(models.模型表的类名)
-
(2)将admin可视化管理页面的模型表显示成中文
- 方法:
# 在对应的模型表中书写:
from django.contrib.auth.models import AbstractUser
class UserInfo(AbstractUser):
def __str__(self):
return self.username
class Meta:
verbose_name_plural = '用户表'
# verbose_name = '用户表' # 会自动加s后缀
(3)auth模块的用户表上传用户头像的bug
- auth模块方法上传头像时,会有bug,所以我们勇士另外一种方式上传用户头像
# 不能用update方法
# 只能用对象点头像属性再赋值,相当于字典中的按key修改值方法,再用对象点save() 来保存(***一定不能忘了保存***)。
# 例子
# 错误演示:
file_obj = request.FILES.get("avatar")
models.Userinfo.objects.filter(pk=request.user.pk).update(avatar=file_obj) # 保存时不会自动拼接文件的路径,只会保存文件名
# 正确操作
file_obj = request.FILES.get("avatar")
request.user.avatar = file_obj # request.user 相当于user.obj
request.user.save()
(4)auth模块提供的登陆装饰器
- auth模块中的登陆认证装饰器,在一个视图函数被装饰上之后,我们如果在没有登录情况下访问,则会
跳转到你定义的登陆页面,在登陆页面的url后面会携带拼接参数——》拼接参数: ?next=被装饰的视图函数的url - 实例:
@login_required
def set_avatar(request):
return HttpResponse('OK')
# 登陆页面的url
127.0.0.1:8000/login?next=视图函数set_avatar对应的url
3. django项目的事务使用实例
# 添加评论表的数据后,并把文章表的评论统计次数加一
from django.db import transaction
with transaction.atomic():
models.Comment.objects.create(user=request.user,article_id=article_id,content=content,parent_id=parent_id)
models.Article.objects.filter(pk=article_id).update(comment_num = F('comment_num') + 1)
back_dic['msg'] = '评论成功'
return JsonResponse(back_dic)
二、django项目的media静态文件夹
-
media与static文件夹都是约定俗成的一种文件夹的命名
-
media静态文件夹存放的是用户上传的文件,包括图片、视频、音乐、也可以是文章等
-
static文件夹中存放的是项目中要用到的css、js、jQuery、字体、文件等
-
django项目中,对于用户上传的文件,django后端会自动识别,并保存到media文件夹中。当有文件要保存时,会到settings文件中定义的media的路径自动创建media文件夹
1. media文件夹的配置
(1)本地资源暴露给用户配置方法
- 本地任何资源都可以通过下面这种方法暴露给外界,谨慎使用
# 1. settings文件中配置
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
# 2. 总路由中配置(注意若不是总路由,如果有路由分发时,可能会出现错误)
from django.views.static import serve
url(r'^media/(?P<path>.*)',serve,{'document_root':settings.MEDIA_ROOT}), # 注意在有名分组后面没有 /
# 使用实例:
# models文件中:
class Userinfo(AbstractUser):
phone = models.BigIntegerField(null=True,blank=True) # blank告诉admin后台管理 该字段可以为空
avatar = models.FileField(upload_to='avatar/',default='media/avatar/default.jpg')
# 该字段你直接传文件即可,会自动将文件保存到avatar文件夹下,没有则会自动创建avatar文件夹,然后数据库里面存文件路径
register_time = models.DateField(auto_now_add=True)
blog = models.OneToOneField(to='Blog',null=True)
templates文件夹的某一html文件中:
<img class="media-object" src="/media/{{ userinfo.avatar }}/" alt="..." height="60">
三、django项目中的小知识点
1. 前端
(1)form表单的serializeArray方法
// from表单的serializeArray()统计所有普通键值对,包括csrf_token
$.each($('#myform').serializeArray(), function (index, obj) {
MyFormDate.append(obj.name, obj.value)
});
(2)img标签的src属性
- img的src属性的值有三种格式
- 资源的绝对路径
- 本网站的图片url
- 直接是二进制数据
(3)FormData对象
- 通过FormData来添加值时,ajax后面一定要指定contentType 和processData 这两个参数,都为false,因为FormData本来是用来传文件时使用的
let MyFormData = new FormData();
// 循环添加键值对
$.each($('#myform').serializeArray(),function (index,obj) {
MyFormData.append(obj.name , obj.value)
}) ;
$.ajax({
url:'',
type:'post',
data:MyFormData,
contentType:false, // 不要使用任何的编码 django能够识别对象自身
processData:false, // 不要让浏览器处理你的数据
success:function (back_data) {}
})
2. 后端
(1)把文章按日期分组查询
from django.db.models import Max,Min,Count,Sum,Avg
from django.db.models.functions import TruncMonth
date_list = models.Article.objects.filter(blog=blog).annotate(month=TruncMonth('create_time')).values('month').annotate(num=Count('pk')).values_list('month','num')
(2)request.path
和 request.get_full_path
# 这是获得全路径的一个方法 /user/register_name/?name=frank 会携带url后面的拼接参数
path = request.get_full_path()
# 这是获得路径的一个属性 /user/register_name/ 不会携带url后面的拼接参数
path = request.path
# os模块
# 在settings文件中
print(os.path.abspath(__file__)) # 绝对路径
print(os.getcwd()) # settings文件的当前路径
# 结果:
D:\DjangoProjects\bbs01\bbs01\settings.py
D:\DjangoProjects\bbs01\bbs01
(3)settings文件中的国际化配置
LANGUAGE_CODE = 'zh-hans' # zh-Hans是简体中文 zh-Hant是繁体中文 ,该字段可以让django的后端数据渲染到前端时,翻译成中文
TIME_ZONE = 'Asia/Shanghai'
# Django设置时区为TIME_ZONE = 'Asia/Shanghai' USE_TZ = True后,存入mysql中的时间只能是UTC时间。这个问题的解释还得看Django官方文档。其实是我理解错了,因为Mysql存储的时间不能灵活设置时区,不像datetime对象有一项参数专门指定时区,所以为了统一全球的时间,必须使用国际标准时间UTC,否则就会乱套。所有时间在存如数据库前,必须转换成UTC时间。比如北京时间8点,存入mysql变成0点(UTC)。
# 设了TIME_ZONE = 'Asia/Shanghai',数据库中存储的所有时间不会改变,还是UTC时间,它的作用是让数据库存储UTC时间在读取时自动转为北京时间(只有前台会自动转换,后台需要手动转换)
USE_I18N = True
USE_L10N = True
USE_TZ = False
(4)TODO注释用法
用法: 因为是注释,所以要加 #
# TODO 注释内容
# 在编辑器的TODO功能窗口可以直接定位到写了TODO注释的位置
四、XSS攻击
1. XXS攻击
- XSS攻击就是别人通过网站提供的上传文件接口向网站服务端上传带有的恶意js脚本,来消耗服务端资源
2. 防止XSS攻击的方法
-
利用beautifulsoup模块,过滤掉script标签
-
使用beautifulsoup模块前,要先下载安装:
python pip install bs4
-
实例
# 先生成一个模块对象 from bs4 import BeautifulSoup soup = BeautifulSoup(content,'html.parser') # print(soup.text) # 获取纯文本 ags = soup.find_all() # print(tags) for tag in tags: if tag.name == 'script': tag.decompose() # 删除script标签 # 文章描述的截取,简单粗暴的直接截取内容的150个字符串 desc = soup.text[0:150]
五、图片防盗链
- 为了防止自己的服务器被当做活雷锋,为其他网站提供服务
- 原理:利用http标准协议中的referer方法
http标准协议中有专门的字段记录referer
一来可以追溯上一个入站地址是什么
二来对于资源文件,可以跟踪到包含显示他的网页地址是什么
因此所有防盗链方法都是基于这个Referer字段
六、图片验证码
- 实际开发中,一般使用网上提供的验证码接口,如百度验证码等
- 生成图片验证码实例
- 用到了两个模块
PIL和io
- 用到了两个模块
自己书写图片验证码:
from PIL import Image,ImageDraw,ImageFont
import random
from io import BytesIO,StringIO
"""
内存管理器模块
BytesIO 保存数据 并且在获取的时候 是以二进制的方式给你
StringIO 保存数据 并且在获取的时候 是以字符串的方式给你
"""
"""
Image 生成图片
ImageDraw 在图片上写字
ImageFont 控制字的字体样式
"""
# 图片验证码
def get_random():
return random.randint(0,255),random.randint(0,255),random.randint(0,255)
def get_code(request):
# 步骤一:生成图片(两种方式)
# 方式一:直接使用本地图片,发送到前端
# with open('avatar/03.jpg','rb')as fr:
# data = fr.read()
#
# return HttpResponse(data)
# 方式二:利用pillow模块自动生成图片,保存到本地后在发送到前端
# 生成图片对象
# img_obj = Image.new('RGB',(360,35),get_random())
#
# # 将图片保存在本地
# with open('yanzhengma.png','wb')as fw:
# img_obj.save(fw,'png')
#
# # 利用文件操作将图片数据读取并发送到前端
# with open('yanzhengma.png','rb')as fr:
# data = fr.read()
# return HttpResponse(data)
# 步骤二:临时存储数据到能够随时取出的地方
# 生成一个图片对象
# img_obj = Image.new('RGB',(360,35),get_random())
#
# # 再生成一个io对象,这个对象相当于文件对象的句柄
# io_obj = BytesIO()
#
# # 把数据临时存储到io_obj中
# img_obj.save(io_obj,'png')
#
# # 读取并发送到前端数据
# return HttpResponse(io_obj.getvalue())
# 步骤三(完整写法):
# 生成图片对象
img_obj = Image.new('RGB',(360,35),get_random())
# 将生成的图片交给ImageDraw,生成一个笔画对象
img_draw = ImageDraw.Draw(img_obj)
# 指定字体样式
img_font = ImageFont.truetype('static/Font/simhei.ttf',30)
# 生成随机验证码(在图片上写字)
code = ''
for i in range(5):
upper_str = chr(random.randint(65,90)) # 随机大写字母
lower_str = chr(random.randint(97,122)) # 随机小写字母
num_str = str(random.randint(0,9)) # 随机数字
tmp = random.choice([upper_str, lower_str, num_str])
img_draw.text((i*60+60,0),tmp, get_random(), img_font)
code += tmp
print(code)
request.session['code'] = code
io_obj = BytesIO() # io_obj就相当于文本操作中的句柄
img_obj.save(io_obj,'png')
return HttpResponse(io_obj.getvalue())
七、kindeditor编辑器基本使用
- 详细用法直接查看kindeditor官方文档
# 前端页面中
<p>
<textarea name="content" id="id_comment" cols="30" rows="10" class="form-control"></textarea>
</p>
<script>
KindEditor.ready(function (K) {
window.editor = K.create('#id_comment',{
width: '100%',
height:'450px',
resizeType:0,
uploadJson : '/upload_img/', // kindeditor编辑器上传图片
extraFileUploadParams : {
csrfmiddlewaretoken : '{{ csrf_token }}',
}
});
});
</script>
# kindeditor编辑器上传图片后端代码
# urls文件中:
url(r'^upload_img/',views.upload_img),
# views文件中:
import os
from BBS import settings
def upload_img(request):
back_dic = {'error': 0}
# 获取用户上传的图片 然后保存到本地
if request.method == 'POST':
# print(request.FILES) 打印键值对
file_obj = request.FILES.get('imgFile') # imgFile是kindeditor提供的文件对象对应的key
# 手动拼接文件存储的文件路径
file_path = os.path.join(settings.BASE_DIR,'media','article_img')
if not os.path.isdir(file_path):
os.mkdir(file_path)
# 文件操作
# 手动拼接文件名全路径
img_path = os.path.join(file_path,file_obj.name)
with open(img_path,'wb') as f:
for line in file_obj:
f.write(line)
_url = '/media/article_img/%s'%file_obj.name
back_dic['url'] = _url
"""
//成功时
{
"error" : 0,
"url" : "http://www.example.com/path/to/file.ext"
}
//失败时
{
"error" : 1,
"message" : "错误信息"
}
"""
return JsonResponse(back_dic)