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>
&nbsp;&nbsp;<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">&nbsp;<span>新头像</span>
</label>
</div>
<label for="id_avatar">
<img src="/media/{{ request.user.avatar }}/" width="200" alt="原头像">&nbsp;<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">&times;</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())

19、Django时区及国际化设置

LANGUAGE_CODE = 'zh-hans'   # 更改国际化翻译,中文

TIME_ZONE = 'Asia/Shanghai'  # 更改东八区时间

USE_TZ = False  #  表示数据库的同步时间,使用上面的东八区时间

20、models.py代码

from django.db import models
from django.contrib.auth.models import AbstractUser
# Create your models here. # 用户表
class UserInfo(AbstractUser):
phone = models.BigIntegerField(null=True, blank=True) # blank=True 告诉后台管理该字段可以为空
# 存用户头像的地址
avatar = models.FileField(upload_to='avatar/', default='avatar/default.jpg')
create_time = models.DateField(auto_now_add=True) # 创建时间自动 # 一个用户只能有一个站点,一个站点给一个用户用。一对一
blog = models.OneToOneField(to='Blog', null=True) # admin后台管理页面展示的表名
class Meta:
verbose_name_plural = '用户表' # 给前端便于展示
def __str__(self):
return self.username # 个人站点表
class Blog(models.Model):
site_name = models.CharField(max_length=32)
site_title = models.CharField(max_length=64)
site_theme = models.CharField(max_length=64) # 站点样式 class Meta:
verbose_name_plural = '个人站点表' # 便于前端展示
def __str__(self):
return self.site_name # 分类表
class Category(models.Model):
name = models.CharField(max_length=32) # 一个站点有多个类,一个类中只有一个站点。一对多,外键在多的一张表中
blog = models.ForeignKey(to='Blog', null=True) class Meta:
verbose_name_plural = '分类表' def __str__(self):
return self.name # 标签表
class Tag(models.Model):
name = models.CharField(max_length=32) # 标签名
blog = models.ForeignKey(to='Blog', null=True) # 一对多,外键在多的一张表中 class Meta:
verbose_name_plural = '标签表' def __str__(self):
return self.name # 文章表
class Article(models.Model):
title = models.CharField(max_length=64) # 标题
desc = models.CharField(max_length=254) # 摘要
content = models.TextField() # 文章内容
create_time = models.DateTimeField('Edit the date', auto_now_add=True) # 创建时间自动 # 这里考虑到复杂度,我们使用简单的版本。一篇文章只能分到一个类中。一对多的关系
# 一对多,分类字段
category = models.ForeignKey(to='Category', null=True)
# 多对多,标签字段
tags = models.ManyToManyField(to='Tag', through='Article2Tag', through_fields=('article', 'tag'))
# 一对多,个人站点字段。一个站点拥有多篇文章,一个文章只有一个站点
blog = models.ForeignKey(to='Blog', null=True) # 数据库优化设计,把这三个字段加到文章表中,时时更新就行了。
# 不用再到下面这几张表中查询,减少了查询的次数
comment_num = models.BigIntegerField(null=True, default=0) # 评论人数
up_num = models.BigIntegerField(null=True, default=0) # 点赞人数
down_num = models.BigIntegerField(null=True, default=0) # 点踩人数 class Meta:
verbose_name_plural = '文章表' def __str__(self):
return self.title # 文章和标签的第三张关系表
class Article2Tag(models.Model):
article = models.ForeignKey(to='Article')
tag = models.ForeignKey(to='Tag') class Meta:
verbose_name_plural = '文章标签多对多关系表' # 点赞点踩表
class UpAndDown(models.Model):
user = models.ForeignKey(to='UserInfo') # 放用户名
article = models.ForeignKey(to='Article') # 放文章
is_up = models.BooleanField() # 标识赞或踩,0,1 class Meta:
verbose_name_plural = '点赞点踩表' # 评论表
class Comment(models.Model):
user = models.ForeignKey(to='UserInfo')
article = models.ForeignKey(to='Article') # 放文章
content = models.CharField(max_length=254)
comment_time = models.DateTimeField('Edit the date', auto_now_add=True) # 评论时间
# 该字段存的是父评论的主键值
# 如果有值 说明当前评论是子评论 如果没有值 说明当前评论是根评论
parent = models.ForeignKey(to='self', null=True) class Meta:
verbose_name_plural = '评论表'

21、urls.py代码

"""BBS URL Configuration

The `urlpatterns` list routes URLs to views. For more information please see:
https://docs.djangoproject.com/en/1.11/topics/http/urls/
Examples:
Function views
1. Add an import: from my_app import views
2. Add a URL to urlpatterns: url(r'^$', views.home, name='home')
Class-based views
1. Add an import: from other_app.views import Home
2. Add a URL to urlpatterns: url(r'^$', Home.as_view(), name='home')
Including another URLconf
1. Import the include() function: from django.conf.urls import url, include
2. Add a URL to urlpatterns: url(r'^blog/', include('blog.urls'))
"""
from django.conf.urls import url
from django.contrib import admin
from app01 import views
from django.views.static import serve
from BBS import settings
urlpatterns = [
url(r'^admin/', admin.site.urls),
# 注册
url(r'^register/', views.register, name='register'),
# 登录
url(r'^login/', views.login, name='login'),
# 获取图片验证码
url(r'^get_code/', views.get_code, name='get_code'),
# 首页
url(r'^home/', views.home, name='home'),
url(r'^$', views.home, name='home'),
# 修改密码
url(r'^set_password', views.set_password, name='set_password'),
# 编辑签名
url(r'^set_sign', views.set_sign, name='set_sign'),
# 修改用户头像
url(r'^set_avatar/', views.set_avatar, name='set_avatar'),
# 注销
url(r'^logout', views.logout, name='logout'), # 暴露任意后端资源配置
url(r'^media/(?P<path>.*)', serve, {"document_root": settings.MEDIA_ROOT}), # 点赞点踩
url(r'^up_or_down/', views.up_or_down, name='updown'), # 文章评论
url(r'^comment/', views.comment, name='comment'), # 后台管理
url(r'^backend/', views.backend, name='backend'),
# 添加文章
url(r'^add_article/', views.add_article, name='add_article'),
# 编辑分类
url(r'^edit_category/', views.edit_category, name='edit_category'),
# 编辑标签
url(r'^edit_tag/', views.edit_tag, name='edit_tag'),
# 添加分类
url(r'^create_category/', views.create_category, name='create_category'),
# 添加标签
url(r'^create_tag/', views.create_tag, name='create_tag'),
# 删除分类
url(r'^delete_category/(?P<del_id>\d+)/', views.delete_category, name='delete_category'),
# 删除标签
url(r'^delete_tag/(?P<del_id>\d+)/', views.delete_tag, name='delete_tag'),
# 编辑分类名
url(r'^edit_category_name/(?P<edit_id>\d+)/', views.edit_category_name, name='edit_category_name'),
# 编辑标签名
url(r'^edit_tag_name/(?P<edit_id>\d+)/', views.edit_tag_name, name='edit_tag_name'),
# 个人站点CSS设置
url(r'^blog_css/', views.blog_css, name='blog_css'), # 编辑器上传图片
url(r'^upload_image/', views.upload_image, name='upload_image'), # 个人站点
url(r'^(?P<username>\w+)/$', views.site, name='site'), # 侧边栏筛选功能
# url(r'^(?P<username>\w+)/category/(\d+)', views.site),
# url(r'^(?P<username>\w+)/tag/(\d+)', views.site),
# url(r'^(?P<username>\w+)/archive/(.*)', views.site),
# 合并3条url
url(r'^(?P<username>\w+)/(?P<condition>category|tag|archive)/(?P<param>.*)/', views.site), # 文章详情页
url(r'^(?P<username>\w+)/article/(?P<article_id>\d+)/', views.article_detail)
]

22、mytag代码

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()

最新文章

  1. Raspberry Pi 3 --- GPIO control
  2. java调用mysql服务做备份与恢复
  3. Struts2学习笔记
  4. JDBC连接MySQL数据库的方法和实例
  5. Maven-010-maven 编译报错:Failure to ... in ... was cached in the local repository, resolution will not be reattempted until the update interval of nexus has elapsed or updates are forced.
  6. 关于jsonp跨域过程中 cookie中的值一直为null的原因
  7. Android实战技巧:深入解析AsyncTask
  8. JS阻止鼠标滚动
  9. [转载] 高流量大并发Linux TCP 性能调优
  10. GDI+ 绘图闪烁解决方法
  11. fscanf(格式化字符串输入)
  12. iOS开发--混编篇&amp;swift与OC混合使用
  13. DataGridView的DataGridViewComboBoxColumn列点击一次,自动处于编辑状态
  14. 在html中写python代码的语法和特点-----基于webpy的httpserver
  15. Ubuntu基本命令--apt, dpkg
  16. CSS display和visibility的用法和区别
  17. Animations in UWP Community Toolkit - Overview
  18. 开源litemall学习
  19. hbase参数配置和说明
  20. python json读取与解析

热门文章

  1. 洛谷$P$2286 宠物收养场 $[HNOI2004]$ $splay$
  2. Maven 生命周期的概念(指令默认顺序执行)
  3. ACWing 248. 窗内的星星|扫描线+懒惰标记
  4. 【转】20个简化开发任务的 JavaScript库
  5. Epplus Excel 导入 MSSQL 数据库
  6. kuangbin专题 专题九 连通图 Strongly connected HDU - 4635
  7. VMware Workstation CentOS7 Linux 学习之路(3)--.net coreWeb部署
  8. Office系列(2)---提取Office文件(Word、PPT)中的所有图片
  9. eclipse git 解决冲突 解决 mergetool 不能使用问题
  10. Java中的equalsIgnoreCase()