一、实例
1.1、生成并渲染图片方法
from django.shortcuts import render, HttpResponse
from PIL import Image
from io import StringIO, BytesIO
import random
# 使用 from PIL import Image, 新建一个图片
# 生成一张图片,第一个是模式:RGB, 第二个参数是图片大小,第三个参数是图片颜色
Image.new('RGB', (宽,高), color="颜色") 或者 color=(0,0,0)
def vaild_random(count):
return tuple([random.randint(0,233) for i in range(count)])
def valid_check(request):
""" 1、前台渲染图片
<img src="{% url 'valid_check' %}" alt="" height="30" width="240">
with open("static/image-20210220102205414.png", "rb") as f:
data = f.read()
return HttpResponse(data)
"""
# pip install pillow
# new(mode, size, color=0)
""" 局限性、每次生成img都会在本地保存为一个文件,增加了IO对性能的压力
img = Image.new("RGB", (320, 33), vaild_random(3))
with open("static/img.png", "wb") as f: # 保存图片
img.save(f, "png")
with open("static/img.png", "rb") as f: # 获取图片数据
data = f.read()
return HttpResponse(data)
"""
# 第三种,推荐使用这种
img = Image.new("RGB", (320, 33), vaild_random(3))
io = BytesIO()
img.save(io, "png") # 直接通过二进制形式写到内存中
data = io.getvalue() # 获取到数据给它返回
return HttpResponse(data)
- 方式一
def valid_check(request):
# 1、前台渲染图片
# <img src="{% url 'valid_check' %}" alt="" height="30" width="240">
with open("static/image-20210220102205414.png", "rb") as f:
data = f.read()
return HttpResponse(data)
- 方式二、三
def valid_check(request):
""" 局限性、每次生成img都会在本地保存为一个文件,增加了IO对性能的压力
img = Image.new("RGB", (320, 33), vaild_random(3))
with open("static/img.png", "wb") as f: # 保存图片
img.save(f, "png")
with open("static/img.png", "rb") as f: # 获取图片数据
data = f.read()
return HttpResponse(data)
"""
# 第三种,推荐使用这种
img = Image.new("RGB", (320, 33), vaild_random(3))
io = BytesIO()
img.save(io, "png") # 直接通过二进制形式写到内存中
data = io.getvalue() # 获取到数据给它返回
return HttpResponse(data)
1.2、在画板中生成随机字符串
def vaild_random(count):
return tuple([random.randint(0,233) for i in range(count)])
def get_vaild_num(count):
f = StringIO()
for i in range(count):
ran_num = random.choice([random.randint(65, 90), random.randint(97, 122), random.randint(0, 9)])
get_num = chr(ran_num) if ran_num > 9 else ran_num
f.write(str(get_num))
return f.getvalue()
def valid_check(request):
"""
先画个图(img), 打开个画板(ImageDraw),放个图片(ImageDraw.Draw)
画板中操作, 写点字 整个字体(draw.text)
"""
# 1、先画一个图
img = Image.new("RGB", (320, 33), vaild_random(3))
# 2、获取一支画笔
draw = ImageDraw.Draw(img, "RGB")
# 3、添加字符集
font = ImageFont.truetype("static/font/fzht.TTF", size=20)
# xy: x横轴 y竖轴,text:文本内容,fill=None: 字体颜色 ,font=None:要用哪种字体
vaild_num = get_vaild_num(6)
# 4、在画笔中添加随机字符串,fill是字符的颜色
draw.text((120,0), vaild_num, fill=vaild_random(3), font=font)
# 5、在内存中保存图片并将它直接返回
io = BytesIO()
img.save(io, "png") # 直接通过二进制形式写到内存中
data = io.getvalue() # 获取到数据给它返回
return HttpResponse(data)
1.2.1、添加点线
width= 270
height = 40
for i in range(10):
x1 = random.randint(0,width)
x2 = random.randint(0,width)
y1 = random.randint(0,height)
y2 = random.randint(0,height)
draw.line((x1,x2,y1,y2), fill=vaild_random(3))
for i in range(50):
draw.point([random.randint(0,width), random.randint(0,height)], fill=vaild_random(3))
x= random.randint(0,width)
y= random.randint(0,height)
draw.arc((x,y, x+4, y+4),0 ,90,fill=vaild_random(3))
1.2.2、验证码更新
<div class="col-md-6 vaild_check">
<img id="vaild_img" src="{% url 'valid_check' %}" alt="">
</div>
$(".vaild_check").on("click", function () {
$.ajax({
url: "{% url 'valid_check' %}",
type: "get",
success: function (data) {
# 先删除
$(".vaild_check").children("#vaild_img").remove()
# 然后在添加一个html标签
$(".vaild_check").html(
'<img id="vaild_img" src="{% url "valid_check" %}">'
)
}
})
})
1.2.3、时区配置
TIME_ZONE = 'Asia/Shanghai' # 时区使用 UTF8
USE_TZ = Fase # 不使用UTC时间,改用本地配置的时间
1.3、前端头像预览
#img_avatar {
width: 50px;
height: 50px;
}
#avatar { display: none;}
<div class="form-group">
<label for="avatar">头像
<img src="/static/image/default.png" alt="" id="img_avatar">
</label>
<input type="file" name="avatar" id="avatar">
</div>
<script>
$("#avatar").on("change", function () {
var get_pre_avatar = $("#avatar")[0].files[0]
// js前端文件阅读器
var filereader = new FileReader()
// 将图片对象传到文件阅读器中
filereader.readAsDataURL(get_pre_avatar)
// 打开文件filereader.result
// 直接将文件对象渲染到img标签上是不可行的,需要加载完成才能进行操作 .onload
filereader.onload = function() {
$("#img_avatar").attr("src", filereader.result)
}
})
</script>
1.4、头像上传处理
关键字: request.FILES
, FileField(upload_to="upload/")
, serializeArray
- 前端-html
# 具体前端页面: https://blog.csdn.net/u010304195/article/details/113944437
$(".btn").on("click", function () {
var forms = new FormData()
{# 对象.serialize() #}
var forms_serialize = $(".get_froms").serializeArray();
// console.log(forms_serialize) 获取的类型为: [{…}, {…}, {…}, {…}, {…}]
//jquery的循环,传参数,第一个参数是要循环的对象,第二个参数是一个匿名对象
$.each(forms_serialize, function (key,items) {
forms.append(items.name, items.value)
})
forms.append("avatar", $("#avatar")[0].files[0])
$.ajax({
url: "{% url 'register' %}",
type: "post",
processData: false,
contentType: false,
data: forms,
success: function (data) {
console.log(data)
$("#error").html(data["msg"]);
}
})
})
- 后端
class Register(View):
def get(self, request):
return render(request, "bbs/register.html")
def post(self, request):
username = request.POST.get("username")
pwd = request.POST.get("pwd")
email = request.POST.get("email")
phone = request.POST.get("phone")
msg = {"code": 1000, "msg": ""}
# 取到图片,模型是 FileField 上传会直接处理,目录也会一并创建
avatar = request.FILES.get("avatar")
# 如果不用FileField模型 就需要使用下面这种方式处理
// with open("upload/{}11".format(avatar.name), "wb") as f:
// # from django.core.files.base import File
// for line in avatar.chunks():
// f.write(line)
if all([username, pwd, email, phone]):
if not UserInfo.objects.filter(username=username).first():
user = UserInfo.objects.create_user(username=username, password=pwd, email=email, phone=phone, avatar=avatar)
msg["msg"] = "注册成功"
else:
msg["msg"] = "注册失败,用户已存在"
else:
msg["msg"] = "选项不能为空"
return JsonResponse(msg)
- 模型
from django.contrib.auth.models import AbstractUser
# Create your models here.
class UserInfo(AbstractUser):
id = models.AutoField(primary_key=True)
phone = models.CharField(max_length=14, null=True)
avatar = models.FileField(upload_to="upload/", default="static/img/default.png")
blog = models.OneToOneField(to="Blog", to_field="id", on_delete=models.CASCADE, null=True)
1.4.1、media上传处理
from django.views.static import serve
# serve 图片处理,需要传递有名参数 path
# 第一个是有名分组,正则表达式,
# 第二参数, 函数的内存地址
# 第三个参数: 字典,它会以关键字参数传递
re_path('media/(?P<path>.*)', serve, {"document_root": settings.MEDIA_ROOT}),
# 当使用FileField使用上传时,可以将图片上传到media中,通过配置settings.py可直接定义
MEDIA_URL = "/media/"
MEDIA_ROOT = os.path.join(BASE_DIR, "media")
# 模型
avatar = models.FileField(upload_to="upload/", default="static/img/default.png")
# 当指定上传目录为upload时,如果配置了media_url,那么真实上传的目录是
# 项目 media/upload/file.name
urls.py文件
# avatar = models.FileField(upload_to='avatar/', default='/static/avatar/default.png')
# 如果不配置保存就是 avatar/文件.xx , 配置之后就是 media/avatar/文件.xx
# 数据库中保存的格式是 avatar/Tulips.jpg
MEDIA_URL = "/media/"
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
1.5、分页器
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#
from django.core.paginator import Paginator
def paginators(object, page):
paginator = Paginator(object, 5)
# 用django自带的检验处理
try:
page = paginator.validate_number(page)
except Exception:
page = 1
# 这里获取的是文章
object = paginator.page(page)
# 这里处理下边页码数
count_paginator = paginator.num_pages
"""
- 判断是否大于11,如果小于则有多少返回多少
- 判断 获取页码-5是否小于等于1, 如果是则返回 1到11页
- 判断 获取页码+5是否大于总页面, 如果是 则总的页面左边减10,右边加1直到最后一行
- 其它情况则 获取的页码 左右各减5
"""
if count_paginator > 11:
if page - 5 <= 1:
per_list = range(1, 11)
elif page + 5 > count_paginator:
per_list = range(count_paginator - 10, count_paginator + 1)
else:
per_list = range(page - 5, page + 5)
else:
per_list = paginator.page_range
return object, per_list, page
# 使用
def index(request, page=1):
# 查询出所有的数据
articles = Article.objects.all()
# 获取分页后的所有数据, 文章, 页码, 当前页
articles, per_list, page = paginators(articles, page)
return render(request, 'bbs/index.html', locals())
1.6、多URL处理
以点赞为例
- URL
re_path("(?P<digg>digg_up|digg_down)/", views.digg, name="digg"),
- html
function clicks(obj, urls) {
obj.on("click", function () {
console.log(obj)
$.ajax({
url: urls,
type: "post",
data: {
"archive_id": "{{ articles.id }}",
"user_id": "{{ user.userinfo.pk }}",
"csrfmiddlewaretoken": '{{ csrf_token }}'
},
success: function (data) {
$("#digg_tips").html(data["msg"])
}
})
setTimeout(function () {
$("#digg_tips").html("")
}, 3000)
})
}
# 获取点击事件,然后传一个url
clicks($(".diggit"), "/digg_up/")
clicks($(".buryit"), "/digg_down/")
- views
from django.http import JsonResponse
def digg(request, digg):
if request.method == "POST":
result = {"status": 100, "msg": None}
archive_id = request.POST.get("archive_id")
user_id = request.POST.get("user_id")
article = Article.objects.filter(pk=archive_id).first()
auther = UserInfo.objects.filter(pk=user_id).first()
upanddown = Upanddown.objects.filter(article=article, user_id=auther)
if upanddown:
result["msg"] = "您已经点过了"
else:
if digg == "digg_up":
Upanddown.objects.create(article=article, user_id=auther, is_check=True)
Article.objects.filter(pk=archive_id).update(up_num=F("up_num") + 1)
ret = "点赞成功"
elif digg == "digg_down":
Upanddown.objects.create(article=article, user_id=auther, is_check=False)
Article.objects.filter(pk=archive_id).update(down_num=F("down_num") + 1)
ret = "反对成功"
return JsonResponse(result)
-
模型
class Upanddown(models.Model): id = models.AutoField(primary_key=True) article = models.ForeignKey(to="Article", to_field="id", on_delete=models.CASCADE) user_id = models.ForeignKey(to="UserInfo", to_field="id", on_delete=models.CASCADE) is_check = models.BooleanField() class Meta: unique_together = (("user_id", "article", "is_check"),)
1.7、django发送邮件
二、前台
2.1、es6 拼接语法
if (data["status"] == 1000) {
// 提交完后台之后,将内容填充到评论页中
var xx = `
<li class="list-group-item">
<p class="small"> <span>${data["user"]}: </span> </p>
<p class="list-group-item-success p_content">${contents}</p>
</li>
`
# ul中定义的类
$(".p_comm_append").append(xx)
}
2.1.1、获取内容切割文件
var contents = $("#contents").val()
if (pid) {
var index = contents.indexOf("\n") + 1;
// 拿到某个值的起始位置indexOf,取到指定值的索引值的后面所有值
contents = contents.slice(index);
}
2.2、kindedit
# 先引用js
<script src="/static/kindeditor/kindeditor-all-min.js"></script>
<script charset="utf-8" src="/static/kindeditor/lang/zh-CN.js"></script>
2.2.1、上传文件
- 前台
<script>
KindEditor.ready(function (K) {
{#editor.sync();#}
window.editor = K.create('#editor_id', {
uploadJson: '/article_upload/', // 后端的url
extraFileUploadParams : {
// csrf处理
"csrfmiddlewaretoken": '{{ csrf_token }}',
},
allowFileManager: true,
resizeType: 1, // 不允许拖拉
});
});
</script>
- 后台
def article_upload(request):
if request.method == "POST":
abs_path = os.path.dirname(os.path.dirname(os.path.dirname(__file__)))
response = {"error": 1, "url": os.path.join(abs_path, "static", "image", "default.png")}
imgFile = request.FILES.get("imgFile")
path = os.path.join(abs_path, "media", "article")
if not os.path.isdir(path): os.mkdir(path)
filename = os.path.join(path, imgFile.name)
with open(filename, "wb") as f:
for line in imgFile.chunks():
f.write(line)
response["error"] = 0
response["url"] = "/media/article/{}".format(imgFile.name)
return JsonResponse(response)
2.2.2、标签处理
可参考:https://www.cnblogs.com/linhaifeng/articles/7783586.html
# 三步曲
一、安装 pip install beautifulsoup4
二、引用 from bs4 import BeautifulSoup
三、使用 xx = BeautifulSoup()
# 后台使用, 第一个参数是内容,第二个是要使用的解析器
content_bs = BeautifulSoup(content,"html.parser")
for bs in content_bs.find_all():
# 找着sciprt标签,给它删除
if bs.name == "script": bs.decompose()
FAQ
Django开发“ ‘X-Frame-Options’ to ‘deny’“报错处理
# settings.py中添加 原因分析:https://blog.csdn.net/ora_dy/article/details/104984482
X_FRAME_OPTIONS = 'SAMEORIGIN'