仿bbs项目之登录补充,首页,后台,个人站点
目录
仿bbs项目之登录补充,首页,后台,个人站点
今日内容概要
- 登录功能补充编写
- 首页导航条样式
- 修改密码与注销登录
- admin后台管理简介
- 后台数据绑定
- 首页样式搭建
- media媒体目录
- 图片防盗链技术
- 个人站点页面搭建
- 个人站点侧边栏展示
今日内容详细
登录功能补充编写
# 5.推导步骤5:编写验证码
# 先产生图片对象
img_obj = Image.new('RGB', (350, 35), get_random())
# 将图片对象交给画笔对象
draw_obj = ImageDraw.Draw(img_obj)
# 确定字体样式(ttf文件)
font_obj = ImageFont.truetype('static/font/111.ttf', 35)
# 产生一个随机验证码
code = ''
for i in range(5):
random_upper = chr(random.randint(65, 90))
random_lower = chr(random.randint(97, 122))
random_int = str(random.randint(1, 9))
# 三选一
temp_choice = random.choice([random_upper, random_lower, random_int])
# 写到图片上
draw_obj.text((i*60+45, 0), temp_choice, font=font_obj)
code += temp_choice
# 后端保存验证码 便于后续的比对
request.session['code'] = code
io_obj = BytesIO()
img_obj.save(io_obj, 'png')
return HttpResponse(io_obj.getvalue())

前端验证码动态刷新和发送ajax请求
<script>
// 1.验证码动态刷新
$('#d1').click(function () {
let oldSrc = $(this).attr('src');
$(this).attr('src', oldSrc + '?')
})
// 2,登录按钮发送ajax请求
$('#loginBtn').click(function () {
// 可以再次使用form标签序列化功能 也可以自己获取
{#console.log($('#myform').serializeArray()) // [{},{},{},{}] ajax不太好用form,所以不用了#}
$.ajax({
url:'',
type:'post',
data:{'username': $('#name').val(), 'password': $('#password').val(), 'code': $('#code').val(), 'csrfmiddlewaretoken': '{{ csrf_token }}'},
success: function (args) {
if(args.code === 10000){
window.location.href = args.url
}else {
// 课下可以使用sweetalert插件美化展示
{#alert(args.msg)#}
swal(args.msg, 'error')
}
}
})
})
</script>

后端接收数据并与前端交互
def login_func(request):
back_dict = {'code': 10000, 'msg': ''}
if request.method == 'POST':
username = request.POST.get('username')
password = request.POST.get('password')
code = request.POST.get('code')
if code.upper() == request.session.get('code').upper():
res = auth.authenticate(request, username=username, password=password)
if res:
back_dict['msg'] = '登录成功'
back_dict['url'] = '/home/'
else:
back_dict['msg'] = '用户名或密码错误'
back_dict['code'] = 10001
else:
back_dict['code'] = 10002
back_dict['msg'] = '验证码错误'
return JsonResponse(back_dict)
return render(request, 'loginPage.html')

首页导航条样式
1.复制导航条,并进行微调
{% if request.user.is_authenticated %} <!--is_authenticated判断当前用户是登录用户还是匿名用户,返回T or F-->
<li><a href="#">{{ request.user.username }}</a></li>
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">用户设置<span class="caret"></span></a>
<ul class="dropdown-menu">
<li><a href="#">修改密码</a></li>
<li><a href="#">修改头像</a></li>
<li><a href="#">后台管理</a></li>
<li role="separator" class="divider"></li>
<li><a href="#">退出登录</a></li>
</ul>
</li>
{% else %}
<li><a href="{% url 'login_view' %}">登录</a></li>
<li><a href="{% url 'register_view' %}">注册</a></li>
{% endif %}
2.登录方面补充完整,保存用户登录状态
auth.login(request, user_obj) # 执行之后就可以使用request.user获取登录用户对象
3.view简单返回页面,传入数据
def home_func(request):
return render(request, 'homePage.html', locals())

修改密码与注销登录
<li><a href="#" data-toggle="modal" data-target="#myModal">修改密码</a></li>
<li><a href="/logout/">退出登录</a></li>
<!--模态框开始-->
<!-- Button trigger modal -->
<!-- Modal -->
<div class="modal fade" id="myModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">
<div class="modal-dialog" 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" id="myModalLabel">修改密码</h4>
</div>
<div class="modal-body">
<div class="form-group">
<label for="">用户名</label>
<input type="text" value="{{ request.user.username }}" disabled class="form-control">
</div>
<div class="form-group">
<label for="">原密码</label>
<input type="password" class="form-control" id="old_pwd">
</div>
<div class="form-group">
<label for="">新密码</label>
<input type="password" class="form-control" id="new_pwd">
</div>
<div class="form-group">
<label for="">确认密码</label>
<input type="password" class="form-control" id="confirm_pwd">
</div>
</div>
<div class="modal-footer">
<span id="error" style="color: red"></span>
<button type="button" class="btn btn-default" data-dismiss="modal">取消</button>
<button type="button" class="btn btn-warning" id="setBtn">确认修改</button>
</div>
</div>
</div>
</div>
<!--模态框结束-->
<script>
$('#setBtn').click(function () {
$.ajax({
url:'/set_pwd/',
type:'post',
data: {
'old_pwd': $('#old_pwd').val(),
'new_pwd': $('#new_pwd').val(),
'confirm_pwd': $('#confirm_pwd').val(),
'csrfmiddlewaretoken': '{{ csrf_token }}',
},
success:function (args) {
if(args.code === 10000){
window.location.href = args.url
}else {
$('#error').text(args.msg)
}
}
})
})
</script>
@login_required
def set_pwd_func(request):
back_dict = {'code': 10000, 'msg': ''}
if request.method == 'POST':
old_pwd = request.POST.get('old_pwd')
new_pwd = request.POST.get('new_pwd')
confirm_pwd = request.POST.get('confirm_pwd')
# 先校验原密码是否正确
if request.user.check_password(old_pwd):
# 再校验两次密码是否一致并且不能为空
if new_pwd == confirm_pwd and new_pwd:
request.user.set_password(new_pwd)
request.user.save()
back_dict['msg'] = '密码修改成功'
back_dict['url'] = '/login/'
else:
back_dict['code'] = 10001
back_dict['msg'] = '两次密码不一致或者为空'
else:
back_dict['code'] = 10002
back_dict['msg'] = '原密码错误'
return JsonResponse(back_dict)
@login_required
def logout_func(request):
auth.logout(request)
return redirect('home_view')



admin后台管理简介
from django.contrib import admin
from app01 import models
# Register your models here.
"""只要注册了 admin就会产生针对该注册表的增删改查至少四个功能"""
admin.site.register(models.UserInfo)
admin.site.register(models.Site)
admin.site.register(models.Article)
admin.site.register(models.Category)
admin.site.register(models.Tag)
admin.site.register(models.Article2Tag)
admin.site.register(models.UpAndDown)
admin.site.register(models.Comment)

后台管理数据绑定
# 修改admin后台管理的表名
class Meta:
verbose_name_plural = '用户表'
def __str__(self):
return f'用户对象:{self.username}'
创建表的数据,不要忘记用户表的绑定
'''phone = models.BigIntegerField(verbose_name='手机号', null=True, blank=True) # blank=True用于控制后台管理与数据库无关'''


首页样式搭建
新建mypage.py内复制分页器代码
def home_func(request):
# 查询所有用户编写的文章
article_queryset = models.Article.objects.all()
'''文章过多的情况下应该考虑添加分页器'''
page_obj = myPage.Pagination(current_page=request.GET.get('page'), all_count=article_queryset.count())
page_queryset = article_queryset[page_obj.start:page_obj.end]
return render(request, 'homePage.html', locals())
---------------------------------------------------------------
<div class="col-md-8">
{% for article_obj in page_queryset %}
<div class="media">
<h4 class="media-heading"><a href="#">{{ article_obj.title }}</a></h4>
<div class="media-left">
<a href="#">
<img class="media-object" src="/static/img/default.jpg" alt="..." width="80">
</a>
</div>
<div class="media-body">
{{ article_obj.desc }}
</div>
<br>
<span><a href="#">{{ article_obj.site.userinfo.username }} </a></span>
<span>{{ article_obj.create_time|date:'Y-m-d H:i:s' }} </span>
<span class="glyphicon glyphicon-thumbs-up">{{ article_obj.up_num }} </span>
<span class="glyphicon glyphicon-thumbs-down">{{ article_obj.down_num }} </span>
<span class="glyphicon glyphicon-comment">{{ article_obj.comment_num }} </span>
</div>
<hr>
{% endfor %}
<div class="text-center">{{ page_obj.page_html|safe }}</div>



media媒体目录
# 以后用户上传的头像 媒体文件都会自动保存到该路径下
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
src="/media/{{ article_obj.site.userinfo.avatar }}/"
from django.views.static import serve
from django.conf import settings
# 自定义暴露资源接口
re_path('media/(?P<path>.*)', serve, {'document_root': settings.MEDIA_ROOT}),
'''可以暴露任何你想暴露的文件路径,慎重使用'''

图片防盗链技术
# 个人站点
path('<str:username>/', views.site_func, name='site_view')
def site_func(request, username):
# 查询个人站点是否存在
site_obj = models.Site.objects.filter(site_name=username)
if not site_obj:
return render(request, 'errorPage.html')
return HttpResponse(username)
'''动态匹配用户名的网址'''

个人站点页面搭建
{% extends 'homePage.html' %}
{% block css %}
<link rel="stylesheet" href="media/css/{{ site_obj.site_theme }}/">
{% endblock %}
{% block title %}
{{ site_obj.site_title }}
{% endblock %}
{% block myhtml %}
<div class="col-md-2">
<ul class="list-group">
<li class="list-group-item list-group-item-success">文章分类</li>
<li class="list-group-item list-group-item-info">文章标签</li>
<li class="list-group-item list-group-item-warning">日期归档</li>
<li class="list-group-item list-group-item-danger">联系电话:12345</li>
</ul>
<div class="list-group">
<a href="#" class="list-group-item list-group-item-success">阳痿求助</a>
<a href="#" class="list-group-item list-group-item-info">必有重谢</a>
<a href="#" class="list-group-item list-group-item-warning">联系人:ahong321</a>
<a href="#" class="list-group-item list-group-item-danger">联系电话:54321</a>
</div>
</div>
<div class="col-md-10">
{% for article_obj in article_queryset %}
<div class="media">
<h4 class="media-heading"><a href="#">{{ article_obj.title }}</a></h4>
<div class="media-body">
{{ article_obj.desc }}
</div>
<br>
<div class="pull-right">
<span>posted; @</span>
<span>{{ article_obj.create_time|date:'Y-m-d H:i:s' }} </span>
<span><a href="/{{ article_obj.site.userinfo.username }}/">{{ article_obj.site.userinfo.username }} </a></span>
<span class="glyphicon glyphicon-thumbs-up">{{ article_obj.up_num }} </span>
<span class="glyphicon glyphicon-thumbs-down">{{ article_obj.down_num }} </span>
<span class="glyphicon glyphicon-comment">{{ article_obj.comment_num }} </span>
</div>
</div>
{% endfor %}
<div class="text-center">{{ page_obj.page_html|safe }}</div>
</div>
{% endblock %}
# 个人站点
path('<str:username>/', views.site_func, name='site_view')
def site_func(request, username):
# 查询个人站点是否存在
site_obj = models.Site.objects.filter(site_name=username).first()
if not site_obj:
return render(request, 'errorPage.html')
# 查询个人站点下所有的文章
article_queryset = models.Article.objects.filter(site=site_obj)
'''文章过多的情况下应该考虑添加分页器'''
page_obj = myPage.Pagination(current_page=request.GET.get('page'), all_count=article_queryset.count())
page_queryset = article_queryset[page_obj.start:page_obj.end]
return render(request, 'sitePage.html', locals())

个人站点侧边栏展示
from django.db.models import Avg, Count, Sum, Min, Max
# 查询个人站点下所有的分类名称以及每个分类下的文章数
category_queryset = models.Category.objects.filter(site=site_obj).annotate(article_num=Count('article__pk')).values(
'name', 'article_num'
)
# print(category_queryset) # <QuerySet [{'name': 'tony的分类1', 'article_num': 1}, {'name': 'tony的分类2', 'article_num
# ': 2}, {'name': 'tony的分类3', 'article_num': 2}]>
# 查询个人站点下所有的标签名称以及每个标签下的文章数
tag_queryset = models.Tag.objects.filter(site=site_obj).annotate(article_num=Count('article__pk')).values(
'name', 'article_num'
)
# 年月分组并统计文章个数
from django.db.models.functions import TruncMonth
date_queryset = models.Article.objects.filter(site=site_obj).annotate(month=TruncMonth('create_time')).values('month').annotate(
article_num=Count('pk')).values('month', 'article_num')
<div class="col-md-2">
<div class="panel panel-primary">
<div class="panel-heading">
<h3 class="panel-title">文章分类</h3>
</div>
<div class="panel-body">
{% for category_obj in category_queryset %}
<p><a href="#">{{ category_obj.name }}({{ category_obj.article_num }})</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 tag_obj in tag_queryset %}
<p><a href="#">{{ tag_obj.name }}({{ tag_obj.article_num }})</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 date_obj in date_queryset %}
<p><a href="#">{{ date_obj.month|date:'Y年m月' }}({{ date_obj.article_num }})</a></p>
{% endfor %}
</div>
</div>
</div>



浙公网安备 33010602011771号