第二篇:BBS首页搭建、个人站点及侧边筛选栏
目录一、首页搭建
1、动态显示个人名称
需要实现效果如下所示。
登录之后显示。
html代码如下。
<!--判断用户是否登录-->
{% if request.user.is_authenticated %}
<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="{% url 'logout' %}">退出登录</a></li>
</ul>
</li>
<!--用户没有登录-->
{% else %}
<li><a href="{% url 'register' %}">注册</a></li>
<li><a href="{% url 'login' %}">登录</a></li>
{% endif %}
2、admin后台管理
django给提供了一个可视化的界面,用来方便对模型表进行数据的增删改查。
如果想要使用amdin后台管理操作模型表,那么就需要先注册模型表告诉admin你需要操作哪些表。
方式如下。
"""去应用下的admin.py中注册你的模型表"""
from django.contrib import admin
from app01 import models
# Register your models here.
admin.site.register(models.UserInfo)
admin.site.register(models.Blog)
admin.site.register(models.Category)
admin.site.register(models.Tag)
admin.site.register(models.Article)
admin.site.register(models.UpAndDown)
admin.site.register(models.Comment)
admin.site.register(models.Article2Tag)
如此一来,我们可以在admin后台管理中操作,数据库表,使用超级用户进行登录。
针对这个数据库表,我们需要先从文章表入手,添加分类和标签,之后操作Article2tags表,最后记得要将用户表和个人站点表进行绑定。
我们可以将表名变成中文的形式,只需要在models.py中的表类中添加,效果如下。
代码如下。
class UserInfo(AbstractUser):
# 手机号
"""
null=True 数据库该字段可以为空
blank=True admin后台管理该字段可以为空
"""
phone = models.BigIntegerField(verbose_name='手机号', null=True, blank=True)
# 头像
"""给avatar字段传文件对象 该文件会自动存储到avatar文件下 然后avatar字段只保存文件路径avatar/default.png"""
avatar = models.FileField(verbose_name='头像', upload_to='avatar/', default='avatar/default.png')
# 创建时间
create_time = models.DateField(verbose_name='创建时间', auto_now_add=True)
# 一对一个人站点表
blog = models.OneToOneField(to='Blog', null=True)
# 修改admin后台管理中的表名
class Meta:
verbose_name_plural = '用户表'
verbose_name = '用户表' # 末尾还是会自动加s
# 显示对象时,直接显示对象的username
def __str__(self):
return self.username
3、用户头像显示
效果如下。
"""
1 网址所使用的静态文件默认放在static文件夹下
2 用户上传的静态文件也应该单独放在某个文件夹下
"""
media配置:该配置可以让用户上传的所有文件都固定存放在某一个指定的文件夹下
配置方式 settings.py文件中
# 配置用户上传的文件存储位置
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
当然,如果需要显示头像,还需要开设后端指定文件夹资源
在urls.py中
# 导入关于文件上传配置路径模块
from django.views.static import serve
from BBS import settings
urlpatterns = [
...
# 暴露后端指定文件夹资源
url(r'^media/(?P<path>.*)', serve, {'document_root': settings.MEDIA_ROOT}),
...
]
4、修改密码
效果如下。
代码如下。
"""login.html"""
<li><a href="#" data-toggle="modal" data-target="#myModal">修改密码</a></li>
<!--修改密码模态框创建-->
<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 text-center" id="myModalLabel">修改密码</h4>
</div>
<div class="modal-body">
<div class="row">
<div class="col-md-8 col-md-offset-2">
<!--用户名-->
<div class="form-group">
用户名:<input type="text" value="{{ request.user.username }}" disabled class="form-control">
</div>
<!--原密码-->
<div class="form-group">
原密码:<input type="password" name="old_password" class="form-control" id="id_old_password">
</div>
<div class="form-group">
新密码:<input type="password" name="new_password" class="form-control" id="id_new_password">
</div>
<div class="form-group">
确认密码:<input type="password" name="confirm_password" class="form-control" id="id_confirm_password">
</div>
</div>
</div>
</div>
<div class="modal-footer">
<div class="col-md-8 col-md-offset-2">
<button type="button" class="btn btn-success pull-left" id="id_commit">确认</button>
<!--需要进行微调样式-->
<span id="id_error" style="color: red; margin-left: 10px; margin-top: 8px" class="pull-left"></span>
<button type="button" class="btn btn-info pull-right" data-dismiss="modal">关闭</button>
</div>
</div>
</div>
</div>
</div>
"""ajax代码"""
<script>
// 修改密码,模态框提交
$('#id_commit').click(function () {
// ajax提交用户数据
$.ajax({
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 (args) {
if(args.code === 1000){
window.location.href = args.url
}else{
$('#id_error').text(args.msg)
}
}
})
})
</script>
"""set_password"""
# 修改密码
@login_required(login_url='/home/')
def set_password(request):
# 如果是ajax请求
if request.is_ajax():
if request.method == 'POST':
# 返回给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')
print(new_password, confirm_password)
# 检查用户传过来的旧密码是否正确
is_right = request.user.check_password(old_password)
if is_right:
if new_password == confirm_password:
# 设置密码
request.user.set_password(new_password)
request.user.save()
# 修改密码完成之后,还在首页显示即可。
back_dic['url'] = '/home/'
else:
back_dic['code'] = 2000
back_dic['msg'] = '两次密码不一致'
else:
back_dic['code'] = 3000
back_dic['msg'] = '旧密码不正确'
return JsonResponse(back_dic)
5、退出登录
# 退出登录
@login_required(login_url='/home/')
def logout(request):
auth.logout(request)
return redirect('/home/')
6、首页完整代码
"""home.html"""
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
{% load static %}
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<link rel="stylesheet" href="{% static 'bootstrap-3.4.1-dist/css/bootstrap.min.css' %}">
<script src="{% static 'bootstrap-3.4.1-dist/js/bootstrap.min.js' %}"></script>
</head>
<body>
<div class="container-fluid">
<!--导航条-->
<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="#">Action</a></li>
<li><a href="#">Another action</a></li>
<li><a href="#">Something else here</a></li>
<li role="separator" class="divider"></li>
<li><a href="#">Separated link</a></li>
<li role="separator" class="divider"></li>
<li><a href="#">One more separated link</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">Submit</button>
</form>
<ul class="nav navbar-nav navbar-right">
<!--判断用户是否登录-->
{% if request.user.is_authenticated %}
<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="#" data-toggle="modal" data-target="#myModal">修改密码</a></li>
<li><a href="#">修改头像</a></li>
<li><a href="#">后台管理</a></li>
<li role="separator" class="divider"></li>
<li><a href="{% url 'logout' %}">退出登录</a></li>
</ul>
</li>
<!--修改密码模态框创建-->
<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 text-center" id="myModalLabel">修改密码</h4>
</div>
<div class="modal-body">
<div class="row">
<div class="col-md-8 col-md-offset-2">
<!--用户名-->
<div class="form-group">
用户名:<input type="text" value="{{ request.user.username }}" disabled class="form-control">
</div>
<!--原密码-->
<div class="form-group">
原密码:<input type="password" name="old_password" class="form-control" id="id_old_password">
</div>
<div class="form-group">
新密码:<input type="password" name="new_password" class="form-control" id="id_new_password">
</div>
<div class="form-group">
确认密码:<input type="password" name="confirm_password" class="form-control" id="id_confirm_password">
</div>
</div>
</div>
</div>
<div class="modal-footer">
<div class="col-md-8 col-md-offset-2">
<button type="button" class="btn btn-success pull-left" id="id_commit">确认</button>
<!--需要进行微调样式-->
<span id="id_error" style="color: red; margin-left: 10px; margin-top: 8px" class="pull-left"></span>
<button type="button" class="btn btn-info pull-right" data-dismiss="modal">关闭</button>
</div>
</div>
</div>
</div>
</div>
{% else %}
<li><a href="{% url 'register' %}">注册</a></li>
<li><a href="{% url 'login' %}">登录</a></li>
{% endif %}
</ul>
</li>
</ul>
</div><!-- /.navbar-collapse -->
</div><!-- /.container-fluid -->
</nav>
<div class="row">
<div class="col-md-8">
<!--for循环拿到的文件对象-->
{% for article_obj in article_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="/media/{{ article_obj.blog.userinfo.avatar}}" alt="..." style="height: 60px">
</a>
</div>
<div class="media-body">
{{ article_obj.desc }}
</div>
</div>
<br>
<!--底部内容展示【评论之类】-->
<div>
<span> <a href="">{{ article_obj.blog.userinfo.username }} </a> </span>
<span>{{ article_obj.create_time|date:'Y-m-d' }} </span>
<span><span class="glyphicon glyphicon-thumbs-up"></span> {{ article_obj.up_num }} </span>
<span><span class="glyphicon glyphicon-list-alt"></span> {{ article_obj.comment_num }} </span>
</div>
<hr>
{% endfor %}
</div>
</div>
</div>
</div>
<script>
// 修改密码,模态框提交
$('#id_commit').click(function () {
// ajax提交用户数据
$.ajax({
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 (args) {
if(args.code === 1000){
window.location.href = args.url
}else{
$('#id_error').text(args.msg)
}
}
})
})
</script>
</body>
</html>
"""views.py"""
# 首页
def home(request):
# 拿到所有的文章对象,将文章对象渲染到页面上
article_queryset = models.Article.objects.all()
# print(request.user) # 拿到用户对象 yangyi
return render(request, 'home.html', locals())
首页效果如下。
二、个人站点和侧边栏筛选功能
"""补充"""
# 每个用户都可以通过暴露出来的media有自己的站点样式
<link rel="stylesheet" href="/media/css/{{ blog.site_theme }}/">
"""日期归档相关"""
django官网提供的一个orm语法
from django.db.models.functions import TruncMonth
Sales.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
时区问题报错
TIME_ZONE = 'Asia/Shanghai'
USE_TZ = True
"""图片防盗链"""
# 如何避免别的网站直接通过本网站的url访问本网站资源
# 简单的防盗
我可以做到请求来的时候先看看当前请求是从哪个网站过来的
如果是本网站那么正常访问
如果是其他网站直接拒绝
请求头里面有一个专门记录请求来自于哪个网址的参数
Referer: http://127.0.0.1:8000/xxx/
# 如何避免
1.要么修改请求头referer
2.直接写爬虫把对方网址的所有资源直接下载到我们自己的服务器上
如何实现这样的功能呢?
- urls.py
urlpatterns = [
...
# 个人站点
url(r'^(?P<username>\w+)/$', views.user_site, name='user_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/(\w+)/',views.site),
# 上面的三条url其实可以合并成一条 .表示匹配除换行符\n之外的任何单字符,*表示零次或多次。所以.*在一起就表示任意字符出现零次或多次。
url(r'^(?P<username>\w+)/(?P<condition>category|tag|archive)/(?P<param>.*)/', views.user_site)
]
- views.py
# 个人站点
@login_required(login_url='/home/')
def user_site(request, username, **kwargs):
"""如果kwargs有值,也就意味着需要对article_list做额外的筛选操作"""
# 先校验个人站点是否存在
user_obj = models.UserInfo.objects.filter(username=username).first()
# 如果用户不存在
if not user_obj:
# 返回一个错误页面
return render(request, 'errors.html')
# 拿到个人站点对象
blog = user_obj.blog
# 查询当前个人站点下的所有的文章
article_list = models.Article.objects.filter(blog=blog)
# 这里进行判别 category|tag|archive
if kwargs:
# print(kwargs) # {'condition': 'category', 'param': '2'}
condition = kwargs.get('condition')
param = kwargs.get('param')
# 判断用户到底想按照哪个条件筛选数据
if condition == 'category':
article_list = article_list.filter(category_id=param)
elif condition == 'tag':
article_list = article_list.filter(tags__id=param)
else:
year, month = param.split('-') # 2020-11 [2020,11]
article_list = article_list.filter(create_time__year=year, create_time__month=month)
# 1 查询当前个人站点下所有的分类及分类下的文章数
category_list = models.Category.objects.filter(blog=blog).annotate(count_num=Count('article__pk')).values_list('name', 'count_num', 'pk')
"""
1、先过滤出个人站点下的所有分类
2、将个人站点过滤出的分类进行分组
3、统计出分组之后分类下的文章数【反向查询(直接表名小写+__, 就可以到达有外键的表)】
4、拿出分类名和分类下的文章数
"""
# print(category_list) # <QuerySet [('yangyi的分类一', 2), ('yangyi的分类二', 1), ('yangyi的分类三', 1)]>
# 2 查询当前个人站点下所有的标签及标签下的文章数
tag_list = models.Tag.objects.filter(blog=blog).annotate(count_num=Count('article__pk')).values_list('tag', 'count_num', 'pk')
# print(tag_list) # <QuerySet [('yangyi的标签一', 1), ('yangyi的标签二', 2), ('yangyi的标签三', 1)]>
# 3 查询当前个人站点下按照年月统计所有的文章
date_list = models.Article.objects.filter(blog=blog).annotate(month=TruncMonth('create_time')).values('month').annotate(count_num=Count('pk')).values_list('month', 'count_num')
# print(date_list) # <QuerySet [(datetime.date(2020, 6, 1), 1), (datetime.date(2021, 5, 1), 1), (datetime.date(2021, 2, 1), 1), (datetime.date(2021, 7, 1), 1)]>
return render(request, 'user_site.html', locals())
- user_site.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
{% load static %}
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<link rel="stylesheet" href="{% static 'bootstrap-3.4.1-dist/css/bootstrap.min.css' %}">
<script src="{% static 'bootstrap-3.4.1-dist/js/bootstrap.min.js' %}"></script>
<link rel="stylesheet" href="/media/css/{{ blog.site_theme }}">
</head>
<body>
<div class="container-fluid">
<!--导航条-->
<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="#">Action</a></li>
<li><a href="#">Another action</a></li>
<li><a href="#">Something else here</a></li>
<li role="separator" class="divider"></li>
<li><a href="#">Separated link</a></li>
<li role="separator" class="divider"></li>
<li><a href="#">One more separated link</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">Submit</button>
</form>
<ul class="nav navbar-nav navbar-right">
<!--判断用户是否登录-->
{% if request.user.is_authenticated %}
<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="#" data-toggle="modal" data-target="#myModal">修改密码</a></li>
<li><a href="#">修改头像</a></li>
<li><a href="#">后台管理</a></li>
<li role="separator" class="divider"></li>
<li><a href="{% url 'logout' %}">退出登录</a></li>
</ul>
</li>
<!--修改密码模态框创建-->
<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 text-center" id="myModalLabel">修改密码</h4>
</div>
<div class="modal-body">
<div class="row">
<div class="col-md-8 col-md-offset-2">
<!--用户名-->
<div class="form-group">
用户名:<input type="text" value="{{ request.user.username }}" disabled class="form-control">
</div>
<!--原密码-->
<div class="form-group">
原密码:<input type="password" name="old_password" class="form-control" id="id_old_password">
</div>
<div class="form-group">
新密码:<input type="password" name="new_password" class="form-control" id="id_new_password">
</div>
<div class="form-group">
确认密码:<input type="password" name="confirm_password" class="form-control" id="id_confirm_password">
</div>
</div>
</div>
</div>
<div class="modal-footer">
<div class="col-md-8 col-md-offset-2">
<button type="button" class="btn btn-success pull-left" id="id_commit">确认</button>
<!--需要进行微调样式-->
<span id="id_error" style="color: red; margin-left: 10px; margin-top: 8px" class="pull-left"></span>
<button type="button" class="btn btn-info pull-right" data-dismiss="modal">关闭</button>
</div>
</div>
</div>
</div>
</div>
{% else %}
<li><a href="{% url 'register' %}">注册</a></li>
<li><a href="{% url 'login' %}">登录</a></li>
{% endif %}
</ul>
</li>
</ul>
</div><!-- /.navbar-collapse -->
</div><!-- /.container-fluid -->
</nav>
<div class="row">
<div class="col-md-3">
<!--广告占位1-->
<div class="panel panel-primary">
<div class="panel-heading">
<h3 class="panel-title text-center">文章分类</h3>
</div>
<div class="panel-body text-center">
{% for category_obj in category_list %}
<p><a href="/{{ username }}/category/{{ category_obj.2 }}">{{ category_obj.0 }} ({{ category_obj.1 }})</a></p>
{% endfor %}
</div>
</div>
<!--广告占位2-->
<div class="panel panel-danger">
<div class="panel-heading">
<h3 class="panel-title text-center">文章标签</h3>
</div>
<div class="panel-body text-center">
{% for tag_obj in tag_list %}
<p><a href="/{{ username }}/tag/{{ tag_obj.2 }}">{{ tag_obj.0 }} ({{ tag_obj.1 }})</a></p>
{% endfor %}
</div>
</div>
<!--广告占位3-->
<div class="panel panel-info">
<div class="panel-heading">
<h3 class="panel-title text-center">日期归档</h3>
</div>
<div class="panel-body text-center">
{% for date_obj in date_list %}
<p><a href="/{{ username }}/archive/{{ date_obj.0|date:'Y-m' }}">{{ date_obj.0|date:'Y年m月' }} ({{ date_obj.1 }})</a></p>
{% endfor %}
</div>
</div>
</div>
<div class="col-md-9">
{% for article_obj in article_list %}
<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="/media/{{ article_obj.blog.userinfo.avatar}}" alt="..." style="height: 60px">
</a>
</div>
<div class="media-body">
{{ article_obj.desc }}
</div>
</div>
<br>
<!--底部内容展示【评论之类】-->
<div class="pull-right">
<span> <a href="">{{ article_obj.blog.userinfo.username }} </a> </span>
<span>{{ article_obj.create_time|date:'Y-m-d' }} </span>
<span><span class="glyphicon glyphicon-thumbs-up"></span> {{ article_obj.up_num }} </span>
<span><span class="glyphicon glyphicon-list-alt"></span> {{ article_obj.comment_num }} </span>
</div>
<hr>
{% endfor %}
</div>
</div>
</div>
</body>
</html>