BBS-博客首页
目录
重点功能:
- 修改密码弹出模态框
- 展示出所有文章
- 文章旁边可以显示作者头像
一、添加路由
在urls.py中添加,
导入模块
from django.conf.urls import url
from django.contrib import admin
from app01 import views
from django.views.static import serve
from django.conf import settings
1、首页路由
# 首页相关
url(r'^home/$', views.home), # 首页路由
2、修改密码路由
url(r'^set_password/$', views.set_password), # 修改密码路由
3、退出登录路由
url(r'^logout/$', views.logout), # 退出登录路由
4、个人头像路由
# 如果你想访问media文件夹下的内容,必须开设一个接口对外访问
url(r'^media/(?P<path>.*)$', serve, {'document_root': settings.MEDIA_ROOT}),
首页
首页前端
首页前端步骤
总步骤:
1.导航条
登录和未登录导航栏右边是两种形式,加个判断
{% if request.session.username %}
<!-- 动态获取用户名 -->
{% else %}
注册登录两个标签
{% endif %}
2.剩下布局分成3份:2,8,2
3.左右两边先用面板占位置
4.中间是文章内容,复制媒体对象先用文字和默认图片调整好样式
5.首页数据展示
6.图片上传到media文件夹中,配置上传路径(settings中配置路径),增加访问media接口(路由配置)
首页前端代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>BBS仿博客园搭建</title>
{# <link rel="stylesheet" href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.4.1/css/bootstrap.min.css">#}
{# <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.4/jquery.min.js"></script>#}
{# <script src="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.4.1/css/bootstrap.min.css"></script>#}
{% load static %}
<link rel="stylesheet" href="{% static 'bootstrap-3.4.1-dist/css/bootstrap.min.css' %}">
<script src="{% static 'js/jquery.min.js' %}"></script>
<script src="{% static 'bootstrap-3.4.1-dist/js/bootstrap.min.js' %}"></script>
</head>
<body>
{# 导航条开始 #}
<nav class="navbar navbar-inverse">
<div class="container-fluid">
<!-- Brand and toggle get grouped for better mobile display -->
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse"
data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="#">仿博客园作业(BBS)</a>
</div>
<!-- Collect the nav links, forms, and other content for toggling -->
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
<ul class="nav navbar-nav">
<li class="active"><a href="#">首页 <span class="sr-only">(current)</span></a></li>
<li><a href="#">新闻</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>
<li role="separator" class="divider"></li>
<li><a href="#">发现</a></li>
</ul>
</li>
</ul>
<form class="navbar-form navbar-left">
<div class="form-group">
<input type="text" class="form-control" placeholder="Search">
</div>
<button type="submit" class="btn btn-default">搜索</button>
</form>
<ul class="nav navbar-nav navbar-right">
{% if request.session.username %} <!-- 用户登录显示用户名,失败显示注册登录样式 -->
<li><a href="#">{{ request.session.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="#" data-toggle="modal" data-target=".bs-example-modal-lg">修改密码</a></li>
<li><a href="#">后台管理</a></li>
<li><a href="/logout/">退出登录</a></li>
</ul>
</li>
{% else %}
<li><a href="/register/">注册</a></li><!-- 点击注册跳转到注册页面 -->
<li><a href="/login/">登录</a></li>
{% endif %}
</ul>
{# 修改密码开始 #}
<div><!-- 点击修改密码,弹出模态框 -->
<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">
<h1 class="text-center">修改密码</h1>
<div class="row">
<div class="col-md-8 col-md-offset-2">
<form action="">
<div class="form-group">
<label for="username">用户名:</label>
<input type="text" id="username" class="form-control" disabled
value="{{ request.session.username }}"><!-- 用户名显示并且禁用 -->
</div>
<div class="form-group">
<label for="old_password">原密码:</label>
<input type="password" id="old_password" class="form-control">
</div>
<div class="form-group">
<label for="new_password">新密码:</label>
<input type="password" id="new_password" class="form-control">
</div>
<div class="form-group">
<label for="re_password">确认密码:</label>
<input type="password" id="re_password" class="form-control">
</div>
{# <input type="button" class="btn btn-success" value="提交">#}
<div class="pull-right">
<button class="btn btn-success">提交</button>
</div>
<!-- button放在form表单中有自动提交的事件,下面script中需要阻止后续事件的执行 -->
<br>
<br>
<br>
</form>
{# 修改密码结束 #}
</div>
</div>
</div>
</div>
</div>
</div>
</div><!-- /.navbar-collapse -->
</div><!-- /.container-fluid -->
</nav>
{# 导航条结束 #}
<!-- 剩下的布局分成3份 -->
<div class="container-fluid">
<div class="row"><!-- 底下的分成3份,2,8,2分 -->
<div class="col-md-2">
<!-- 面板先占位置 -->
<div class="panel panel-primary">
<div class="panel-heading">Panel heading without title</div>
<div class="panel-body">Panel content</div>
<div class="panel-body">Panel content</div>
<div class="panel-body">Panel content</div>
<div class="panel-body">Panel content</div>
<div class="panel-body">Panel content</div>
<div class="panel-body">Panel content</div>
</div>
<div class="panel panel-danger">
<div class="panel-heading">Panel heading without title</div>
<div class="panel-body">Panel content</div>
<div class="panel-body">Panel content</div>
</div>
</div>
<div class="col-md-8">
<!-- 中间是文章内容,使用媒体对象 -->
<ul class="media-list">
{% for article in article_list %}<!-- 循环所有文章对象的列表 -->
<li class="media">
<h4 class="media-heading"><a href="/{{ username }}/article/{{ article.pk }}">{{ article.title }}</a></h4><!-- 文章标题 -->
<div class="media-left">
<a href="#">
<!-- 显示文章列表中的avatar,使用media文件夹 -->
<!-- 文章对象.站点表(反向).用户表(反向) -->
<img class="media-object" src="/media/{{ article.blog.userinfo.avatar }}" alt="..." style="width: 70px;"><!-- 头像 -->
</a>
</div>
<div class="media-body">{{ article.desc }}</div><!-- 文章摘要 -->
<br>
<div>
<!-- xiezhr 2023-05-10 08:04 1 0 121 -->
<!-- 文章对象.站点表(反向).用户表(反向) -->
<span><a href="">{{ article.blog.userinfo.username }}</a></span> <!-- 用户名 -->
<span>{{ article.create_time|date:'Y-m-d' }} </span><!-- 注册时间 -->
<span><span class="glyphicon glyphicon-thumbs-up"></span>({{ article.up_num }}) </span><!-- 点赞数 -->
<span><span class="glyphicon glyphicon-thumbs-down"></span>({{ article.down_num }}) </span><!-- 点踩数 -->
<span><span class="glyphicon glyphicon-comment"></span>({{ article.comment_num }}) </span><!-- 评论数 -->
</div>
<hr>
</li>
{% endfor %}
</ul>
</div>
<div class="col-md-2">
<div class="panel panel-primary">
<div class="panel-heading">Panel heading without title</div>
<div class="panel-body">
Panel content
</div>
</div>
<div class="panel panel-primary">
<div class="panel-heading">Panel heading without title</div>
<div class="panel-body">
Panel content
</div>
</div>
<div class="panel panel-primary">
<div class="panel-heading">Panel heading without title</div>
<div class="panel-body">
Panel content
</div>
</div>
</div>
</div>
</div>
<!-- 引入layer文件 -->
{#<script src="/static/layer/layer.js"></script>#}
<script src="/static/layer/layer.js"></script>
<script>
// 修改密码提交事件
$(".btn").click(function (e) {
e.preventDefault(); // 阻止后续事件执行
// 1. 获取参数
var old_password = $("#old_password").val();
var new_password = $("#new_password").val();
var re_password = $("#re_password").val();
// 2. 验证参数
//if (!old_password) {
// layer.msg('原始密码不能为空');
// return;
//}
//if (!new_password) {
// layer.msg('新密码不能为空');
// return;
//}
//if (!re_password) {
// layer.msg('确认密码不能为空');
// return;
//}
// 3. 发起ajax请求,把密码输入的数据提交到后端
$.ajax({
url: '/set_password/', // url不能省略,朝修改密码的路由提交post请求
type: 'post',
data: {
old_password: old_password,
new_password: new_password,
re_password: re_password,
csrfmiddlewaretoken: '{{ csrf_token }}'
},
success: function (res) {
if (res.code == 200) {
// icon: 1
layer.msg(res.msg, {}, function () {
{#console.log(msg);#}
location.reload(); // 输入密码成功后刷新页面,或者也可以跳转到登录页面
// location.href = res.url;
})
} else {
{#console.log(msg);#}
layer.msg(res.msg);
}
}
})
})
</script>
</body>
</html>
首页后端
后端代码
Django的分页器:https://www.cnblogs.com/zjyao/p/17378488.html
def home(request):
"""首页搭建"""
# 展示所有的文章列表,查出文章表对象
article_list = models.Article.objects.all()
# print(article_list) # QuerySet对象:[对象1,对象2]
"""文章过多的情况下,可以考虑添加分页器"""
return render(request, 'home.html', locals())
前端重点
前端数据展示逻辑
5.首页数据展示
文章标题
图片:展示用户头像,文章查用户
# 文章--> 站点(正向)--> 用户(反向)--> 点avatar
文章摘要
作者:文章查作者
# 文章--> 站点(正向)--> 用户(反向)--> 点username
文章创建时间,过滤器格式化
文章点赞数
文章点踩数
文章评论数
文章旁的图片展示
6.图片上传到media文件夹中,配置上传路径(settings中配置路径),增加访问media接口(路由配置)
6.1 settings中做配置,配置上传路径
6.2 增加路由,增加访问接口
6.3 改变一下首页前端图片标签中的src地址,把/media/加上
图片展示不正常?
没有开设avater接口。项目中静态文件想要展示,需要开设接口的。图片文件,我们一般不开设avater接口,新开设一个media文件夹,把用户上传的图片单独防止在这个文件夹中。
1.settings中做配置,配置上传路径
# 只要配置了这句话,以后再上传图片的话,就会上传到这个文件夹下面去
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
2.增加路由,增加访问接口 # 暴漏该目录
from django.views.static import serve
from django.conf import settings
# 如果你想访问media文件夹下的内容,必须开设一个接口对外访问
url(r'^media/(?P<path>.*)$', serve, {'document_root': settings.MEDIA_ROOT}), # 固定写法
修改密码
点击修改密码中弹出模态框
修改密码前端步骤
1.在修改密码的a标签中增加两个属性
data-toggle="modal" data-target=".bs-example-modal-lg"
2.模态框中第一个div标签的
class="modal fade"中增加一个类:bs-example-modal-lg
3.增加form表单数据框
用户名显示并且禁用
button在form表单自带提交事件,ajax中需要阻止后续事件的执行
4.ajax提交数据到后端re_password路由中
4.1 绑定点击事件
4.2 阻止后续事件执行,e.preventDefault();
4.3 获取前端参数,3个密码参数
4.4 验证参数
4.5 发起ajax请求,朝后端提交数据,url必填,
4.6 修改成功后,刷新页面location.reload()
修改密码后端
后端逻辑
# 只需要功能,不需要页面了,不用render
1.定义返回给前端的数据格式
2.接收参数
3.验证参数
4.正常逻辑
4.1 修改密码必须登录之后,认证登录装饰器
4.2 原始密码加密
4.3 根据用户名(登录后在session中保存的)和密码查询用户是否存在
4.4 没有查到返回信息
4.5 新密码加密
4.6 修改数据表中的新密码,根据pk(登录后在session中保存的id)筛选update(password=加密后的新密码)
4.7 返回信息
认证登录的装饰器
def login_auth(func):
"""认证登录的装饰器"""
def inner(request, *args, **kwargs):
if request.session.get('username'): # 用户存在,执行正常函数,用户不存在,调登录路由
return func(request, *args, **kwargs)
else:
# return redirect('/login/') # ajax请求,不能跳转页面,只会接收整个页面html代码数据,ajax只要结果
return JsonResponse({'code': 201, 'msg': '请先登录', 'url': '/login/'}) # 返回json格式数据
return inner
后端代码
@login_auth
def set_password(request):
"""修改密码"""
if request.method == 'POST':
# 1.定义返回前端的数据格式
back_dic = {'code': 200, 'msg': '修改密码成功'}
# 2.接收参数
old_password = request.POST.get('old_password')
new_password = request.POST.get('new_password')
re_password = request.POST.get('re_password')
# 3.验证参数
if not old_password:
back_dic['code'] = 1009
back_dic['msg'] = '原始密码不能为空'
return JsonResponse(back_dic)
if not new_password:
back_dic['code'] = 1010
back_dic['msg'] = '新密码不能为空'
return JsonResponse(back_dic)
if not re_password:
back_dic['code'] = 1011
back_dic['msg'] = '确认密码不能为空'
return JsonResponse(back_dic)
# 两次密码不一致
if new_password != re_password:
back_dic['code'] = 1005
back_dic['msg'] = '两次密码不一致'
return JsonResponse(back_dic)
# 4.正常逻辑
# 4.1 验证原始密码是正确
old_pwd = get_md5_pwd(old_password)
# 4.2 使用用户名和原始密码查询用户对象
is_right = models.UserInfo.objects.filter(username=request.session.get('username'), password=old_pwd).first()
print(is_right)
if not is_right: # 用户不存在
back_dic['code'] = 1012
back_dic['msg'] = '原始密码不正确'
return JsonResponse(back_dic)
# 4.3 修改新密码
new_pwd = get_md5_pwd(new_password)
# 4.4 入库
models.UserInfo.objects.filter(pk=request.session.get('id')).update(password=new_pwd)
return JsonResponse(back_dic)
退出登录
后端逻辑
1.清空session,用flush(),数据库和cookie都删
2.跳转到登录页面redirect('登录页面')
后端代码
def logout(request):
"""退出登录"""
# 清空session
request.session.flush()
# 前端使用的是ajax提交的,后端不能在跳转了,需要自己在前端写路由地址
return redirect('/login/')
后台数据模拟
用后台管理系统(admin路由)模拟数据。
1.注册表,把自己创建的所有表都注册到后台
在admin.py文件:
from django.contrib import admin
# Register your models here.
from app01 import models
# 想要在后台管理系统中显示表,需要在admin.py中注册表
# 只要注册了,admin就会自动生产针对该注册表的增删改查至少四个功能
admin.site.register(models.UserInfo)
admin.site.register(models.Blog)
admin.site.register(models.Tag)
admin.site.register(models.Category)
admin.site.register(models.Article)
admin.site.register(models.Article2Tag)
admin.site.register(models.UpAndDown)
admin.site.register(models.Comment)
2.修改admin后台管理的表名
class Meta: # 元信息
verbose_name_plural = '用户表'
# verbose_name = '用户表' # 会显示:用户表s
# 英文表名后台自动加个s
3.字段显示中文,给字段增加verbose_name属性
verbose_name='手机号'
4.添加数据,右上角的add
5.添加的外键显示对象,怎么显示相应name呢?
def __str__(self):
return self.site_name
- 数据可以为空,字段加个属性blank=True
null=True, 代表的是数据库中可以为空
blank=True,代表的是django的后台页面上可以为空
以上操作都跟数据库没有关系,不需要执行数据库迁移命令。
注意事项:绑定时,一定注意用户要对应,不要绑错了
复制整篇文章的html代码
点击id='cnblogs_post_body',右击copy,选择Copy outerHTML
总结点
总结button
button在form表单自带提交事件,ajax绑定点击事件后,点击提交就会产生二次提交,怎么改正?
- 方式一:取消form表单,只显示输入框
- 方式二:button按钮框写在form表单外面,就没有自动提交事件了
- 方式三:仍然form中写button,在js中绑定点击事件后,写阻止后续事件的执行,用
e.preventDefault();
或者
return false;
如果前端使用的是ajax提交,后端就不能跳转了
因为ajax前端只要后端的结果,如果后端使用redirect('一个页面'),会返回给前端res一整个heml页面,不会跳转的。
为什么退出登录可以跳转,因为退出登录的前端是a链接,是个get请求,就可以直接重定向了