目录
1、随机验证码
url(r'^get_code/', views.get_code, name='get_code'), # 获取随机3个0-255数 def get_random(): """ :return: 返回0-255三个随机数,元组 """ return random.randint(0, 255), random.randint(0, 255), random.randint(0, 255) # 获取验证码 def get_code(request): # 1.产生一张随机颜色的图片 img_obj = Image.new('RGB', (350, 35), get_random()) # 2.产生一只在图片上的画笔 img_draw = ImageDraw.Draw(img_obj) # 3.产生字体样式 img_font = ImageFont.truetype(r'static\font\font.ttf', 35) io_obj = BytesIO() # 产生5个随机验证码 code = '' for i in range(5): upper_str = chr(random.randint(65, 90)) # 大写字母 lower_str = chr(random.randint(97, 122)) # 小写字母 random_int = str(random.randint(0, 9)) # 数字 # 随机取一个 temp_str = random.choice([upper_str, lower_str, random_int]) # 写在图片上,位置,内容,颜色,字体 img_draw.text((45 + i * 60, -2), temp_str, get_random(), font=img_font) # 储存 code += temp_str print(code) img_obj.save(io_obj, 'png') request.session['code'] = code return HttpResponse(io_obj.getvalue())
前端代码:
<img src="/get_code/" alt="图片验证码" id="id_img">
js代码:
原理:src改变,立马刷新。点击一次图片,给url添加一个?号
$('#id_img').click(function () { var oldSrc = $(this).attr('src'); $(this).attr('src', oldSrc += '?') });
2、注册功能
前端代码:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>注册</title> <script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script> <link rel="stylesheet" href="https://cdn.bootcss.com/font-awesome/4.7.0/css/font-awesome.min.css"> <script src="https://cdn.bootcss.com/sweetalert/2.0.0/sweetalert.min.js"></script> <link rel="https://cdn.bootcss.com/sweetalert/1.1.3/sweetalert.min.css"> {% load static %} <link rel="stylesheet" href="{% static '/bootstrap-3.3.7-dist/css/bootstrap.min.css'%}"> <script src="{% static 'bootstrap-3.3.7-dist/js/bootstrap.min.js' %}"></script> </head> <body> <div class="container"> <div class="row"> <h2 class="text-center">注册页面</h2> <div class="col-md-8 col-md-offset-2"> <form id="myform"> {% csrf_token %} {% for form in form_obj %} <div class="form-group"> <label for="{{ form.id_for_label }}">{{ form.label }}</label> {{ form }} <span style="color: red" class="pull-right"></span> </div> {% endfor %} <div class="form-group"> <label for="id_avatar">头像 <img src="/static/images/default.jpg" alt="" width="100" style="margin-left: 10px" id="id_img"> </label> <input type="file" name="myfile" id="id_avatar"> </div> <a href="/login/"><input type="button" value="登录" class="btn btn-success pull-left"></a> <input type="button" value="注册" class="btn btn-danger" id="id_submit"> </form> </div> </div> </div> <script> $('#id_avatar').change(function () { // 1 先获取用户上传的头像文件 var avatarFile = $(this)[0].files[0]; // 2 利用文件阅读器对象 var myFileReader = new FileReader(); // 3 将文件交由阅读器对象读取 myFileReader.readAsDataURL(avatarFile); // 4 修改img标签的src属性 等待文件阅读器对象读取文件之后再操作img标签 myFileReader.onload = function(){ $('#id_img').attr('src',myFileReader.result) } }); // 点击按钮触发ajax提交动作 $('#id_submit').on('click',function () { // 1 先生成一个内置对象 FormData var myFormData = new FormData(); // 2 添加普通键值对 {#console.log($('#myform').serializeArray())#} $.each($('#myform').serializeArray(),function (index,obj) { myFormData.append(obj.name,obj.value) }); // 3 添加文件数据 myFormData.append('avatar',$('#id_avatar')[0].files[0]); // 4 发送数据 $.ajax({ url:'', type:'post', data:myFormData, // 两个关键性参数 contentType:false, processData:false, success:function (data) { if (data.code===1000){ // 注册成功之后 应该跳转到后端返回过来的url location.href = data.url }else{ $.each(data.msg,function(index,obj){ // 1 先手动拼接字段名所对应的input框的id值 var targetId = '#id_' + index; // #id_username // 2 利用id选择器查找标签 并且将div标签添加报错类 $(targetId).next().text(obj[0]).parent().addClass('has-error') }) } } }) }); $('input').focus(function () { // 移除span标签内部的文本 还需要移除div标签的class中has-error属性 $(this).next().text('').parent().removeClass('has-error') }) </script> </body> </html>
后端urls.py:
# 注册 url(r'^register/', views.register, name='register'),
views.py:
# 注册 def register(request): form_obj = MyRegForm() if request.method == 'POST': back_dic = {'code': 1000, 'msg': ""} # 校验用户名、密码 form_obj = MyRegForm(request.POST) if form_obj.is_valid(): # 用变量接收正确的结果 clean_data = {'username' 'password' 're_password' 'email'} clean_data = form_obj.cleaned_data # 将确认密码键值对删除,表中没有re_password clean_data.pop('re_password') # 把签名、用户CSS名字存进Blog表中 sign = clean_data.get('sign') username = clean_data.get('username') site_theme = username + '.css' models.Blog.objects.create(site_name=username, site_title=sign, site_theme=site_theme) # 添加字段 clean_data['blog'] = models.Blog.objects.filter(site_name=username).first() clean_data.pop('sign') # 额外做的事情:给每个新的注册用户添加3个默认的分类和3个默认的标签 create_list = [] blog = models.Blog.objects.filter(site_name=username).first() for i in ['一', '二', '三']: category_name = username+'的分类'+i create_list.append(models.Category(name=category_name, blog=blog)) models.Category.objects.bulk_create(create_list) # 添加3个默认标签 create_list = [] for i in ['一', '二', '三']: tag_name = username + '的标签' + i create_list.append(models.Tag(name=tag_name, blog=blog)) models.Tag.objects.bulk_create(create_list) # 获取用户头像文件 avatar_obj = request.FILES.get('avatar') # 判断用户头像文件是否为空,用户没有上传 if avatar_obj: # 用户上传了,添加到clean_data中 clean_data['avatar'] = avatar_obj # clean_data = {'username' 'password' 'email' 'avatar'} models.UserInfo.objects.create_user(**clean_data) # 打散传入 ??=??的形式 back_dic['msg'] = '注册成功' back_dic['url'] = '/login/' else: back_dic['code'] = 2000 back_dic['msg'] = form_obj.errors return JsonResponse(back_dic) return render(request, 'register.html', locals())
myforms.py:
from django import forms from app01 import models class MyRegForm(forms.Form): username = forms.CharField(min_length=3,max_length=8,label='用户名', error_messages={ "min_length":'用户名最短3位', "max_length":'用户名最长8位', "required":'用户名不能为空', },widget=forms.widgets.TextInput(attrs={'class':'form-control'}) ) password = forms.CharField(min_length=3, max_length=8, label='密码', error_messages={ "min_length": '密码最短3位', "max_length": '密码最长8位', "required": '密码不能为空', }, widget=forms.widgets.PasswordInput(attrs={'class': 'form-control'}) ) re_password = forms.CharField(min_length=3, max_length=8, label='确认密码', error_messages={ "min_length": '确认密码最短3位', "max_length": '确认密码最长8位', "required": '确认密码不能为空', }, widget=forms.widgets.PasswordInput(attrs={'class': 'form-control'}) ) email = forms.EmailField(label='邮箱', error_messages={ "required": '邮箱不能为空', "invalid":"邮箱格式不正确" }, widget=forms.widgets.EmailInput(attrs={'class': 'form-control'}) ) sign = forms.CharField(min_length=5, max_length=15, label='学习宣言', error_messages={ "min_length": '学习宣言最短5位', "max_length": '学习宣言最长15位', "required": '学习宣言不能为空',} , widget=forms.widgets.TextInput(attrs={'class': 'form-control'}) ) # 钩子函数 # 局部钩子校验用户名是否已存在 def clean_username(self): username = self.cleaned_data.get('username') is_alive = models.UserInfo.objects.filter(username=username) if is_alive: self.add_error('username','用户名已存在') return username # 全局钩子校验密码与确认密码是否一致 def clean(self): password = self.cleaned_data.get('password') re_password = self.cleaned_data.get('re_password') if not password == re_password: self.add_error('re_password','两次密码不一致') return self.cleaned_data
3、登录功能
前端代码:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script> <link rel="stylesheet" href="https://cdn.bootcss.com/font-awesome/4.7.0/css/font-awesome.min.css"> <script src="https://cdn.bootcss.com/sweetalert/2.0.0/sweetalert.min.js"></script> <link rel="https://cdn.bootcss.com/sweetalert/1.1.3/sweetalert.min.css"> {% load static %} <link rel="stylesheet" href="{% static '/bootstrap-3.3.7-dist/css/bootstrap.min.css'%}"> <script src="{% static 'bootstrap-3.3.7-dist/js/bootstrap.min.js' %}"></script> </head> <body> <div class="container"> <h2 class="text-center">登录页面</h2> <div class="row"> <div class="col-md-8 col-md-offset-2"> <div class="form-group"> <label for="id_username">用户名</label> <input type="text" name="username" class="form-control" id="id_username"> </div> <div class="form-group"> <label for="id_password">密码</label> <input type="password" name="password" class="form-control" id="id_password"> </div> <div class="form-group"> <label for="id_code">验证码</label> <div class="row"> <div class="col-md-6"> <input type="text" name="code" class="form-control" id="id_code"> </div> <div class="col-md-6"> <img src="/get_code/" alt="图片验证码" id="id_img"> </div> </div> </div> <input type="button" value="登录" class="btn btn-success" id="id_submit"> <a href="/register/"><input type="button" value="注册" class="btn btn-danger"></a> <span style="color: red" id="error"></span> </div> </div> </div> <script> $('#id_img').click(function () { var oldSrc = $(this).attr('src'); $(this).attr('src', oldSrc += '?') }); $('#id_submit').click(function () { $.ajax({ url:'', type:'post', data:{ 'username': $('#id_username').val(), 'password': $('#id_password').val(), 'csrfmiddlewaretoken':'{{ csrf_token }}', 'code':$('#id_code').val() }, success:function (data) { if (data.code === 1000){ // 登录成功,跳转页面 location.href = data.url }else { // 点击此按钮,添加文本信息 $('#error').text(data.msg) } } }) }) </script> </body> </html>
后端代码:
urls.py:
# 登录 url(r'^login/', views.login, name='login'),
views.py:
# 登录 def login(request): back_dic = {'code': None, 'msg': None} if request.method == 'POST': username = request.POST.get('username') password = request.POST.get('password') code = request.POST.get('code') # 从前端拿过来的验证码 # 先对比验证码 if request.session.get('code').lower() == code.lower(): # 校验用户名和密码 user_obj = auth.authenticate(username=username, password=password) if user_obj: # 记录登录状态 auth.login(request, user_obj) back_dic['code'] = 1000 back_dic['msg'] = '登录成功' back_dic['url'] = '/home/' # back_dic['url'] = '/%s/' % username else: back_dic['code'] = 2000 back_dic['msg'] = '用户名或密码错误' else: back_dic['code'] = 3000 back_dic['msg'] = '验证码错误' return JsonResponse(back_dic) return render(request, 'login.html')
4、登录认证装饰器配置
settings.py:
LOGIN_URL = '/login/'
5、修改密码模态框方法
前端代码:
<li><a href="#" data-toggle="modal" data-target=".bs-example-modal-lg">修改密码</a></li> {#修改密码模态框#} <div class="modal fade bs-example-modal-lg" tabindex="-1" role="dialog" aria-labelledby="myLargeModalLabel"> <div class="modal-dialog modal-lg" role="document"> <div class="modal-content"> <h2 class="text-center">修改密码</h2> <div class="row"> <div class="col-md-8 col-md-offset-2"> <div class="form-group"> <label for="">用户名</label> <input type="text" name="username" value={{ request.user.username }} class="form-control" disabled> </div> <div class="form-group"> <label for="id_old_password">原密码</label> <input type="password" name="old_password" class="form-control" id="id_old_password"> </div> <div class="form-group"> <label for="id_new_password">新密码</label> <input type="password" name="new_password" class="form-control" id="id_new_password"> </div> <div class="form-group"> <label for="id_confirm_password">确认密码</label> <input type="password" name="confirm_password" class="form-control" id="id_confirm_password"> </div> <button class="btn btn-primary" id="id_set">修改</button> <button type="button" class="btn btn-default" data-dismiss="modal">取消</button> <span style="color: red"></span> </div> </div> <br> </div> </div> </div>
js代码:
<script> {#修改密码#} $('#id_set').click(function () { var $btn = $(this); $.ajax({ url: '{% url 'set_password' %}', type: 'post', data: { old_password: $('#id_old_password').val(), new_password: $('#id_new_password').val(), confirm_password: $('#id_confirm_password').val(), csrfmiddlewaretoken: '{{ csrf_token }}' }, success: function (data) { if (data.code === 1000) { location.href = data.url } else { $btn.next().next().text(data.msg) } } }) }); </script>
后端代码:
urls.py:
# 修改密码 url(r'^set_password', views.set_password, name='set_password'),
views.py:
# 修改密码 @login_required def set_password(request): if request.is_ajax(): back_dic = {'code': 1000, 'msg': ''} old_password = request.POST.get('old_password') new_password = request.POST.get('new_password') confirm_password = request.POST.get('confirm_password') if new_password == confirm_password: is_right = request.user.check_password(old_password) if is_right: request.user.set_password(new_password) request.user.save() back_dic['msg'] = '修改成功' back_dic['url'] = reverse('login') else: back_dic['code'] = 2000 back_dic['msg'] = '原密码错误' else: back_dic['code'] = 3000 back_dic['msg'] = '两次密码不一致' return JsonResponse(back_dic)
6、修改头像
前端代码:
<li><a href="/set_avatar/">修改头像</a></li>
stt_avatar.html:
{% extends 'base.html' %} {% block content %} <form action="" method="post" enctype="multipart/form-data"> <p><a href="/home/">返回</a></p> <input type="file" name="myfile" id="id_avatar"><br> {% csrf_token %} <div class="form-group"> <label for="id_avatar"> <img src="/static/images/default.jpg" alt="" width="100" id="id_img"> <span>新头像</span> </label> </div> <label for="id_avatar"> <img src="/media/{{ request.user.avatar }}/" width="200" alt="原头像"> <span>原头像</span> </label> <p><input type="submit" class="btn btn-primary"></p> </form> <script> $('#id_avatar').change(function () { // 1 先获取用户上传的头像文件 var avatarFile = $(this)[0].files[0]; // 2 利用文件阅读器对象 var myFileReader = new FileReader(); // 3 将文件交由阅读器对象读取 myFileReader.readAsDataURL(avatarFile); // 4 修改img标签的src属性 等待文件阅读器对象读取文件之后再操作img标签 myFileReader.onload = function () { $('#id_img').attr('src', myFileReader.result) } }); </script> {% endblock %}
后端代码:
urls.py:
# 修改用户头像 url(r'^set_avatar/', views.set_avatar, name='set_avatar'),
views.py:
# 修改头像 @ login_required def set_avatar(request): if request.method == 'POST': avatar_obj = request.FILES.get('myfile') # models.UserInfo.objects.filter(pk=request.user.pk).update(avatar=avatar_obj) # 不会帮你自动添加前缀 # 用自己的save方法,自动帮你添加前缀 request.user.avatar = avatar_obj request.user.save() return render(request, 'set_avatar.html')
7、修改签名模态框方法
前端代码:
<li><a href="#" data-toggle="modal" data-target=".set_sign">编辑签名</a></li> {#编辑签名模态框#} <div class="modal fade bs-example-modal-lg set_sign" id="set_sign" tabindex="-1" role="dialog" aria-labelledby="myLargeModalLabel"> <div class="modal-dialog modal-lg" role="document"> <div class="modal-content"> <h2 class="text-center">编辑签名</h2> <div class="row"> <div class="col-md-8 col-md-offset-2"> <div class="form-group"> <label for="">用户名</label> <input type="text" name="username" value={{ request.user.username }} class="form-control" disabled> </div> <div class="form-group"> <label for="id_old_sign">原签名</label><br> <textarea name="old_sign" id="id_old_sign" cols="81" rows="5" disabled>{{ request.user.blog.site_title }}</textarea> </div> <div class="form-group"> <label for="id_new_sign">新签名</label> <span style="color: red" id="error_sign"></span> <textarea name="new_sign" id="id_new_sign" cols="81" rows="5"></textarea> </div> <button class="btn btn-primary" id="set_sign">修改</button> <button type="button" class="btn btn-default" data-dismiss="modal">取消</button> </div> </div> <br> </div> </div> </div>
js代码:
{#修改签名#} $("#set_sign").click(function () { $.ajax({ url: '{% url 'set_sign' %}', type: 'post', data: { new_sign: $('#id_new_sign').val(), csrfmiddlewaretoken: '{{ csrf_token }}' }, success: function (data) { if (data.code === 1000) { location.href = data.url; $('#error_sign').text(data.msg) } else { $('#error_sign').text(data.msg) } } }) });
后端代码:
urls.py:
# 编辑签名 url(r'^set_sign', views.set_sign, name='set_sign'),
views.py:
# 编辑签名 @login_required def set_sign(request): if request.is_ajax(): back_dic = {'code': 1000, 'msg': ''} site_name = request.user.username site_title = request.POST.get('new_sign') if len(site_title) < 5: back_dic['code'] = 2000 back_dic['msg'] = '(你的学习宣言必须大于5位)' elif len(site_title) > 15: back_dic['code'] = 3000 back_dic['msg'] = '(你的学习宣言必须小于于15位)' else: back_dic['code'] = 1000 back_dic['url'] = reverse('home') back_dic['msg'] = '修改成功' models.Blog.objects.filter(site_name=site_name).update(site_title=site_title) return JsonResponse(back_dic)
8、注销功能模态框
前端代码:
<li><a href="#" data-toggle="modal" data-target=".bs-example-modal-sm">注销</a></li> {# 退出确认模态框#} <div class="modal fade bs-example-modal-sm" tabindex="-1" role="dialog"> <div class="modal-dialog modal-sm" role="document"> <div class="modal-content"> <div class="modal-header"> <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button> <h4 class="modal-title">你忍心离开我吗</h4> </div> <div class="modal-body"> <p>确定退出?</p> </div> <div class="modal-footer"> <button type="button" class="btn btn-default" data-dismiss="modal">关闭</button> <button type="button" class="btn btn-primary" id="exit">确定</button> </div> </div><!-- /.modal-content --> </div> </div>
js代码:
{#注销#} $('#exit').click(function () { location.href = "/logout/" });
后端代码:
urls.py:
# 注销 url(r'^logout', views.logout, name='logout'),
views.py:
# 注销 @login_required def logout(request): auth.logout(request) # 原理删除了对应的session值 return redirect(reverse('home'))
9、用户上传静态文件配置
setting.py文件配置:
配置好之后,文件夹自动创建
# media配置 MEDIA_ROOT = os.path.join(BASE_DIR, 'media') # 用户上传的文件全部保存该文件下
10、图片防盗链
请求头里面有一个referer请求头,用来标识你上一次是从哪一个网址过来的
判断上一次这个网址是否有权限
自己的项目:把图片所在的文件夹暴露,那么只能访问图片。
别人的图片怎么解决防盗链?:
1.用爬虫将所有的图片资源下载到本地 这是爬虫的价值所在 2.修改请求头参数 百度搜吧
referer属性:
11、暴露任意文件的配置
urls.py:
注意:千万不要暴露重要文件资源,否则拍屁股走人
MEDIA_ROOT,一定不要暴露关键文件
from django.views.static import serve # 暴露任意后端资源配置 url(r'^media/(?P<path>.*)', serve, {"document_root": settings.MEDIA_ROOT}),
图片文件地址:
<img class="media-object" src="/media/{{ article_obj.blog.userinfo.avatar }}" style="width: 60px" alt="这是你的头像" width="60px;">
12、分页器的使用
分页器:新建py文件,把代码复制过来
代码:
class Pagination(object): def __init__(self,current_page,all_count,per_page_num=2,pager_count=11): """ 封装分页相关数据 :param current_page: 当前页 :param all_count: 数据库中的数据总条数 :param per_page_num: 每页显示的数据条数 :param pager_count: 最多显示的页码个数 用法: queryset = model.objects.all() page_obj = Pagination(current_page,all_count) page_data = queryset[page_obj.start:page_obj.end] 获取数据用page_data而不再使用原始的queryset 获取前端分页样式用page_obj.page_html """ try: current_page = int(current_page) except Exception as e: current_page = 1 if current_page <1: current_page = 1 self.current_page = current_page self.all_count = all_count self.per_page_num = per_page_num # 总页码 all_pager, tmp = divmod(all_count, per_page_num) if tmp: all_pager += 1 self.all_pager = all_pager self.pager_count = pager_count self.pager_count_half = int((pager_count - 1) / 2) @property def start(self): return (self.current_page - 1) * self.per_page_num @property def end(self): return self.current_page * self.per_page_num def page_html(self): # 如果总页码 < 11个: if self.all_pager <= self.pager_count: pager_start = 1 pager_end = self.all_pager + 1 # 总页码 > 11 else: # 当前页如果<=页面上最多显示11/2个页码 if self.current_page <= self.pager_count_half: pager_start = 1 pager_end = self.pager_count + 1 # 当前页大于5 else: # 页码翻到最后 if (self.current_page + self.pager_count_half) > self.all_pager: pager_end = self.all_pager + 1 pager_start = self.all_pager - self.pager_count + 1 else: pager_start = self.current_page - self.pager_count_half pager_end = self.current_page + self.pager_count_half + 1 page_html_list = [] # 添加前面的nav和ul标签 page_html_list.append(''' <nav aria-label='Page navigation>' <ul class='pagination'> ''') first_page = '<li><a href="?page=%s">首页</a></li>' % (1) page_html_list.append(first_page) if self.current_page <= 1: prev_page = '<li class="disabled"><a href="#">上一页</a></li>' else: prev_page = '<li><a href="?page=%s">上一页</a></li>' % (self.current_page - 1,) page_html_list.append(prev_page) for i in range(pager_start, pager_end): if i == self.current_page: temp = '<li class="active"><a href="?page=%s">%s</a></li>' % (i, i,) else: temp = '<li><a href="?page=%s">%s</a></li>' % (i, i,) page_html_list.append(temp) if self.current_page >= self.all_pager: next_page = '<li class="disabled"><a href="#">下一页</a></li>' else: next_page = '<li><a href="?page=%s">下一页</a></li>' % (self.current_page + 1,) page_html_list.append(next_page) last_page = '<li><a href="?page=%s">尾页</a></li>' % (self.all_pager,) page_html_list.append(last_page) # 尾部添加标签 page_html_list.append(''' </nav> </ul> ''') return ''.join(page_html_list)
使用方法:
后端代码:
from app01.utils.mypagenation import Pagination # 分页器导 # 首页 def home(request): # 将网站的所有文章展示到前端 article_list = models.Article.objects.all() # 分页处理 page_obj = Pagination(current_page=request.GET.get('page',1),all_count=article_list.count()) article_list = article_list[page_obj.start:page_obj.end] return render(request, 'home.html', locals())
前端代码:
{#分页器 #} <div class="text-center">{{ page_obj.page_html|safe }}</div>
13、每个用户拥有自己的css
1.在注册的时候把用户的css文件的名字固定写好,写进数据库
2.在用户编辑CSS的时候,再通过文件操作,创建用户固定的CSS文件
3.再应用导入自己的CSS文件
{#引用自己的css#} <link rel="stylesheet" href="/media/css/{{ user_obj.blog.site_theme }}">
前端代码:
<a href="{% url 'blog_css' %}">个人站点CSS设置</a>
urls.py:
# 个人站点CSS设置 url(r'^blog_css/', views.blog_css, name='blog_css'),
views.py:
# 个人站点CSS设置 @login_required def blog_css(request): username = request.user.username site_theme = models.Blog.objects.filter(site_name=username).first().site_theme css_dir = f'media/css/{site_theme}' if request.method == 'POST': new_css = request.POST.get('new_css') with open(css_dir, 'w', encoding='utf-8')as f: for line in new_css: res = line.replace('\n', '') f.write(res) f.close() return redirect('/blog_css/') if request.method == 'GET': # 先判断用户css文件是否存在,不存在就创建。存在就读取 isfile = os.path.exists(css_dir) # 不存在创建空的css文件 if not isfile: with open(css_dir, 'w')as f: f.close() # 存在就读取 with open(css_dir, 'r')as f: old_css = f.read() return render(request, 'backend/blog_css.html', locals())
backend/blog_css.html:
{% extends 'backend/backend_base.html' %} {% block article %} <form action="" method="post"> {% csrf_token %} <h2>页面定制 CSS 代码</h2> <textarea name="new_css" id="" cols="100" rows="30">{{ old_css }}</textarea> <p>推荐客户端: <a href="">Open Live Writer</a></p> <p>MetaWeblog访问地址: <a href="http://127.0.0.1:8000/home">http://127.0.0.1:8000/home</a>/</p> <input type="submit" class="btn btn-primary"> </form> {% endblock %}
14、分组,按年月等。官方推荐
-官方提供
from django.db.models.functions import TruncMonth
Article.objects
.annotate(month=TruncMonth('timestamp')) # Truncate to month and add to select list
.values('month') # Group By month
.annotate(c=Count('id')) # Select the count of the grouping
.values('month', 'c') # (might be redundant, haven't tested) select month and count
# 3.按照文章的年月分组 date_list = models.Article.objects.filter(blog=blog).\ annotate(month=TruncMonth('create_time')).values( 'month').annotate(c=Count('pk')).values('c', 'month')
{% for date in date_list %} <p> <a href="/{{ username }}/archive/{{ date.month|date:'Y-m' }}/"> {{ date.month|date:'Y年m月' }}({{ date.c }})</a> </p> {% endfor %}
15、侧边栏筛选(自定义过滤器方法)
新建文件夹和py文件:
mytag.py代码:
from django.template import Library from app01 import models from django.db.models import Count from django.db.models.functions import TruncMonth register = Library() # 侧边栏渲染,自定义过滤器方法 @register.inclusion_tag('left_menu.html', name='my_left') def index(username): # 提供left_menu所需要的所有数据 user_obj = models.UserInfo.objects.filter(username=username).first() blog = user_obj.blog # 1.查询当前用户的分类及每个分类下的文章数 category_list = models.Category.objects.all().filter(blog=blog).annotate(article_sum=Count('article__pk')).values( 'article_sum', 'name', 'pk') # 2.查询当前用户的标签,及每个标签下的文章数 tag_list = models.Tag.objects.all().filter(blog=blog).annotate(tag_sum=Count('article__pk')).values('tag_sum', 'name', 'pk') # 3.按照文章的年月分组 date_list = models.Article.objects.filter(blog=blog).\ annotate(month=TruncMonth('create_time')).values( 'month').annotate(c=Count('pk')).values('c', 'month') return locals()
left_menu.html:
<div class="panel panel-primary"> <div class="panel-heading"> <h3 class="panel-title">文章分类</h3> </div> <div class="panel-body"> {% for category in category_list %} <p><a href="/{{ username }}/category/{{ category.pk }}">{{ category.name }}({{ category.article_sum }})</a> </p> {% endfor %} </div> </div> <div class="panel panel-danger"> <div class="panel-heading"> <h3 class="panel-title">文章标签</h3> </div> <div class="panel-body"> {% for tag in tag_list %} <p><a href="/{{ username }}/tag/{{ tag.pk }}">{{ tag.name }}({{ tag.tag_sum }})</a></p> {% endfor %} </div> </div> <div class="panel panel-warning"> <div class="panel-heading"> <h3 class="panel-title">日期归档</h3> </div> <div class="panel-body"> {% for date in date_list %} <p> <a href="/{{ username }}/archive/{{ date.month|date:'Y-m' }}/"> {{ date.month|date:'Y年m月' }}({{ date.c }})</a> </p> {% endfor %} </div> </div>
16、点赞点踩
前端样式:可以直接去别人网站拷贝html代码,改改自己用
{# 点赞点踩前端样式#} <div class="clearfix"> <div id="div_digg"> <div class="diggit jeff"> <span class="diggnum" id="digg_count">{{ article_obj.up_num }}</span> </div> <div class="buryit jeff"> <span class="burynum" id="bury_count">{{ article_obj.down_num }}</span> </div> <div class="clear"></div> <div class="diggword" id="digg_tips" style="color: red"> </div> </div> </div>
js代码:
<script> {#点赞点踩JS代码#} $('.jeff').click(function () { var $divEle = $(this); $.ajax({ url: '{% url 'updown' %}', type: 'post', data: { 'article_id':{{ article_obj.pk }}, 'is_up': $(this).hasClass('diggit'), 'csrfmiddlewaretoken': '{{ csrf_token }}' }, success: function (data) { if (data.code === 1000) { $('#digg_tips').text(data.msg); $divEle.children().text(Number($divEle.children().text()) + 1) } else { $('#digg_tips').html(data.msg) } } }) }); </script>
后端:
urls.py:
# 点赞点踩 url(r'^up_or_down/', views.up_or_down, name='updown'),
views.py:
# 点赞点踩 import json from django.contrib import auth from django.db.models import F def up_or_down(request): bank_dic = {'code': 1000, 'msg': ''} if request.is_ajax(): article_id = request.POST.get('article_id') # 注意:前端返回来的bool值是str形式。拿到是点赞还是点踩 赞True 踩false is_up = request.POST.get('is_up') is_up = json.loads(is_up) # 转成python形式的bool值 ''' 1.必须是登录的用户才能点赞点踩,判断用户是否登录 2.判断当前文章是否是用户自己写的,自己不能给自己点赞点踩 3.当前用户是否已经给文章点过赞或踩了 4.操作数据库---操作两张表,优化表字段 ''' # 1.判断用户是否已登录 if request.user.is_authenticated(): # 2.拿到当前文章,从文章里拿到当前用户,和登录的用户比较。如果用户一样,则证明是自己写的文章,不能点赞踩 article_obj = models.Article.objects.filter(pk=article_id).first() if not article_obj.blog.userinfo.pk == request.user.pk: # 3.判断当前用户是否已经给当前文章点过赞或踩了。到点赞点踩表中查询是否有当前用户的记录,如果有,则证明当前用户已经点过了 is_click = models.UpAndDown.objects.filter(user=request.user.pk, article=article_id) if not is_click: # 用户没电点过,操作表数据.第一张表 # 点赞给点赞字段+1 if is_up: models.Article.objects.filter(pk=article_id).update(up_num=F('up_num') + 1) bank_dic['msg'] = '点赞成功' # 点踩给点踩字段+1 else: models.Article.objects.filter(pk=article_id).update(down_num=F('down_num') + 1) bank_dic['msg'] = '点踩成功' # 操作表数据,第二张表 models.UpAndDown.objects.create(user=request.user, article=article_obj, is_up=is_up) else: bank_dic['code'] = 2000 bank_dic['msg'] = '你已经点过了' else: bank_dic['code'] = 3000 bank_dic['msg'] = '不能给自己点' else: bank_dic['code'] = 4000 bank_dic['msg'] = '请先<a href="/login/">登录</a>' return JsonResponse(bank_dic)
17、模板字符串
文章评论零时渲染:
//定义全局变量 var parentId = null; // 文章评论js代码 $('#id_comment').click(function () { var conTent = $('#id_content').val(); // 如果是根评论不处理,如果是子评论需要处理,将@jeff 切割 // @jeff 萨尔 if (parentId) { //切割方式 获取第一个\n对应的索引 var indexN = conTent.indexOf('\n') + 1 //顾头不顾尾 // 按照获取的索引切割 conTent = conTent.slice(indexN) //将indexN之前的全部切除,中保留之后的 } $.ajax({ url: '{% url "comment" %}', type: 'post', data: { "article_id":{{ article_obj.pk }}, "content": conTent, "csrfmiddlewaretoken": '{{ csrf_token }}', "parent_id": parentId }, success: function (data) { if (data.code === 1000) { // 临时渲染评论内容 var UserName = '{{ request.user.username }}'; var conTent = $('#id_content').val(); // 将内容临时渲染到ul标签内 var temp = ` <li class="list-group-item"> <span><span class="glyphicon glyphicon-comment"></span><a href="/${UserName}/">${UserName}</a></span> <div> ${conTent} </div> </li> `; $('.list-group').append(temp); // 将获取用户输入评论的内容框清空 $('#id_content').val(''); // 将全局的parentId清空,否则parentId后续一直有值,就一直是子评论 parentId = null } } }) });
18、KindEditor编辑器使用
看官方文档
前端内容:
{% extends 'backend/backend_base.html' %} {% block article %} <h2>添加文章</h2> <form action="" method="post"> {% csrf_token %} <p>标题</p> <p> <input type="text" name="title" class="form-control"> </p> <p>内容(使用kindeditor编辑器)</p> <p> <textarea name="content" id="id_content" cols="60" rows="20"></textarea> </p> <div> <p>文章标签</p> <p> {% for tag in tag_list %} {{ tag.name }} <input type="checkbox" name="tag" value="{{ tag.pk }}"> {% endfor %} </p> </div> <div> <p>文章分类</p> <p> {% for category in category_list %} {{ category.name }} <input type="radio" name="category" value="{{ category.pk }}"> {% endfor %} </p> </div> <input type="submit" class="btn btn-primary" value="添加"> </form> <script charset="utf-8" src="/static/kindeditor/kindeditor-all-min.js"></script> <script> KindEditor.ready(function(K) { window.editor = K.create('#id_content',{ width:'100%', height:'500px', resizeType:0, uploadJson : '/upload_image/', //控制用户写文章上传文件的后端地址 extraFileUploadParams : { 'csrfmiddlewaretoken':'{{ csrf_token }}', } }); }); </script> {% endblock %}
后端代码:
urls.py:
# 添加文章 url(r'^add_article/', views.add_article, name='add_article'),
views.py:
# 添加随笔 from bs4 import BeautifulSoup @login_required def add_article(request): if request.method == 'POST': # 获取从前端页面传来的文章数据 title = request.POST.get('title') content = request.POST.get('content') tag_list = request.POST.get('tag') category_id = request.POST.get('category') # 先生成一个该模块beautifulsoup4的对象 soup = BeautifulSoup(content, 'html.parser') for tag in soup.find_all(): # 筛选除script标签直接删除,避免XSS攻击 if tag.name == 'script': tag.decompose() # 删除该标签 # desc = content[0:150] # 截取文章简介,错误示范。会从html代码截取 desc = soup.text[0:150] # 通过模块处理,直接从内容截取 # 写入数据 article_obj = models.Article.objects.create(title=title, desc=desc, content=str(soup), category_id=category_id, blog=request.user.blog) # 手动操作文章与标签的第三张表 # 用批量插入数据 bulk_create b_list = [] for tag_id in tag_list: b_list.append(models.Article2Tag(article=article_obj, tag_id=tag_id)) models.Article2Tag.objects.bulk_create(b_list) return redirect(reverse('backend')) # 获取文章分类、文章标签列表,让用户选择添加文章的分类与标签 category_list = models.Category.objects.filter(blog=request.user.blog) tag_list = models.Tag.objects.filter(blog=request.user.blog) return render(request, 'backend/add_article.html', locals())
来源:https://www.cnblogs.com/guyouyin123/p/12293758.html