1210 BBS admin后台管理及侧边栏筛选个人站点
目录
昨日内容
昨日内容回顾
登录功能
图片验证码
自己写的
pillow模块
图片相关的它都可以操作
图片上写字
图片上加虚幻效果
加一些妨碍你观看的对象
登录功能逻辑
pillow模块
生成图片
画笔
字体
.ttf结尾的都是用来控制字体样式
io模块
内存管理器
ByteIo StringIo
chr() 数字转字母
ord() 字母转数字
random模型
def get_code(n):
code = ''
for i in range(n):
upper_str = chr(random.randint(65,90))
lower_str = chr(random.randint(97,122))
random_int = str(random.randint(0,9))
code += random.choice([upper_str,lower_str,random_int])
return code
我们是将产生的随机验证码保存到了session中(session有时候可以作为临时存储数据的仓库
并不一定非要是和用户登录相关的业务才能操作session
)
前端点击图片自动刷新
利用的src后面的url变化就会自动再次朝该url发送get请求
首页搭建
282布局
跟对一个好的领导
导航条用户功能
1.根据用户是否登录 来控制导航条左侧显示的内容
用户登录 显示用户名和更多操作
用户没有登录 显示登录注册
利用request.user.is_authenticated()
2.修改密码
check_password
set_password
save()
只要是跟用户相关的功能 你都利用auth模块来帮你简单快捷的实现
在以后的工作中 任何不要想着立马自己动手写
一定要先想有没有相应的模块
先按照自己的思路先往下写 写到没法实现的地方
可以寻求相应的模块
3.退出登录
logout()
django admin后台管理
Xadmin组件
本身有很多bug
使用
-
去应用下的admin.py中注册你想要管理的模型类
from app01 import models 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)
-
要想登录到后台管理,必须是超级用户,管理员root权限
createsuperuser
-
admin会自动给所有的注册了的模型表加一个s后缀
-
admin会自动给注册了的模型表自动生成增删改查四条url
url(r'^admin/', admin.site.urls) http://127.0.0.1:8000/admin/app01/userinfo/ 查 http://127.0.0.1:8000/admin/app01/userinfo/3/change/ 改 http://127.0.0.1:8000/admin/app01/userinfo/3/delete/ 删 http://127.0.0.1:8000/admin/app01/userinfo/add/ 增
-
更换后台中表的中文显示
在模型表中models.py 在表名类下定义类: class Meta: verbose_name_plural = '用户表' // 直接更改 # verbose_name = '用户表 // 自动加s后缀
-
查看时显示第三张表中的文件显示
# 表类中定义双下str方法,便于查看 def __str__(self): return self.username
建表
-
首先录入Article表数据,表中外键字段较多,但可以通过点击的方式跳转到外键关联的表中
-
建立userinfo表的外键字段表关系,用于确认用户与个人站点之间的外键关联
- 在添加前使表关系中的phone字段添加
blank=true
属性,告诉admin后台管理该字段可以为空
- 在添加前使表关系中的phone字段添加
-
Tag表绑定标签
-
文章与标签的多对多关系,在第三张表中绑定关系
-
编写前端数据显示
用户图片的显示
前端for循环进行查看图片时加载不出来
- 网站所用到的静态文件统一默认都放在static文件夹内
- 用户上传的静态文件也应该单独找一个文件夹存储
- 无论用户上传什么类型的文件,只要是静态的都应该放在某一个固定的文件夹下
MEDIA用户配置
media配置,能够将用户上传的所有文件都统一放在指定的文件夹下
settings中配置文件
MEDIA_ROOT = os.path.join(BASE_DIR,'media')
会直接定义了一个文件夹,然后用户上传注册的文件都会存在里面
查找照片
给图片开设后端接口
url中手动开启后端资源,会将media文件夹下的所有资源暴露给前端
url(r'^media/(?P<path>.*)',serve,{'document_root':settings.MEDIA_ROOT})
搭建个人站点
- 利用有名分组将匹配到的用户站点名传入后端views中的site函数
- 根据穿入的username查询数据库的user对象
- 判断有无,定义404界面
防盗链
- 通过判断当前请求之前的所在地址
- 如果本网站就正常访问,一旦不是则直接禁止
- 请求头里面的
Referer
标识从哪里来的
新建css文件
-
在media文件夹定义css文件的文件夹css
-
在css文件夹内根据表的关系定义每个css样式的文件名
-
link导入css样式
{{blog.site_theme}}
侧边栏展示标签
利用聚合函数进行查询
定义分类栏与标签栏
from django.db.models import Max,Min,Count,Sum,Avg
# 1.查询当前用户每一个分类及分类的文章数(根据个人站点blog得到分类)
category_list = models.Category.objects.filter(blog=blog).annotate(num=Count('article')).values_list('name','num')
# 2.查询当前用户每一个标签及标签下的文章数
tag_list = models.Tag.objects.filter(blog=blog).annotate(num=Count('article')).values_list('name','num')
定义时间栏
临时在表中添加字段
django提供了一个方法
from django.db.models.functions import TruncMonth
-官方提供
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
按照年月分组
# 3.按照年月分组
date_list = models.Article.objects.filter(blog=blog).annotate(month=TruncMonth('create_time')).values('month').annotate(num=Count('pk')).values_list('month','num')
前端获取数据
{% for date in date_list %}
<p><a href="#">{{ date.0|date:'Y年m月' }}({{ date.1 }})</a></p>
{% endfor %}
侧边栏筛选功能
侧边栏的对应筛选使用标签\分类\时间格式的拼接使用
博客园的连接
https://www.cnblogs.com/wupeiqi/tag/Python/
https://www.cnblogs.com/wupeiqi/category/850028.html
https://www.cnblogs.com/wupeiqi/archive/2016/07.html
1.侧边栏的设置
利用一个函数接受多天url传来的数据来使用
# 个人站点
url(r'^(?P<username>\w+)/$',views.site,name='username'),
# 个人站点侧边栏筛选功能
# url(r'^(?P<username>\w+)/category/(?P<param>\d+)/',views.site),
# url(r'^(?P<username>\w+)/tag/(?P<param>\d+)/',views.site),
# url(r'^(?P<username>\w+)/archive/(?P<param>\w+)/',views.site),
有多个关键字参数,所以使用**kwargs进行接受,在view中
进行优化
# 个人站点
url(r'^(?P<username>\w+)/$',views.site,name='username'),
url(r'^(?P<username>\w+)/(?P<condition>category|tag|archive)/(?P<param>.*)/',views.site)
侧边栏的展示
# 侧边栏展示
'''
侧边栏筛选功能到底是筛选什么
筛的是当前这个用户下面的所有的文章,再进行筛选
本质其实就是对已经查询出来的article_list再进行筛选操作
'''
if kwargs:
# kwargs是用户根据相应的网址url输入获取到的键值对 {'condition': 'tag', 'param': '1'}
condition = kwargs.get('condition')
# param是网址的后缀数字
param = kwargs.get('param')
# 对用户输入的url数据进行判断
if condition == 'category':
# 按照分类去找列表中筛选
article_list = article_list.filter(category_id=param)
elif condition == 'tag':
# 按照标签去筛选
article_list = article_list.filter(tag_id=param)
else:
# 按照日期去筛选,神奇的双下划线方法
year,month = param.split('-')
article_list = article_list.filter(create_time__year=year,create_time__month=month)
路由分发本质
禁止禁止俄罗斯套娃
url(r'^index/',([
url(r'^index_1/',([
url(r'^index_1_1/',([
],None,None)),
url(r'^index_1_2/',index2),
url(r'^index_1_3/',index1),
],None,None)),
url(r'^index_2/',index2),
],None,None)),
代码
url.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='code'),
# 首页搭建
url(r'^home/',views.home,name='home'),
# 退出登录
url(r'^logout/',views.logout,name='logout'),
# 修改密码
url(r'^set_password/',views.set_password,name='set_pwd'),
# 手动开设后端资源,将media文件夹下的所有资源暴露给前端
url(r'^media/(?P<path>.*)',serve,{'document_root':settings.MEDIA_ROOT}),
# 个人站点
url(r'^(?P<username>\w+)/$',views.site,name='username'),
# 个人站点侧边栏筛选功能
# url(r'^(?P<username>\w+)/category/(?P<param>\d+)/',views.site),
# url(r'^(?P<username>\w+)/tag/(?P<param>\d+)/',views.site),
# url(r'^(?P<username>\w+)/archive/(?P<param>\d+)/',views.site),
# 优化
url(r'^(?P<username>\w+)/(?P<condition>category|tag|archive)/(?P<param>.*)/',views.site)
]
views
# 个人站点的搭建
# 侧边栏
from django.db.models import Max,Min,Count,Sum,Avg
# 按照年月分组
from django.db.models.functions import TruncMonth
def site(request,username,**kwargs):
user_obj = models.Userinfo.objects.filter(username=username).first()
# 获取用户的站点
blog = user_obj.blog
# 判断是否有站点,404界面
if not user_obj:
return render(request,'error.html')
# 跳转到个人站点
# 个人站点有当前用户的所有文章,查询所有的文章
article_list = models.Article.objects.filter(blog=blog)
# 侧边栏展示
'''
侧边栏筛选功能到底是筛选什么
筛的是当前这个用户下面的所有的文章,再进行筛选
本质其实就是对已经查询出来的article_list再进行筛选操作
'''
if kwargs:
# kwargs是用户根据相应的网址url输入获取到的键值对 {'condition': 'tag', 'param': '1'}
condition = kwargs.get('condition')
# param是网址的后缀数字
param = kwargs.get('param')
# 对用户输入的url数据进行判断
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('-')
article_list = article_list.filter(create_time__year=year,create_time__month=month)
# 1.查询当前用户每一个分类及分类的文章数(根据个人站点blog得到分类)
category_list = models.Category.objects.filter(blog=blog).annotate(num=Count('article')).values_list('name','num','pk')
# 2.查询当前用户每一个标签及标签下的文章数
tag_list = models.Tag.objects.filter(blog=blog).annotate(num=Count('article')).values_list('name','num','pk')
# 3.按照年月分组
date_list = models.Article.objects.filter(blog=blog).annotate(month=TruncMonth('create_time')).values('month').annotate(num=Count('pk')).values_list('month','num')
return render(request,'site.html',locals())
error.html
<html>
<head>
<meta charset='utf-8' />
<link rel="icon" href="//common.cnblogs.com/favicon.ico" type="image/x-icon" />
<title>404_页面不存在</title>
<style type='text/css'>
body {
margin: 8% auto 0;
max-width: 550px;
min-height: 200px;
padding: 10px;
font-family: 'PingFang SC', 'Microsoft YaHei', 'Helvetica Neue', 'Helvetica', 'Arial', sans-serif;
font-size: 14px;
}
p {
color: #555;
margin: 10px 10px;
}
img {
border: 0px;
}
.d {
color: #404040;
}
</style>
</head>
<body>
<a href='{% url 'home' %}'><img src='/media/avatar/01b19.jpg' alt='cnblogs' /></a>
<p><b>404.</b> 抱歉,您访问的资源不存在。</p>
<p class='d'>请确认您输入的网址是否正确,如果问题持续存在,请憋着,不要与我们联系。</p>
<p><a href='{% url 'home' %}'>返回网站首页</a></p>
</body>
</html>
site.html
<!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>
{% 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>
<link rel="stylesheet" href="/media/css/{{ blog.site_theme }}">
</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="">{{ user_obj.blog.site_title }}</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><a href="/home/" class="glyphicon glyphicon-home"></a></li>
<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">
{# 添加模态框的属性data-toggle="modal" data-target="#myModal"#}
<li><a 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>
{% else %}
{# 没有登录则显示登录注册按钮#}
<li><a href="{% url 'login' %}">登录</a></li>
<li><a href="{% url 'register' %}">注册</a></li>
{% endif %}
</ul>
</div><!-- /.navbar-collapse -->
</div><!-- /.container-fluid -->
</nav>
{#整体页面布局#}
<div class="container-fluid">
<div class="row">
{# 左边面板#}
<div class="col-md-3">
<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.2 }}/">{{ category.0 }}({{ category.1 }})</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.2 }}/">{{ tag.0 }}({{ tag.1 }})</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.0|date:'Y-m' }}/">{{ date.0|date:'Y年m月' }}({{ date.1 }})</a></p>
{% endfor %}
</div>
</div>
</div>
{# 文章面板 #}
<div class="col-md-9">
{% for article in article_list %}
{# 媒体对象#}
<div class="media">
{# 标题#}
<h4 class="media-heading"><a href="#">{{ article.title }}</a></h4>
<div class="media-left media-middle">
{# 头像#}
<a href="#">
<img class="media-object" src="/media/{{ article.blog.userinfo.avatar }}/" alt="..." height="60">
</a>
</div>
{# 简介#}
<div class="media-body" id="desc">
{{ article.desc }}
</div>
{# 定义文章的下标#}
<div class="pull-right">
<span>posted </span>
<span>@ </span>
<span>{{ article.create_time|date:'Y-m-d' }} </span>
<span>{{ article.blog.userinfo.username }} </span>
<span><span class="glyphicon glyphicon-comment"></span>评论数({{ article.comment_num }}) </span>
<span><span class="glyphicon glyphicon-thumbs-up"></span>点赞数({{ article.up_num }})</span>
<span><a href="#">编辑</a></span>
</div>
</div>
<hr>
{% endfor %}
</div>
</div>
</div>
{#修改密码的弹出框#}
<!-- 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">
{# 设置密码的更改#}
<form action="{% url 'set_pwd' %}" method="post">
{% csrf_token %}
<div class="form-group">
<label for="id_username">用户名</label>
<input type="text" id="id_username" name="username" disabled class="form-control" value="{{ request.user.username }}">
</div>
<div class="form-group">
<label for="id_password">初始密码</label>
<input type="password" id="id_password" name="old_password" class="form-control" >
</div>
<div class="form-group">
<label for="id_new_password">新密码</label>
<input type="password" id="id_new_password" name="new_password" class="form-control" >
</div>
<div class="form-group">
<label for="id_confirm_password">确认新密码</label>
<input type="password" id="id_confirm_password" name="confirm_password" class="form-control">
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">取消</button>
<button class="btn btn-primary" id="id_s">提交</button>
</div>
</form>
</div>
</div>
</div>
</div>
</body>
</html>