BBS+Blog项目流程及补充知识点
项目流程:
1. 产品需求
(1)基于用户认证组件和Ajax实现登陆验证(图片验证码)
(2)基于forms组件和Ajax实现注册功能
(3)设计系统首页(文章列表渲染)
(4)设计个人站点页面
(5)文章详情页
(6)实现文章点赞功能
(7)实现文章的评论
--- 文章的评论
--- 评论的评论
(8)富文本编辑框的防止xss攻击
2. 设计表结构
3. 按照每个功能进行开发
4. 功能测试
5. 项目部署上线
所需知识点:
1. JQuery 获取input标签中的文件对象:$("#avatar")[0].files[0]
2.
$("#form").serializeArray(); // 返回一个数组,数组中是一个个对象,对象的格式为: {name:"form表单控件的name属性值",value:"form表单控件的value值"}
浏览器效果图:
3. Django的Media配置:
FileField字段和ImageField字段:FileField可用于上传任何文件,ImageField只能上传图片 # 表:
class UserInfo(AbstractUser):
nid = models.AutoField(primary_key=True)
telephone = models.CharField(max_length=11, null=True, unique=True)
avatar = models.FileField(upload_to="avatars/", default="/avatars/default.png") # avatar 这个字段不传的时候(avatar字段为空时,也是上传了 avatar字段),才会使用 default 的默认值
# FileField字段一定要接受一个文件对象;ImageFile字段一定要接受一个图片对象 # 给上面的表生成记录:
avatar_obj = request.FILES.get("avatar")
user_obj = UserInfo.objects.create_user(username=user,password=psw,email=email,avatar=avatar_obj) # 生成记录时,Django会自动下载 avatar_obj 这个文件对象,由于 upload_to = "avatars/",Django会把avatar_obj下载到根目录的 "avatars"文件夹下(没有"avatars"文件夹 django会自动创建一个),user_obj的avatar字段(UserInfo表中)存的是 文件的相对路径 Media配置之 MEDIA_ROOT: Django对静态文件的区分:Django有两种静态文件
1. /static/ :js,css,img;服务器自己用到的文件;
2. /media/ :用户上传的文件 # 用户注册时的代码:
avatar_obj = request.FILES.get("avatar")
user_obj = UserInfo.objects.create_user(username=user,password=psw,email=email,avatar=avatar_obj) 上面两行代码,一旦在settings.py中配置了 media 文件("media"是project中的一个文件夹名,可放在project下,也可放在 app 下):
MEDIA_ROOT = os.path.join(BASE_DIR,"media")
Django会自动实现如下功能:
会将文件对象下载到MEDIA_ROOT中的 avatars(因为UserInfo表中是:upload_to="avatars/")文件夹中(如果MEDIA_ROOT中没有 avatars 这个文件夹,Django会自动创建 );user_obj的avatar存的是文件的相对路径 Media配置之 MEDIA_URL:
客户端浏览器如何能直接访问到 media 中的数据? 1. settings.py:
MEDIA_URL = "/media/" # MEDIA_URL = "/media/" 也是为上面的 MEDIA_ROOT 那个绝对路径起了一个别名;效果和 STATIC_URL = '/static/' 一样 2. urls.py中的 urlpatterns :
from django.views.static import serve
re_path(r"media/(?P<path>.*)$",serve,{"document_root":settings.MEDIA_ROOT})
4. Django的 admin 组件:
admin组件:(不是必需的)
# Django内部提供的一个组件,作用:后台数据管理组件(通过web页面)
# 注:1. 只有超级用户(superuser)才能登陆 admin 路径; 2. python manage.py createsuperuser ---> 针对的是用户认证组件auth对应的那个用户表(auth_user或者Blog项目中的 blog_userinfo 表) # 用admin组件对象后台数据进行操作之前,需要先进行 admin 注册:
# admin注册语法: 每一个 app 下面有一个 admin.py 的文件,在这个文件中做 admin 注册,如这个 Blog 项目:
# (1) 先把表拿过来:
from blog import models
# (2) 通过 admin.site.register() 注册 models.py 中的表
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.Article2Tag)
admin.site.register(models.ArticleUpDown)
admin.site.register(models.Comment)
# 在 admin 页面操作 Comment 表,url为:http://127.0.0.1:8000/admin/blog/comment/ # /blog/为app的名字, /comment/为表的名字
5. 日期归档查询
日期归档查询(数据库)1:
1. date_format(时间相关字段,时间格式)
# date存“年月日”,time存“时分秒”,datetime存“年月日时分秒” ===============date,time,datetime============== create table t_mul(d date,t time,dt datetime);
insert into t_mul values(now(),now(),now());
select * from t_mul; +------------+----------+---------------------+
| d | t | dt |
+------------+----------+---------------------+
| 2017-08-01 | 19:42:22 | 2017-08-01 19:42:22 |
+------------+----------+---------------------+ select date_format(dt,"%Y-%m") from t_mul;
# 查询结果为: 2017-08 2. extra():用于插入sql语句;该函数的调用者是 QuerySet
extra(select=None,where=None,params=None,tables=None,order_by=None,select_params=None)
有点情况下,对于一些复杂的sql语句,Django并没有相应的ORM去对应这些sql语句,这种情况下Django提供了 extra() 函数:QuerySet修改机制---它能在QuerySet生成的SQL从句中注入新子句
extra() 可以指定一个或多个参数,例如 select,where 或者 tables;这些参数都不是必需的,但至少要使用一个 select参数:select参数可以让你在 SELECT 从句中添加其它字段信息;它是一个字典,存放着 属性名到 SQL 从句的映射 如:
in MySQL:
article_obj = models.Article.objects.extra(select={"is_recent":"create_time > '2017-09-05'"}.values("title","is_recent") # 通过 extra() 就可以写 create_time > '2017-09-05' 这种SQL的写法 # 返回结果还可以继续 annotate()(.values("xxx").annotate())进行分组
# 返回的结果集(也是一个QuerySet)中每个 Entry 对象都有一个额外的属性 is_recent,它是一个布尔值(True为1,False为0),表示 Article对象的 create_time 是否晚于 2017-09-05 日期归档查询(数据库)2: # 利用Django提供的 TruncMonth() 方法: 截取到月
from django.db.models.functions import TruncMonth
Sales.objects
.annotate(month=TruncMonth('timestamp')) # .annotate()的作用不是分组,而是截取 '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')
6. inclusion_tag(具体操作见项目)
from django import template
from django.db.models import Count
from blog.models import * register = template.Library() @register.inclusion_tag("classification.html") # inclusion_tag()中的参数表示所要引入的一套模板文件
def get_classification_data(username): # get_classification_data() 这个方法一旦被调用,它会先执行下面的数据查询,查询完之后会把下面的字典返回 "classification.html" 这个模板文件(没有返回给调用者),因为 "classification.html" 文件会需要下面的这几个变量;下面的变量传给 "classification.html"之后会进行 render 渲染,渲染成一堆完整的 html标签
user = UserInfo.objects.filter(username=username).first() # 查询当前站点对象
blog = user.blog cate_list = Category.objects.filter(blog=blog).values("pk").annotate(c=Count("article__title")).values("title", "c") tag_list = Tag.objects.filter(blog=blog).values("pk").annotate(c=Count("article__title")).values_list("title", "c") date_list = Article.objects.filter(user=user).extra(
select={"y_m_date": "date_format(create_time,'%%Y-%%m')"}).values("y_m_date").annotate(c=Count("nid")).values(
"y_m_date", "c") # annotate()之前要先 .values()
return {"username":username,"blog": blog, "cate_list": cate_list, "tag_list": tag_list, "date_list": date_list}
7. KindEditor上传文件:
uploadJson
指定上传文件的服务器端程序。 数据类型: String
默认值: basePath + ‘php/upload_json.php’
uploadJson对应的value是一个路径
如: {
uploadJson:"/upload/", // uploadJson对应的是一个路径
extraFileUploadParams:{
csrfmiddlewaretoken:$("[name=csrfmiddlewaretoken]").val(), // post请求,需要自己组装数据,所以要加上这个 key-value
filePostName:"upload_img" // 所上传文件对应的 key
} extraFileUploadParams
上传图片、Flash、视音频、文件时,支持添加别的参数一并传到服务器。 数据类型: Array
默认值: {} filePostName
指定上传文件form名称。 数据类型: String
默认值: imgFile
8. BeautifulSoup:
from bs4 import BeautifulSoup
# BeautifulSoup的用法1:获取标签字符串文内容
s = "<h1>hello</h1><span>123</span>" soup = BeautifulSoup(s,"html.parser") # 第一个参数放标签字符串,第二参数放 解析器
print(soup.text) # soup.text 表示 获取s 这个标签字符串的文本
# 打印结果:
# hello123 # BeautifulSoup的用法2:防止xss攻击:过滤出去 <script> 标签
s = "<h1>hello</h1><span>123</span><script>alert(123)</script>"
soup = BeautifulSoup(s,"html.parser")
print(soup.find_all()) # soup.find_all():获取标签字符串中所有的标签对象;列表的形式
# 打印结果: [<h1>hello</h1>,<span>123</span>,<script>alert(123)</script>] for tag in soup.find_all():
if tag.name == "script": # tag.name 表示 标签名(字符串格式)
tag.decompose() # 从 soup 中把 该标签 删除 print(str(soup))
# 打印结果:<h1>hello</h1><span>123</span>
9. 图片验证码
from random import randint
import random
def get_random_color(): # 用于随机生成颜色
return (randint(0, 255), randint(0, 255), randint(0, 255)) def get_valid_code_img(request): # 方式四:给生成的图片(画板)中添加文字
from io import BytesIO
# BytesIO是内存管理工具
from PIL import Image, ImageDraw, ImageFont
img = Image.new("RGB", (260, 33), color=get_random_color()) # new()里面有三个参数:第一个表示模式(RGB表示彩色),第二个表示图片宽高(需要和css中设置的宽高一致),第三个表示背景颜色 # 得到一个Image对象img
# 往画板中添加文字
draw = ImageDraw.Draw(img) # 得到一个draw对象 # 可这么理解:用ImageDraw这个画笔往 img 画板上书写
kumo_font = ImageFont.truetype("static/font/KumoFont.ttf",
size=20) # 定义字体;第一个参数是字体样式的路径,第二个是字体大小 # 路径中 static 前不能加 / valid_code_str = "" # 用于保存验证码
for i in range(4):
random_num = str(randint(0, 9)) # 数字
random_lower = chr(randint(97, 122)) # 小写
random_upper = chr(randint(65, 90)) # 大写
random_char = random.choice([random_num, random_lower, random_upper])
draw.text((i * 60 + 30, 5), random_char, get_random_color(),
font=kumo_font) # draw.text():利用draw对象往画板里面书写文字;第一个参数是一个元组(x,y),表示横坐标、纵坐标的距离;第二个参数表示文字内容;第三个参数表示字体颜色;第四个表示字体样式
valid_code_str += random_char # 验证图片的噪点、噪线
width = 260
height = 33 # width 和 height要和前端验证图片的宽高一致
# 噪线
for i in range(5):
x1 = randint(0, width)
y1 = randint(0, height) # (x1,y1)是线的起点
x2 = randint(0, width)
y2 = randint(0, height) # (x2,y2)是线的终点
draw.line((x1, y1, x2, y2), fill=get_random_color())
# 噪点
for i in range(100):
draw.point([randint(0, width), randint(0, height)], fill=get_random_color()) # 在给定的坐标点上画一些点。
x = randint(0, width)
y = randint(0, height)
draw.arc((x, y, x + 4, y + 4), 0, 90, fill=get_random_color()) # 在给定的区域内,在开始和结束角度之间绘制一条弧(圆的一部分)
# 参考链接: https://blog.csdn.net/icamera0/article/details/50747084 # 重点:储存随机生成的验证码(不能用 global 的方式去处理验证码 valid_code_str,因为此时当有其他用户登陆时验证码会被别人刷新掉;正确的方式是把该验证码存到 session 中 )
request.session["valid_code_str"] = valid_code_str # 注意:这句代码执行了三个操作过程 # 内存处理
f = BytesIO() # f就是一个内存句柄
img.save(f, "png") # 把img保存到内存句柄中;# save()之后就能把img保存到内存中
data = f.getvalue() # 把保存到内存中的数据读取出来
# BytesIO会有一个自动清除内存的操作 return data
BBS+Blog项目流程及补充知识点的更多相关文章
- Django学习笔记(19)——BBS+Blog项目开发(3)细节知识点补充
本文将BBS+Blog项目开发中所需要的细节知识点进行补充,其中内容包括KindEditor编辑器的使用,BeautifulSoup 模块及其防XSS攻击,Django中admin管理工具的使用,me ...
- Django学习笔记(18)——BBS+Blog项目开发(2)主体思路及流程
这篇博客主要完成一个BBS+Blog项目,那么主要是模仿博客园的博客思路,使用Django框架进行练习. 准备:项目需求分析 在做一个项目的时候,我们首先做的就是谈清楚项目需求,功能需求,然后才开始写 ...
- Django学习笔记(20)——BBS+Blog项目开发(4)Django如何使用Bootstrap
本文学习如何通过Django使用Bootstrap.其实在之前好几个Django项目中已经尝试使用过了Bootstrap,而且都留有学习记录,我已经大概有了一个大的框架,那么本文就从头再走一遍流程,其 ...
- Django学习笔记(17)——BBS+Blog项目开发(1)验证码功能的实现
本文主要学习验证码功能的实现,为了项目BBS+Blog项目打下基础. 为了防止机器人频繁登陆网站或者破坏分子恶意登陆,很多用户登录和注册系统都提供了图形验证码功能. 验证码(CAPTCHA)是“Com ...
- 项目实战:BBS+Blog项目开发
01-博客系统之功能需求 02-博客系统之表结构设计1 03-博客系统之表结构设计2 04-博客系统之表结构设计3 05-博客系统之表结构设计4 06-博客系统之表机构设计5 07-博客系统之创建系统 ...
- BBS+Blog项目代码
项目目录结构: cnblog/ |-- blog/(APP) |-- migrations(其中文件略) |-- templatetags/ |-- my_tags.py |-- utils/ |-- ...
- BBS+Blog项目开发
BBS+Blog项目开发 目前本项目已经上线,可以直接在GEEK浏览本项目效果:GEEK 1.项目需求 基于ajax和用户认证组件实现登录验证 基于ajax和form组件实现注册功能 系统首页文章列表 ...
- Django blog项目知识点总结
数据库操作部分 当我们在Django项目中的models.py下写好创建表的代码后.为了创建好这些数据库表,我们再一次请出我的工程管理助手 manage.py.激活虚拟环境,切换到 manage.py ...
- Vue 简单的总结四(项目流程,DIY脚手架、vue-cli的使用)
项目流程 1.下载 cdn 2.引包 vue-router依赖vue vue-router.js 3.如果是模块化机制 Vue.use(vue-router) 4.创建示例 let Home = {/ ...
随机推荐
- HttpURLConnection教程
1.Class Overview An URLConnection for HTTP (RFC 2616) used to send and receive data over the web. Da ...
- Multitenant best Practice clone pdb seed and Clone a Pluggable Database – 12c Edition
1. 1.Tnsnames when connecting to either Container or Pluggable instance The tnsnames.ora should be c ...
- C# PropertyInfo(官网)
using System; using System.Reflection; class Module1 { public static void Main() { // This variable ...
- c#.net 正则匹配以特定字符串开头,以特定字符串结尾
string[] unit = Getunit(result40, "(?<=(开始字符串))[.\\s\\S]*?(?=(结束字符串))"); private string ...
- turn协议的工作原理
Allocate请求 客户端通过发送Allocate请求给STUN服务器,从而让STUN服务器为A用户开启一个relay端口. a) 客户端A向STUN Port发送Allocate请求(图中 ...
- [BZOJ2190][SDOI2008]仪仗队 数学
题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=2190 看到这道题首先想到了NOI2010的能量采集,这不就是赤裸裸的弱化版吗?直接上莫比乌 ...
- javascript中闭包与作用域的理解
很多js的框架与插件编写都用到了闭包,所以,阅读和掌握闭包很有必要.最近学习vue框架时,经常会猜想很多功能的native js实现,很多都应用到了闭包,闭包除了目前已知的一些特性,如:可以保持局部变 ...
- 【JavaScript】之函数的this
提起函数中的this是很多初学者较为苦恼的,也是很多工作一段时间的人也存在误解的,你问this指向的是谁,大多数人会随口一答当然是指向调用这个函数的元素,当然这也没什么错,可是函数的调用方法不同thi ...
- 简洁大方的wordpress主题,不容错过的主题,附带主题源码下载
cu主题是由疯狂的大叔设计,界面简洁大方是它最大的特点之一. 手残君也比较喜爱这款主题,在使用的过程中,根据手残君的个人习惯,对其进行了优化. 标题优化 标题居中显示 增加标题div背景色 标题div ...
- pandas 选择某几列
转自:https://blog.csdn.net/aaa_aaa1sdf/article/details/77414387 col_n = ['名称','收盘价','日期'] a = pd.DataF ...