项目实战:BBS+Blog项目开发
01-博客系统之功能需求
02-博客系统之表结构设计1
03-博客系统之表结构设计2
04-博客系统之表结构设计3
05-博客系统之表结构设计4
06-博客系统之表机构设计5
07-博客系统之创建系统与迁移表
08-博客系统之登录页面设计
09-博客系统之验证码图片的生成1
10-博客系统之验证码图片的生成2
11-博客系统之验证码图片的噪点燥线
12-博客系统之验证码刷新
13-博客系统之保存验证码字符串
14-博客系统之登录验证
15-博客系统之登录验证代码优化
16-博客系统之滑动验证码作业
17-博客系统之基于form组件的注册页面设计1
18-博客系统之注册页面的默认头像
19-博客系统之注册页面的头像预览功能
20-博客系统之基于Ajax的form data数据
21-博客系统之基于Ajax提交from data的数据的优化
22-博客系统之基于Ajax在注册页面显示错误消息1
23-博客系统之基于Ajax在注册页面显示错误消息2
24-博客系统之form组件的局部钩子与全局钩子的应用
25-博客系统之FileFiled字段
26-博客系统之media配置1
27-博客系统之media配置2
28-博客系统之生成用户对象的代码优化
29-博客系统之系统首页的导航区域
30-博客系统之系统首页的主体布局
31-博客系统之admin的简单实用
32-博客系统之基于admin录入文章数据
33-博客系统之系统首页的文章列表的渲染1
34-博客系统之系统首页的文章列表的渲染2
35-博客系统之个人站点的页面文章的查询
36-博客系统之个人站点页面的标签与分类查询
37-博客系统之个人站点页面的日期查询1
38-博客系统之个人站点页面的日期查询2
39-博客系统之个人站点页面的日期查询3
40-博客系统之个人站点页面的日期查询4
41-博客系统之个人站点页面的渲染布局1
42-博客系统之个人站点页面的渲染布局2
43-博客系统之个人站点页面的渲染布局3
44-博客系统之个人站点的跳转过滤功能的实现1
45-博客系统之个人站点页面的跳转过滤功能实现2
46-博客系统之个人站点页面的跳转过滤功能的实现3
47-博客系统之文章详情页的设计
48-博客系统之文章详情页的构建
49-博客系统之文章详情页的inclution_tag
50-博客系统之文章详情页渲染标签的标签字符串转义1
51-博客系统之文章详情页渲染的标签字符串转义2
52-博客系统之文章点赞样式的构建
53-博客系统之文章点赞事件的绑定
54-博客系统之文章点赞的保存
55-博客系统之文章点赞数的数据同步
56-博客系统之文章点赞的提示重复操作
57-博客系统之文章点赞数的Ajax更新
58-博客系统之文章点赞代码的优化
59-博客系统之评论功能的实现流程
60-博客系统之评论样式
61-博客系统之提交根评论
62-博客系统之render显示根评论
63-博客系统之Ajax显示根评论
64-博客系统之回复按钮事件
65-博客系统之提交子评论
66-博客系统之render显示
67-博客系统之Ajax显示子评论的思路
68-博客系统之评论树简介
69-博客系统之评论树的请求数据
70-博客系统之展开评论树
71-博客系统之展开评论树2
72-博客系统之评论树的思考1
73-博客系统之评论树的思考2
74-博客系统之评论事务操作
75-博客系统之评论的邮件发送new
76-博客系统之后台管理页面的编辑功能
77-博客系统之后台管理的编辑器引入和参数
78-博客系统之文本编辑器的上传功能1
79-博客系统之文本编辑器的上传功能2
80-博客系统之文章摘要的保存
81-博客系统之bs4的简单应用
82-博客系统之bs4模块防御xss攻击
01-博客系统之功能需求
1、项目开发流程;
- 1、搞清楚需求(与产品经理对接);
- 2、设计表结构;
- 3、按照每一个功能(需求)进行开发;
2、什么是博客?
- 博客园;
- CSDN;
- 微信朋友圈;
- 新浪博客;
- 新浪微博;
3、基础功能分析;
- 1、登录-基于用户认证组件和Ajax实现登录验证(图片验证码);
- 2、注册-基于Ajax和form组件实现;
- 3、设计系统首页(文章列表的渲染);
- 4、设计个人站点页面;
- 5、文章详情页;
- 6、实现点赞功能(基于Ajax);
- 7、实现文章评论功能(对子评论的评论);
- 8、后台管理页面;
- 9、富文本编辑框和防止XSS攻击;
4、功能测试;
5、项目部署上线;
02-博客系统之表结构设计1
03-博客系统之表结构设计2
04-博客系统之表结构设计3
05-博客系统之表结构设计4
06-博客系统之表机构设计5
07-博客系统之创建系统与迁移表
1、注释默认的sqllite数据库,修改为MySQL数据库;
2、编写models.py;
from django.db import models # Create your models here. from django.contrib.auth.models import AbstractUser 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")
create_time = models.DateTimeField(verbose_name='创建时间', auto_now_add=True) blog = models.OneToOneField(to='Blog', to_field='nid', null=True, on_delete=models.CASCADE) def __str__(self):
return self.username class Blog(models.Model):
"""
博客信息
"""
nid = models.AutoField(primary_key=True)
title = models.CharField(verbose_name='个人博客标题', max_length=64)
site_name = models.CharField(verbose_name='站点名称', max_length=64)
theme = models.CharField(verbose_name='博客主题', max_length=32) def __str__(self):
return self.title class Category(models.Model):
"""
博主个人文章分类表
"""
nid = models.AutoField(primary_key=True)
title = models.CharField(verbose_name='分类标题', max_length=32)
blog = models.ForeignKey(verbose_name='所属博客', to='Blog', to_field='nid', on_delete=models.CASCADE) def __str__(self):
return self.title class Tag(models.Model):
nid = models.AutoField(primary_key=True)
title = models.CharField(verbose_name='标签名称', max_length=32)
blog = models.ForeignKey(verbose_name='所属博客', to='Blog', to_field='nid', on_delete=models.CASCADE) def __str__(self):
return self.title class Article(models.Model):
nid = models.AutoField(primary_key=True)
title = models.CharField(max_length=50, verbose_name='文章标题')
desc = models.CharField(max_length=255, verbose_name='文章描述')
create_time = models.DateTimeField(verbose_name='创建时间', auto_now_add=True)
content = models.TextField() comment_count = models.IntegerField(default=0)
up_count = models.IntegerField(default=0)
down_count = models.IntegerField(default=0) user = models.ForeignKey(verbose_name='作者', to='UserInfo', to_field='nid', on_delete=models.CASCADE)
category = models.ForeignKey(to='Category', to_field='nid', null=True, on_delete=models.CASCADE)
tags = models.ManyToManyField(
to="Tag",
through='Article2Tag',
through_fields=('article', 'tag'),
) def __str__(self):
return self.title class Article2Tag(models.Model):
nid = models.AutoField(primary_key=True)
article = models.ForeignKey(verbose_name='文章', to="Article", to_field='nid', on_delete=models.CASCADE)
tag = models.ForeignKey(verbose_name='标签', to="Tag", to_field='nid', on_delete=models.CASCADE) class Meta:
unique_together = [
('article', 'tag'),
] def __str__(self):
v = self.article.title + "---" + self.tag.title
return v class ArticleUpDown(models.Model):
"""
点赞表
""" nid = models.AutoField(primary_key=True)
user = models.ForeignKey('UserInfo', null=True, on_delete=models.CASCADE)
article = models.ForeignKey("Article", null=True, on_delete=models.CASCADE)
is_up = models.BooleanField(default=True) class Meta:
unique_together = [
('article', 'user'),
] class Comment(models.Model):
""" 评论表 """
nid = models.AutoField(primary_key=True)
article = models.ForeignKey(verbose_name='评论文章', to='Article', to_field='nid', on_delete=models.CASCADE)
user = models.ForeignKey(verbose_name='评论者', to='UserInfo', to_field='nid', on_delete=models.CASCADE)
content = models.CharField(verbose_name='评论内容', max_length=255)
create_time = models.DateTimeField(verbose_name='创建时间', auto_now_add=True)
parent_comment = models.ForeignKey('self', null=True, on_delete=models.CASCADE) def __str__(self):
return self.content
3、settingsp.py添加AUTH_USER_MODEL=“blog.UserInfo”;
"""
Django settings for cnblogs project. Generated by 'django-admin startproject' using Django 2.1. For more information on this file, see
https://docs.djangoproject.com/en/2.1/topics/settings/ For the full list of settings and their values, see
https://docs.djangoproject.com/en/2.1/ref/settings/
""" import os # Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) # Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/2.1/howto/deployment/checklist/ # SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = '97e0#&=bl*0zl@99!_#4o*6fs=e&3-6@8rdq0clas*hojx6!5z' # SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True ALLOWED_HOSTS = [] # Application definition INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'blog.apps.BlogConfig',
] MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
] ROOT_URLCONF = 'cnblogs.urls' TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [os.path.join(BASE_DIR, 'templates')]
,
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
] WSGI_APPLICATION = 'cnblogs.wsgi.application' # Database
# https://docs.djangoproject.com/en/2.1/ref/settings/#databases # DATABASES = {
# 'default': {
# 'ENGINE': 'django.db.backends.sqlite3',
# 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
# }
# } DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',#数据库的引擎为MySQL;
'NAME': 'cnblogs',#要连接的数据库实例的名称,连接前需要已经完成创建;
'USER': 'root',#MySQL数据库的用户名;
'PASSWORD': 'Tqtl911!@%*)',#MySQL数据库的密码;
'HOST': '47.95.121.154',#MySQL数据库服务器的IP地址;
'PORT': ''#MySQL数据库的款口号,默认3306;
}
} # Password validation
# https://docs.djangoproject.com/en/2.1/ref/settings/#auth-password-validators AUTH_PASSWORD_VALIDATORS = [
{
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
},
] # Internationalization
# https://docs.djangoproject.com/en/2.1/topics/i18n/ LANGUAGE_CODE = 'en-us' TIME_ZONE = 'UTC' USE_I18N = True USE_L10N = True USE_TZ = True # Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/2.1/howto/static-files/ STATIC_URL = '/static/' AUTH_USER_MODEL = 'blog.UserInfo' LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'handlers': {
'console':{
'level':'DEBUG',
'class':'logging.StreamHandler',
},
},
'loggers': {
'django.db.backends': {
'handlers': ['console'],
'propagate': True,
'level':'DEBUG',
},
}
}
4、进行数据库迁移操作;
python manage.py makemigrations
python manage.py migrate
08-博客系统之登录页面设计
1、登录页面设计;
2、login.html设计之引入本地Bootstrap静态文件;
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<link rel="stylesheet" href="/static/blog/bs/css/bootstrap.css">
</head>
<body>
<h3>登录页面</h3>
<div class="container">
<div class="row">
<div class="col-md-6 col-lg-offset-3">
<form action="">
<div class="form-group">
<label for="user">用户名</label>
<input type="text" id="user" class="form-control">
</div>
<div class="form-group">
<label for="pwd">密码</label>
<input type="password" id="pwd" class="form-control">
</div >
<input type="button" class="btn btn-default pull-right login_btn" value="submit">
</form>
</div>
</div>
</div>
</body>
</html>
3、views.py;
from django.shortcuts import render # Create your views here. def login(request):
return render(request,"login.html")
4、settings.py之配置STATICFILE_DIRS目录;
STATICFILES_DIRS = [
os.path.join(BASE_DIR,"static")
]
09-博客系统之验证码图片的生成1
1、PIL图像处理模块初识;
PIL:Python Imaging Library,已经是Python平台事实上的图像处理标准库了,PIL功能非常强大,且API非常简单易用。
由于PIL仅支持到Python 2.7,加上年久失修,于是一群志愿者在PIL的基础上创建了兼容的版本,名字叫Pillow,支持最新Python 3.x,又加入了许多新特性,因此,我们可以直接安装使用Pillow。
pip install pillow
2、打开图片的两种方式;
from django.shortcuts import render,HttpResponse,redirect # Create your views here. def login(request):
return render(request,"login.html") import random
def get_validCode_img(request):
def get_random_color():
return (random.randint(0,255),random.randint(0,255),random.randint(0,255))
#方式1-with open方法;
# with open("lufei.jpg","rb") as f:
# data = f.read() #方式2;pip install pillow;
from PIL import Image
img = Image.new("RGB",(270,40),color=get_random_color())
with open("validCode.png","wb") as f:
img.save(f,"png")
with open("validCode.png","rb") as f:
data = f.read()
return HttpResponse(data)
3、login.html;
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<link rel="stylesheet" href="/static/blog/bs/css/bootstrap.css">
</head>
<body>
<h3>登录页面</h3>
<div class="container">
<div class="row">
<div class="col-md-6 col-lg-offset-3">
<form action="">
{% csrf_token %}
<div class="form-group">
<label for="user">用户名</label>
<input type="text" id="user" class="form-control">
</div>
<div class="form-group">
<label for="pwd">密码</label>
<input type="password" id="pwd" class="form-control">
</div >
<div class="form-group">
<label for="pwd">验证码</label>
<div class="row">
<div class="col-md-6">
<input type="text" class="valid_code form-control">
</div>
<div class="col-md-6"></div>
<img width="" height="" src="/get_validCode_img/" alt="">
</div>
</div>
<input type="button" class="btn btn-default pull-right login_btn" value="submit">
</form>
</div>
</div>
</div>
</body>
</html>
10-博客系统之验证码图片的生成2
1、生成随机字符串的两种方法;
2、引入string、ImageDraw、ImageFont模块;
from django.shortcuts import render,HttpResponse,redirect # Create your views here. def login(request):
return render(request,"login.html") import random
def get_validCode_img(request):
def get_random_color():
return (random.randint(0,255),random.randint(0,255),random.randint(0,255))
#方式1-with open方法;
# with open("lufei.jpg","rb") as f:
# data = f.read() #方式2;pip install pillow;
# from PIL import Image
# img = Image.new("RGB",(270,40),color=get_random_color())
# with open("validCode.png","wb") as f:
# img.save(f,"png")
# with open("validCode.png","rb") as f:
# data = f.read() #方式3:将数据放置于内存中,加快处理速度;
# from PIL import Image
# from io import BytesIO
#
# img = Image.new("RGB",(270,40),color=get_random_color())
# f = BytesIO()
# img.save(f,"png")
# data = f.getvalue() #方式4:向图像区域他添加噪点,和字符串;
from PIL import Image,ImageDraw,ImageFont
from io import BytesIO
import random
char = str(random.randint(0,9)) img = Image.new("RGB",(270,40),color=get_random_color())
draw = ImageDraw.Draw(img)
kumo_font = ImageFont.truetype("static/font/kumo.ttf",size=28) #生成随机字符串;
#方法1:
# for i in range(0,5):
# import string
# random_char = ' '.join(random.sample(string.ascii_lowercase + string.ascii_uppercase,1)) # d4}5c+/m|97e@"16]s
# draw.text((i*50+20,5),random_char,get_random_color(),font=kumo_font) #方法2:
for i in range(5):
random_num = str(random.randint(0,9))
random_lowercase = chr(random.randint(95,122))
random_uppercase = chr(random.randint(65,90))
random_char = random.choice([random_num,random_lowercase,random_uppercase])
draw.text((i*50+20,5),random_char,get_random_color(),font=kumo_font) #进行画图
#draw.line()
#draw.point()
f = BytesIO()
img.save(f,"png")
data = f.getvalue() return HttpResponse(data)
11-博客系统之验证码图片的噪点燥线
1、添加噪点、燥线;
#给图片添加上噪点;
width = 270
height = 40
for i in range(5):
x1 = random.randint(0,width)
x2 = random.randint(0,width)
y1 = random.randint(0,height)
y2 = random.randint(0,height)
draw.line((x1,y1,x2,y2),fill=get_random_color()) for i in range(10):
draw.point([random.randint(0,width),random.randint(0,height)],fill=get_random_color())
x = random.randint(0,width)
y = random.randint(0,height)
draw.arc((x,y,x+4,y+4),0,90,fill=get_random_color())
2、展示效果;
12-博客系统之验证码刷新
1、先引入jQuery文件;
2、添加鼠标点击事件click();
3、给图片添加一个id属性,然后通过js代码添加事件;
<script src="/static/blog/js/jquery-3.3.1.min.js"></script>
<script>
//刷新图片验证码;
$("#valid_code_img").click(function () {
$(this)[0].src+="?"
})
</script>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<link rel="stylesheet" href="/static/blog/bs/css/bootstrap.css">
</head>
<body>
<h3>登录页面</h3>
<div class="container">
<div class="row">
<div class="col-md-6 col-lg-offset-3">
<form action="">
{% csrf_token %}
<div class="form-group">
<label for="user">用户名</label>
<input type="text" id="user" class="form-control">
</div>
<div class="form-group">
<label for="pwd">密码</label>
<input type="password" id="pwd" class="form-control">
</div >
<div class="form-group">
<label for="pwd">验证码</label>
<div class="row">
<div class="col-md-6">
<input type="text" class="valid_code form-control">
</div>
<div class="col-md-6">
<img width="260" height="45" id="valid_code_img" src="/get_validCode_img/" alt="">
</div>
</div>
</div>
<input type="button" class="btn btn-default pull-right login_btn" value="submit">
</form>
</div>
</div>
</div>
<script src="/static/blog/js/jquery-3.3.1.min.js"></script>
<script>
//刷新图片验证码;
$("#valid_code_img").click(function () {
$(this)[0].src+="?"
})
</script> </body>
</html>
13-博客系统之保存验证码字符串
1、保存验证字符串之Ajax引入;
2、views.py;
from django.shortcuts import render,HttpResponse,redirect # Create your views here. from django.http import JsonResponse def login(request):
if request.method == "POST":
response = {"user":None,"msg":None}
user = request.POST.get("user")
pwd = request.POST.get("pwd")
valid_code = request.POST.get("valid_code") valid_code_str = request.session.get("valid_code_str")
#print("valid_code_str",valid_code_str,type(valid_code_str))
#print("valid_code",valid_code,type(valid_code))
if valid_code.upper() == valid_code_str.upper():
pass
else:
response["msg"] = "valid code error!"
return JsonResponse(response)
return render(request,"login.html") import random
def get_validCode_img(request):
def get_random_color():
return (random.randint(0,255),random.randint(0,255),random.randint(0,255))
#方式1-with open方法;
# with open("lufei.jpg","rb") as f:
# data = f.read() #方式2;pip install pillow;
# from PIL import Image
# img = Image.new("RGB",(270,40),color=get_random_color())
# with open("validCode.png","wb") as f:
# img.save(f,"png")
# with open("validCode.png","rb") as f:
# data = f.read() #方式3:将数据放置于内存中,加快处理速度;
# from PIL import Image
# from io import BytesIO
#
# img = Image.new("RGB",(270,40),color=get_random_color())
# f = BytesIO()
# img.save(f,"png")
# data = f.getvalue() #方式4-向图像区域他添加噪点,和字符串;
from PIL import Image,ImageDraw,ImageFont
from io import BytesIO
import random
char = str(random.randint(0,9)) img = Image.new("RGB",(270,40),color=get_random_color())
draw = ImageDraw.Draw(img)
kumo_font = ImageFont.truetype("static/font/BASKVILL.TTF",size=28) #保存随机字符串;
valid_code_str = ""
#生成随机字符串;
#方法1:
for i in range(0,5):
import string
random_char = ' '.join(random.sample(string.ascii_lowercase + string.ascii_uppercase,1)) # d4}5c+/m|97e@"16]s
draw.text((i*50+20,5),random_char,get_random_color(),font=kumo_font)
#保存验证码字符串;
valid_code_str+= random_char #方法2:
# for i in range(500):
# random_num = str(random.randint(0,9))
# random_lowercase = chr(random.randint(95,122))
# random_uppercase = chr(random.randint(65,90))
# random_char = random.choice([random_num,random_lowercase,random_uppercase])
# draw.text((i*50+20,5),random_char,get_random_color(),font=kumo_font) #进行画图;
#draw.line()
#draw.point() #给图片添加上噪点;
width = 270
height = 40
for i in range(5):
x1 = random.randint(0,width)
x2 = random.randint(0,width)
y1 = random.randint(0,height)
y2 = random.randint(0,height)
draw.line((x1,y1,x2,y2),fill=get_random_color()) for i in range(10):
draw.point([random.randint(0,width),random.randint(0,height)],fill=get_random_color())
x = random.randint(0,width)
y = random.randint(0,height)
draw.arc((x,y,x+4,y+4),0,90,fill=get_random_color())
print("valid_code_str",valid_code_str) request.session["valid_code_str"] = valid_code_str
'''
1、生成随机字符串;
2、COOKIE{"sessionid":fdsfdsfds}
3、django-session表生成记录;
'''
f = BytesIO()
img.save(f, "png")
data = f.getvalue()
return HttpResponse(data)
login.html;
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<link rel="stylesheet" href="/static/blog/bs/css/bootstrap.css">
</head>
<body>
<h3>登录页面</h3>
<div class="container">
<div class="row">
<div class="col-md-6 col-lg-offset-3">
<form >
{% csrf_token %}
<div class="form-group">
<label for="user">用户名</label>
<input type="text" id="user" class="form-control">
</div>
<div class="form-group">
<label for="pwd">密码</label>
<input type="password" id="pwd" class="form-control">
</div >
<div class="form-group">
<label for="pwd">验证码</label>
<div class="row">
<div class="col-md-6">
<input type="text" class="form-control" id="valid_code">
</div>
<div class="col-md-6">
<img width="260" height="45" id="valid_code_img" src="/get_validCode_img/" alt="">
</div>
</div>
</div>
<input type="button" class="btn btn-default pull-right login_btn" value="submit">
</form>
</div>
</div>
</div>
<script src="/static/blog/js/jquery-3.3.1.min.js"></script>
<script>
//刷新图片验证码;
$("#valid_code_img").click(function () {
$(this)[0].src+="?"
}); //登陆过验证;
$(".login_btn").click(function () {
$.ajax({
url:"",
type:"post",
data:{
user:$("#user").val(),
pwd:$("#pwd").val(),
valid_code:$("#valid_code").val(),
csrfmiddlewaretoken:$("[name='csrfmiddlewaretoken']").val(), },
success:function (data) {
console.log(data) }
})
}) </script> </body>
</html>
14-博客系统之登录验证
1、修改消息提示语;
15-博客系统之登录验证代码优化
1、新增utils目录,将功能迁移至此;
views.py;
from django.shortcuts import render,HttpResponse,redirect # Create your views here. from django.http import JsonResponse
from django.contrib import auth def login(request):
if request.method == "POST":
response = {"user":None,"msg":None}
user = request.POST.get("user")
pwd = request.POST.get("pwd")
valid_code = request.POST.get("valid_code") valid_code_str = request.session.get("valid_code_str")
#print("valid_code_str",valid_code_str,type(valid_code_str))
#print("valid_code",valid_code,type(valid_code))
if valid_code.upper() == valid_code_str.upper():
user = auth.authenticate(username = user,password = pwd)
if user:
response["user"] = user.username
else:
response["msg"] = "用户名或者密码错误!"
else:
response["msg"] = "验证码错误!"
return JsonResponse(response)
return render(request,"login.html") def get_valid_code_img(request):
"""
基于PIL模块动态生成响应状态码图片;
:param request:
:return:
"""
from blog.utils.validCode import get_valid_code_img
data = get_valid_code_img(request)
return HttpResponse(data) def index(request):
return render(request,"index.html") """
小结:
1、一次请求伴随着多次请求;
2、PIL模块的掌握;
3、session存储;
4、验证码刷新,基于js鼠标的click()事件进行;
"""
validCode.py;
#!/usr/bin/env python3
# -*- coding:utf-8 -*-
# __Author__:TQTL911
# Version:python3.6.6
# Time:2018/8/23 21:30 import random def get_random_color():
return (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255)) def get_valid_code_img(request):
# 方式1-with open方法;
# with open("lufei.jpg","rb") as f:
# data = f.read() # 方式2;pip install pillow;
# from PIL import Image
# img = Image.new("RGB",(270,40),color=get_random_color())
# with open("validCode.png","wb") as f:
# img.save(f,"png")
# with open("validCode.png","rb") as f:
# data = f.read() # 方式3:将数据放置于内存中,加快处理速度;
# from PIL import Image
# from io import BytesIO
#
# img = Image.new("RGB",(270,40),color=get_random_color())
# f = BytesIO()
# img.save(f,"png")
# data = f.getvalue() # 方式4-向图像区域他添加噪点,和字符串;
from PIL import Image, ImageDraw, ImageFont
from io import BytesIO
import random
char = str(random.randint(0, 9)) img = Image.new("RGB", (270, 40), color=get_random_color())
draw = ImageDraw.Draw(img)
kumo_font = ImageFont.truetype("static/font/BASKVILL.TTF", size=28) # 保存随机字符串;
valid_code_str = ""
# 生成随机字符串;
# 方法1:
for i in range(0, 5):
import string
random_char = ' '.join(
random.sample(string.ascii_lowercase + string.ascii_uppercase, 1)) # d4}5c+/m|97e@"16]s
draw.text((i * 50 + 20, 5), random_char, get_random_color(), font=kumo_font)
# 保存验证码字符串;
valid_code_str += random_char # 方法2:
# for i in range(500):
# random_num = str(random.randint(0,9))
# random_lowercase = chr(random.randint(95,122))
# random_uppercase = chr(random.randint(65,90))
# random_char = random.choice([random_num,random_lowercase,random_uppercase])
# draw.text((i*50+20,5),random_char,get_random_color(),font=kumo_font) # 进行画图;
# draw.line()
# draw.point() # 给图片添加上噪点;
width = 270
height = 40
for i in range(5):
x1 = random.randint(0, width)
x2 = random.randint(0, width)
y1 = random.randint(0, height)
y2 = random.randint(0, height)
draw.line((x1, y1, x2, y2), fill=get_random_color()) for i in range(10):
draw.point([random.randint(0, width), random.randint(0, height)], fill=get_random_color())
x = random.randint(0, width)
y = random.randint(0, height)
draw.arc((x, y, x + 4, y + 4), 0, 90, fill=get_random_color())
print("valid_code_str", valid_code_str) request.session["valid_code_str"] = valid_code_str
'''
1、生成随机字符串;
2、COOKIE{"sessionid":fdsfdsfds};
3、django-session表生成记录;
'''
f = BytesIO()
img.save(f, "png")
data = f.getvalue()
return data
2、新增图片验证码1秒刷新功能,基于js的setTimeout()方法实现;
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<link rel="stylesheet" href="/static/blog/bs/css/bootstrap.css">
</head>
<body>
<h3>登录页面</h3>
<div class="container">
<div class="row">
<div class="col-md-6 col-lg-offset-3">
<form >
{% csrf_token %}
<div class="form-group">
<label for="user">用户名</label>
<input type="text" id="user" class="form-control">
</div>
<div class="form-group">
<label for="pwd">密码</label>
<input type="password" id="pwd" class="form-control">
</div >
<div class="form-group">
<label for="pwd">验证码</label>
<div class="row">
<div class="col-md-6">
<input type="text" class="form-control" id="valid_code">
</div>
<div class="col-md-6">
<img width="260" height="45" id="valid_code_img" src="/get_validCode_img/" alt="">
</div>
</div>
</div>
<input type="button" class="btn btn-default login_btn" value="submit"><span class="error"></span>
</form>
</div>
</div>
</div>
<script src="/static/blog/js/jquery-3.3.1.min.js"></script>
<script>
//刷新图片验证码;
$("#valid_code_img").click(function () {
$(this)[0].src+="?"
}); //登陆过验证;
$(".login_btn").click(function () {
$.ajax({
url:"",
type:"post",
data:{
user:$("#user").val(),
pwd:$("#pwd").val(),
valid_code:$("#valid_code").val(),
csrfmiddlewaretoken:$("[name='csrfmiddlewaretoken']").val(), },
success:function (data) {
console.log(data);
if (data.user){
location.href = "/index/"
}else {
$(".error").text(data.msg).css({"color":"red","margin-left":"10px"});
//清空错误消息提示,1000毫秒;
setTimeout(function () {
$(".error").text("")
},1000)
}
}
})
}); </script> </body>
</html>
16-博客系统之滑动验证码作业
17-博客系统之基于form组件的注册页面设计1
1、注册页面的路由配置urls.py;
"""cnblogs URL Configuration The `urlpatterns` list routes URLs to views. For more information please see:
https://docs.djangoproject.com/en/2.1/topics/http/urls/
Examples:
Function views
1. Add an import: from my_app import views
2. Add a URL to urlpatterns: path('', views.home, name='home')
Class-based views
1. Add an import: from other_app.views import Home
2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
Including another URLconf
1. Import the include() function: from django.urls import include, path
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
"""
from django.contrib import admin
from django.urls import path from blog import views
urlpatterns = {
path('admin/', admin.site.urls),
path('login/', views.login),
path('index/', views.index),
path('get_validCode_img/', views.get_valid_code_img),
path('register/', views.register),
}
2、注册页面的views.py视图函数编写;
from django.shortcuts import render,HttpResponse,redirect # Create your views here. from django.http import JsonResponse
from django.contrib import auth def login(request):
"""
登录;
:param request:
:return:
"""
if request.method == "POST":
response = {"user":None,"msg":None}
user = request.POST.get("user")
pwd = request.POST.get("pwd")
valid_code = request.POST.get("valid_code") valid_code_str = request.session.get("valid_code_str")
if valid_code.upper() == valid_code_str.upper():
user = auth.authenticate(username = user,password = pwd)
if user:
response["user"] = user.username
else:
response["msg"] = "用户名或者密码错误!"
else:
response["msg"] = "验证码错误!"
return JsonResponse(response)
return render(request,"login.html") def get_valid_code_img(request):
"""
基于PIL模块动态生成响应状态码图片;
:param request:
:return:
"""
from blog.utils.validCode import get_valid_code_img
data = get_valid_code_img(request)
return HttpResponse(data) def index(request):
"""
首页;
:param request:
:return:
"""
return render(request,"index.html") from django import forms
from django.forms import widgets class UserForm(forms.Form):
user = forms.CharField(max_length=32,label="用户名",widget=widgets.TextInput(attrs={"class":"form-control"}))
pwd = forms.CharField(max_length=32,label="密码",widget=widgets.PasswordInput(attrs={"class":"form-control"}))
r_pwd = forms.CharField(max_length=32,label="确认密码",widget=widgets.PasswordInput(attrs={"class":"form-control"}))
email = forms.EmailField(max_length=32,label="注册邮箱",widget=widgets.EmailInput(attrs={"class":"form-control"})) def register(request):
"""
注册;
:param request:
:return:
"""
form = UserForm()
return render(request,"register.html",{"form":form})
3、注册页面的模板templates下的register.html编写(基于forms组件循环生成并添加约束信息);
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>注册页面</title>
<link rel="stylesheet" href="/static/blog/bs/css/bootstrap.css">
</head>
<body>
<h3>注册页面</h3>
<div class="container">
<div class="row">
<div class="col-md-6 col-lg-offset-3">
<form >
{% csrf_token %}
<!--进行渲染form对象-->
{% for field in form %}
<div class="form-group">
<label for="user">{{ field.label }}</label>
{{ field }}
</div>
{% endfor %}
<div class="form-group">
<label for="avatar">头像</label>
<input type="file">
</div> <input type="button" class="btn btn-default login_btn" value="提交">
</form>
</div>
</div>
</div>
<script src="/static/blog/js/jquery-3.3.1.min.js"></script> </body>
</html>
18-博客系统之注册页面的默认头像
1、点击头像相当于点击input标签的功能实现;
- 1)将label标签包含img标签;
- 2)将label标签的for值等同于input标签的id值;
- 3)为input标签设置display:none的隐藏属性;
2、register.html;
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>注册页面</title>
<link rel="stylesheet" href="/static/blog/bs/css/bootstrap.css">
<style>
#avatar{
display: none;
}
#avatar_img{
margin-left: 20px;
}
</style>
</head>
<body>
<h3>注册页面</h3>
<div class="container">
<div class="row">
<div class="col-md-6 col-lg-offset-3">
<form >
{% csrf_token %}
<!--进行渲染form对象-->
{% for field in form %}
<div class="form-group">
<label for="{{ field.auto_id }}">{{ field.label }}</label>
{{ field }}
</div>
{% endfor %}
<div class="form-group">
<label for="avatar">
头像
<img id="avatar_img" width="60px" height="60px" src="/static/blog/img/default.png" alt="">
</label>
<input type="file" id="avatar" >
</div>
<input type="button" class="btn btn-default login_btn" value="提交">
</form>
</div>
</div>
</div>
<script src="/static/blog/js/jquery-3.3.1.min.js"></script> </body>
</html>
19-博客系统之注册页面的头像预览功能
1、头像预览功能实现的js代码;
2、获取上传后文件的方法;
- $("#avatar")[0].files[0],得到如下内容:File(11414) {name: "keep.jpg", lastModified: 1531732278496, lastModifiedDate: Mon Jul 16 2018 17:11:18 GMT+0800 (中国标准时间), webkitRelativePath: "", size: 11414, …}
<script>
$("#avatar").change(function () {
//1、获取用户选中的文件对象;
var file_obj = $(this)[0].files[0];
//2、获取文件对象的路径;
var reader = new FileReader();
reader.readAsDataURL(file_obj);
//3、修改img的src属性值,src= 文件对象的路径;
reader.onload = function(){
$("#avatar_img").attr("src",reader.result)
}; })
</script>
20-博客系统之基于Ajax的form data数据
1、通过Ajax方法实现向后端传送数据;
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>注册页面</title>
<link rel="stylesheet" href="/static/blog/bs/css/bootstrap.css">
<style>
#avatar{
display: none;
}
#avatar_img{
margin-left: 20px;
}
</style>
</head>
<body>
<h3>注册页面</h3>
<div class="container">
<div class="row">
<div class="col-md-6 col-lg-offset-3">
<form >
{% csrf_token %}
<!--进行渲染form对象-->
{% for field in form %}
<div class="form-group">
<label for="{{ field.auto_id }}">{{ field.label }}</label>
{{ field }}
</div>
{% endfor %}
<div class="form-group">
<label for="avatar">
头像
<img id="avatar_img" width="60px" height="60px" src="/static/blog/img/default.png" alt="">
</label>
<input type="file" id="avatar" >
</div>
<input type="button" class="btn btn-default reg_btn" value="提交">
</form>
</div>
</div>
</div>
<script src="/static/blog/js/jquery-3.3.1.min.js"></script> <!--编写js代码-->
<script>
$("#avatar").change(function () {
//1、获取用户选中的文件对象;
var file_obj = $(this)[0].files[0];
//2、获取文件对象的路径;
var reader = new FileReader();
reader.readAsDataURL(file_obj);
//3、修改img的src属性值,src= 文件对象的路径;
reader.onload = function(){
$("#avatar_img").attr("src",reader.result)
}; });
//基于Ajax提交事件;
$(".reg_btn").click(function () {
var formdata = new FormData();
formdata.append("user",$("#id_user").val());
formdata.append("pwd",$("#id_pwd").val());
formdata.append("r_pwd",$("#id_r_pwd").val());
formdata.append("email",$("#id_email").val());
formdata.append("avatar",$("#avatar")[0].files[0]);
formdata.append("csrfmiddlewaretoken",$("[name = 'csrfmiddlewaretoken']").val()); $.ajax({
url:"",
type:"post",
contentType:false,
processData:false,
data:formdata,
success:function (data) {
console.log(data) }
})
})
</script> </body>
</html>
2、views.py进行逻辑判断;
from django.shortcuts import render,HttpResponse,redirect # Create your views here. from django.http import JsonResponse
from django.contrib import auth def login(request):
"""
登录;
:param request:
:return:
"""
if request.method == "POST":
response = {"user":None,"msg":None}
user = request.POST.get("user")
pwd = request.POST.get("pwd")
valid_code = request.POST.get("valid_code") valid_code_str = request.session.get("valid_code_str")
if valid_code.upper() == valid_code_str.upper():
user = auth.authenticate(username = user,password = pwd)
if user:
response["user"] = user.username
else:
response["msg"] = "用户名或者密码错误!"
else:
response["msg"] = "验证码错误!"
return JsonResponse(response)
return render(request,"login.html") def get_valid_code_img(request):
"""
基于PIL模块动态生成响应状态码图片;
:param request:
:return:
"""
from blog.utils.validCode import get_valid_code_img
data = get_valid_code_img(request)
return HttpResponse(data) def index(request):
"""
首页;
:param request:
:return:
"""
return render(request,"index.html") from django import forms
from django.forms import widgets
from django.http import JsonResponse class UserForm(forms.Form):
user = forms.CharField(max_length=32,label="用户名",widget=widgets.TextInput(attrs={"class":"form-control"}))
pwd = forms.CharField(max_length=32,label="密码",widget=widgets.PasswordInput(attrs={"class":"form-control"}))
r_pwd = forms.CharField(max_length=32,label="确认密码",widget=widgets.PasswordInput(attrs={"class":"form-control"}))
email = forms.EmailField(max_length=32,label="注册邮箱",widget=widgets.EmailInput(attrs={"class":"form-control"})) def register(request):
"""
注册;
:param request:
:return:
"""
#if request.method == "POST":
if request.is_ajax():
print(request.POST)
form = UserForm(request.POST) response = {"user":None,"msg":None}
if form.is_valid():
response["user"] = form.cleaned_data.get("user")
else:
print(form.cleaned_data)
print(form.errors)
response["msg"] = form.errors
return JsonResponse(response)
form = UserForm()
return render(request,"register.html",{"form":form})
21-博客系统之基于Ajax提交from data的数据的优化
1、使用serializeArray()方法进行优化;
注意犯的错误,Js中的代码多行注释的方法为:/*被注释的内容,而不是<!--被注释的内容-->*/
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>注册页面</title>
<link rel="stylesheet" href="/static/blog/bs/css/bootstrap.css">
<style>
#avatar{
display: none;
}
#avatar_img{
margin-left: 20px;
}
</style>
</head>
<body>
<h3>注册页面</h3>
<div class="container">
<div class="row">
<div class="col-md-6 col-lg-offset-3">
<form id="form">
{% csrf_token %}
<!--进行渲染form对象-->
{% for field in form %}
<div class="form-group">
<label for="{{ field.auto_id }}">{{ field.label }}</label>
{{ field }}
</div>
{% endfor %}
<div class="form-group">
<label for="avatar">
头像
<img id="avatar_img" width="60px" height="60px" src="/static/blog/img/default.png" alt="">
</label>
<input type="file" id="avatar" >
</div>
<input type="button" class="btn btn-default reg_btn" value="提交">
</form>
</div>
</div>
</div>
<script src="/static/blog/js/jquery-3.3.1.min.js"></script> <!--编写js代码-->
<script>
$("#avatar").change(function () {
//1、获取用户选中的文件对象;
var file_obj = $(this)[0].files[0];
//2、获取文件对象的路径;
var reader = new FileReader();
reader.readAsDataURL(file_obj);
//3、修改img的src属性值,src= 文件对象的路径;
reader.onload = function(){
$("#avatar_img").attr("src",reader.result)
}; });
//基于Ajax提交事件;
$(".reg_btn").click(function () { var formdata = new FormData();
console.log($("#form").serializeArray());
var request_data = $("#form").serializeArray();
$.each(request_data,function (index,data) {
formdata.append(data.name,data.value)
});
formdata.append("avatar",$("#avatar")[0].files[0]); /* var formdata = new FormData();
formdata.append("user",$("#id_user").val());
formdata.append("pwd",$("#id_pwd").val());
formdata.append("r_pwd",$("#id_r_pwd").val());
formdata.append("email",$("#id_email").val());
formdata.append("avatar",$("#avatar")[0].files[0]);
formdata.append("csrfmiddlewaretoken",$("[name = 'csrfmiddlewaretoken']").val());*/ $.ajax({
url:"",
type:"post",
contentType:false,
processData:false,
data:formdata,
success:function (data) {
console.log(data) }
})
})
</script> </body>
</html>
22-博客系统之基于Ajax在注册页面显示错误消息1
1、基于Ajax实现页面错误消息;
register.html;
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>注册页面</title>
<link rel="stylesheet" href="/static/blog/bs/css/bootstrap.css">
<style>
#avatar{
display: none;
}
#avatar_img{
margin-left: 20px;
}
.error{
color: red;
}
</style>
</head>
<body>
<h3>注册页面</h3>
<div class="container">
<div class="row">
<div class="col-md-6 col-lg-offset-3">
<form id="form">
{% csrf_token %}
<!--进行渲染form对象-->
{% for field in form %}
<div class="form-group">
<label for="{{ field.auto_id }}">{{ field.label }}</label>
{{ field }}<span class="error pull-right"></span>
</div>
{% endfor %}
<div class="form-group">
<label for="avatar">
头像
<img id="avatar_img" width="60px" height="60px" src="/static/blog/img/default.png" alt="">
</label>
<input type="file" id="avatar" name="avatar">
</div>
<input type="button" class="btn btn-default reg_btn" value="提交">
</form>
</div>
</div>
</div>
<script src="/static/blog/js/jquery-3.3.1.min.js"></script> <!--编写js代码-->
<script>
$("#avatar").change(function () {
//1、获取用户选中的文件对象;
var file_obj = $(this)[0].files[0];
//2、获取文件对象的路径;
var reader = new FileReader();
reader.readAsDataURL(file_obj);
//3、修改img的src属性值,src= 文件对象的路径;
reader.onload = function(){
$("#avatar_img").attr("src",reader.result)
}; });
//基于Ajax提交事件;
$(".reg_btn").click(function () { var formdata = new FormData();
console.log($("#form").serializeArray());
var request_data = $("#form").serializeArray();
$.each(request_data,function (index,data) {
formdata.append(data.name,data.value)
});
formdata.append("avatar",$("#avatar")[0].files[0]); /* var formdata = new FormData();
formdata.append("user",$("#id_user").val());
formdata.append("pwd",$("#id_pwd").val());
formdata.append("r_pwd",$("#id_r_pwd").val());
formdata.append("email",$("#id_email").val());
formdata.append("avatar",$("#avatar")[0].files[0]);
formdata.append("csrfmiddlewaretoken",$("[name = 'csrfmiddlewaretoken']").val());*/ $.ajax({
url:"",
type:"post",
contentType:false,
processData:false,
data:formdata,
success:function (data) {
console.log(data);
if(data.user){
//注册成功;
}else {
//console.log(data.msg)
$.each(data.msg,function (field,error_list) {
console.log(field,error_list);
$("#id_"+field).next().html(error_list[0])
})
}
}
});
});
</script> </body>
</html>
23-博客系统之基于Ajax在注册页面显示错误消息2
1、addClass以及removeClass以及Bootstrap中has-error的使用;
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>注册页面</title>
<link rel="stylesheet" href="/static/blog/bs/css/bootstrap.css">
<style>
#avatar{
display: none;
}
#avatar_img{
margin-left: 20px;
}
.error{
color: red;
}
</style>
</head>
<body>
<h3>注册页面</h3>
<div class="container">
<div class="row">
<div class="col-md-6 col-lg-offset-3">
<form id="form">
{% csrf_token %}
<!--进行渲染form对象-->
{% for field in form %}
<div class="form-group">
<label for="{{ field.auto_id }}">{{ field.label }}</label>
{{ field }}<span class="error pull-right"></span>
</div>
{% endfor %}
<div class="form-group">
<label for="avatar">
头像
<img id="avatar_img" width="60px" height="60px" src="/static/blog/img/default.png" alt="">
</label>
<input type="file" id="avatar" name="avatar">
</div>
<input type="button" class="btn btn-default reg_btn" value="提交">
</form>
</div>
</div>
</div>
<script src="/static/blog/js/jquery-3.3.1.min.js"></script> <!--编写js代码-->
<script>
$("#avatar").change(function () {
//1、获取用户选中的文件对象;
var file_obj = $(this)[0].files[0];
//2、获取文件对象的路径;
var reader = new FileReader();
reader.readAsDataURL(file_obj);
//3、修改img的src属性值,src= 文件对象的路径;
reader.onload = function(){
$("#avatar_img").attr("src",reader.result)
}; });
//基于Ajax提交事件;
$(".reg_btn").click(function () { var formdata = new FormData();
console.log($("#form").serializeArray());
var request_data = $("#form").serializeArray();
$.each(request_data,function (index,data) {
formdata.append(data.name,data.value)
});
formdata.append("avatar",$("#avatar")[0].files[0]); /* var formdata = new FormData();
formdata.append("user",$("#id_user").val());
formdata.append("pwd",$("#id_pwd").val());
formdata.append("r_pwd",$("#id_r_pwd").val());
formdata.append("email",$("#id_email").val());
formdata.append("avatar",$("#avatar")[0].files[0]);
formdata.append("csrfmiddlewaretoken",$("[name = 'csrfmiddlewaretoken']").val());*/ $.ajax({
url:"",
type:"post",
contentType:false,
processData:false,
data:formdata,
success:function (data) {
console.log(data);
if(data.user){
//注册成功;
}else {//注册失败
//console.log(data.msg)
//清空错误提示消息;
$("span.error").html();
$(".form-group").removeClass("has-error");
//展示此次提提交的信息;
$.each(data.msg,function (field,error_list) {
console.log(field,error_list);
$("#id_"+field).next().html(error_list[0]);
$("#id_"+field).parent().addClass("has-error");
})
}
}
});
});
</script> </body>
</html>
24-博客系统之form组件的局部钩子与全局钩子的应用
1、form组件中的钩子和全局钩子应用;
#!/usr/bin/env python3
# -*- coding:utf-8 -*-
# __Author__:TQTL911
# Version:python3.6.6
# Time:2018/8/24 14:41
from django import forms
from django.forms import widgets
from django.http import JsonResponse
from blog.models import UserInfo
from django.core.exceptions import NON_FIELD_ERRORS, ValidationError class UserForm(forms.Form):
user = forms.CharField(max_length=32,error_messages={"required":"该字段不能为空!"},label="用户名",widget=widgets.TextInput(attrs={"class":"form-control"}))
pwd = forms.CharField(max_length=32,label="密码",widget=widgets.PasswordInput(attrs={"class":"form-control"}))
r_pwd = forms.CharField(max_length=32,label="确认密码",widget=widgets.PasswordInput(attrs={"class":"form-control"}))
email = forms.EmailField(max_length=32,label="注册邮箱",widget=widgets.EmailInput(attrs={"class":"form-control"})) def clean_user(self):
user = self.cleaned_data.get("user")
user = UserInfo.objects.filter(username=user).first() if not user:
return user
else:
raise ValidationError("该用户已经注册")
def clean(self):
pwd = self.cleaned_data.get("pwd")
r_pwd = self.cleaned_data.get("r_pwd") if pwd == r_pwd:
return self.cleaned_data
else:
raise ValidationError("两次密码不一致!")
2、from django.core.exceptions import NON_FIELD_ERRORS, ValidationError的引入;
3、抛出中文异常的方法;
4、cleaned_data的用法;
register.html;
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>注册页面</title>
<link rel="stylesheet" href="/static/blog/bs/css/bootstrap.css">
<style>
#avatar{
display: none;
}
#avatar_img{
margin-left: 20px;
}
.error{
color: red;
}
</style>
</head>
<body>
<h3>注册页面</h3>
<div class="container">
<div class="row">
<div class="col-md-6 col-lg-offset-3">
<form id="form">
{% csrf_token %}
<!--进行渲染form对象-->
{% for field in form %}
<div class="form-group">
<label for="{{ field.auto_id }}">{{ field.label }}</label>
{{ field }}<span class="error pull-right"></span>
</div>
{% endfor %}
<div class="form-group">
<label for="avatar">
头像
<img id="avatar_img" width="60px" height="60px" src="/static/blog/img/default.png" alt="">
</label>
<input type="file" id="avatar" name="avatar">
</div>
<input type="button" class="btn btn-default reg_btn" value="提交">
</form>
</div>
</div>
</div>
<script src="/static/blog/js/jquery-3.3.1.min.js"></script> <!--编写js代码-->
<script>
$("#avatar").change(function () {
//1、获取用户选中的文件对象;
var file_obj = $(this)[0].files[0];
//2、获取文件对象的路径;
var reader = new FileReader();
reader.readAsDataURL(file_obj);
//3、修改img的src属性值,src= 文件对象的路径;
reader.onload = function(){
$("#avatar_img").attr("src",reader.result)
}; });
//基于Ajax提交事件;
$(".reg_btn").click(function () { var formdata = new FormData();
console.log($("#form").serializeArray());
var request_data = $("#form").serializeArray();
$.each(request_data,function (index,data) {
formdata.append(data.name,data.value)
});
formdata.append("avatar",$("#avatar")[0].files[0]); /* var formdata = new FormData();
formdata.append("user",$("#id_user").val());
formdata.append("pwd",$("#id_pwd").val());
formdata.append("r_pwd",$("#id_r_pwd").val());
formdata.append("email",$("#id_email").val());
formdata.append("avatar",$("#avatar")[0].files[0]);
formdata.append("csrfmiddlewaretoken",$("[name = 'csrfmiddlewaretoken']").val());*/ $.ajax({
url:"",
type:"post",
contentType:false,
processData:false,
data:formdata,
success:function (data) {
console.log(data);
if(data.user){
//注册成功;
}else {//注册失败
//console.log(data.msg)
//清空错误提示消息;
$("span.error").html();
$(".form-group").removeClass("has-error");
//展示此次提提交的信息;
$.each(data.msg,function (field,error_list) {
console.log(field,error_list);
if(field=="__all__"){
$("#id_r_pwd").next().html(error_list[0]).parent().addClass("has-error")
}
$("#id_"+field).next().html(error_list[0]);
$("#id_"+field).parent().addClass("has-error");
})
}
}
});
});
</script> </body>
</html>
25-博客系统之FileFiled字段
1、先导入from blog.models import UserInfo;
2、is_valid()校验通过后,向数据库库中写入数据,注意此时使用的是,UserInfo.objects.create_user()方法进行写入数据;
3、进行注册功能的验证;
4、进行登录验证;
26-博客系统之media配置1
1、settings.py中,STATICFILES_DIRS、MEDIA_ROOT配置;
2、上传文件,default的逻辑判断;
from django.shortcuts import render,HttpResponse,redirect # Create your views here. from django.http import JsonResponse
from django.contrib import auth def login(request):
"""
登录;
:param request:
:return:
"""
if request.method == "POST":
response = {"user":None,"msg":None}
user = request.POST.get("user")
pwd = request.POST.get("pwd")
valid_code = request.POST.get("valid_code") valid_code_str = request.session.get("valid_code_str")
if valid_code.upper() == valid_code_str.upper():
user = auth.authenticate(username = user,password = pwd)
if user:
response["user"] = user.username
else:
response["msg"] = "用户名或者密码错误!"
else:
response["msg"] = "验证码错误!"
return JsonResponse(response)
return render(request,"login.html") def get_valid_code_img(request):
"""
基于PIL模块动态生成响应状态码图片;
:param request:
:return:
"""
from blog.utils.validCode import get_valid_code_img
data = get_valid_code_img(request)
return HttpResponse(data) def index(request):
"""
首页;
:param request:
:return:
"""
return render(request,"index.html") from blog.Myforms import UserForm
from blog.models import UserInfo
def register(request):
"""
注册;
:param request:
:return:
"""
#if request.method == "POST":
if request.is_ajax():
print(request.POST)
form = UserForm(request.POST) response = {"user":None,"msg":None}
if form.is_valid():
response["user"] = form.cleaned_data.get("user")
#生成一条用户记录;
user = form.cleaned_data.get("user")
pwd = form.cleaned_data.get("pwd")
email = form.cleaned_data.get("email")
avatar_obj = request.FILES.get("avatar")
if avatar_obj:
user_obj = UserInfo.objects.create_user(username=user,password=pwd,email=email,avatar = avatar_obj )
else:
user_obj = UserInfo.objects.create_user(username=user, password=pwd, email=email) else:
print(form.cleaned_data)
print(form.errors)
response["msg"] = form.errors
return JsonResponse(response)
form = UserForm()
return render(request,"register.html",{"form":form})
27-博客系统之media配置2
1、settings.py中MEDIAZ_URL的配置;
2、urls.py中MEDIA_ROOT的引用而不是MEDIA_URL;
28-博客系统之生成用户对象的代码优化
1、减少冗余代码,提高可读性;
from django.shortcuts import render, HttpResponse, redirect # Create your views here. from django.http import JsonResponse
from django.contrib import auth
from blog.Myforms import UserForm
from blog.models import UserInfo def login(request):
"""
登录;
:param request:
:return:
"""
if request.method == "POST":
response = {"user": None, "msg": None}
user = request.POST.get("user")
pwd = request.POST.get("pwd")
valid_code = request.POST.get("valid_code") valid_code_str = request.session.get("valid_code_str")
if valid_code.upper() == valid_code_str.upper():
user = auth.authenticate(username=user, password=pwd)
if user:
response["user"] = user.username
else:
response["msg"] = "用户名或者密码错误!"
else:
response["msg"] = "验证码错误!"
return JsonResponse(response)
return render(request, "login.html") def get_valid_code_img(request):
"""
基于PIL模块动态生成响应状态码图片;
:param request:
:return:
"""
from blog.utils.validCode import get_valid_code_img
data = get_valid_code_img(request)
return HttpResponse(data) def index(request):
"""
首页;
:param request:
:return:
"""
return render(request, "index.html") def register(request):
"""
注册;
:param request:
:return:
"""
# if request.method == "POST":
if request.is_ajax():
# print(request.POST)
form = UserForm(request.POST) response = {"user": None, "msg": None}
if form.is_valid():
response["user"] = form.cleaned_data.get("user")
# 生成一条用户记录;
user = form.cleaned_data.get("user")
pwd = form.cleaned_data.get("pwd")
email = form.cleaned_data.get("email")
avatar_obj = request.FILES.get("avatar")
'''
if avatar_obj:
user_obj = UserInfo.objects.create_user(username=user,password=pwd,email=email,avatar = avatar_obj )
else:
user_obj = UserInfo.objects.create_user(username=user, password=pwd, email=email)
'''
extra = {}
if avatar_obj:
extra["avatar"] = avatar_obj
UserInfo.objects.create_user(username=user, password=pwd, email=email, ) else:
# print(form.cleaned_data)
# print(form.errors)
response["msg"] = form.errors
return JsonResponse(response)
form = UserForm()
return render(request, "register.html", {"form": form})
2、import使用方式,一般位于py文件的顶部且有顺序,先xx后xx;
Pycharm中,使用Ctrl+Shift+L组合键,进行Reformat Code操作,提升代码排版规范;
3、遵循PEP8代码编写规范,提高代码质量;
Python语言规范;https://zh-google-styleguide.readthedocs.io/en/latest/google-python-styleguide/python_language_rules/
29-博客系统之系统首页的导航区域
1、Bootstrap之导航条的使用;
2、{{ request.user.username }}的使用;
3、auth.logout(request) # 等同于request.session.flush();
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>博客系统首页</title>
<link rel="stylesheet" href="/static/blog/bs/css/bootstrap.css">
<script src="/static/blog/js/jquery-3.3.1.min.js"></script>
<script src="/static/blog/bs/js/bootstrap.min.js"></script>
<style>
#user_icon {
font-size: 18px;
margin-right: 10px;
vertical-align: -3px;
}
</style>
</head>
<body>
<nav class="navbar navbar-default">
<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="#">博客园</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><a href="#">博文</a></li>
</ul> <ul class="nav navbar-nav navbar-right">
{% if request.user.is_authenticated %}
<li><a href="#"><span id="user_icon" class="glyphicon glyphicon-user"></span>{{ request.user.username }}</a></li>
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true"
aria-expanded="false">Dropdown <span class="caret"></span></a>
<ul class="dropdown-menu">
<li><a href="#">修改密码</a></li>
<li><a href="#">修改头像</a></li>
<li><a href="/logout/">注销</a></li>
<li role="separator" class="divider"></li>
<li><a href="#">Separated link</a></li>
</ul>
</li> {% else %}
<li><a href="/login/">登录</a></li>
<li><a href="/register/">注册</a></li>
{% endif %} </ul>
</div><!-- /.navbar-collapse -->
</div><!-- /.container-fluid -->
</nav> </body>
</html>
views.py;
from django.shortcuts import render, HttpResponse, redirect # Create your views here. from django.http import JsonResponse
from django.contrib import auth
from blog.Myforms import UserForm
from blog.models import UserInfo def login(request):
"""
登录;
:param request:
:return:
"""
if request.method == "POST":
response = {"user": None, "msg": None}
user = request.POST.get("user")
pwd = request.POST.get("pwd")
valid_code = request.POST.get("valid_code") valid_code_str = request.session.get("valid_code_str")
if valid_code.upper() == valid_code_str.upper():
user = auth.authenticate(username=user, password=pwd)
if user:
auth.login(request, user)
response["user"] = user.username
else:
response["msg"] = "用户名或者密码错误!"
else:
response["msg"] = "验证码错误!"
return JsonResponse(response)
return render(request, "login.html") def index(request):
"""
首页;
:param request:
:return:
"""
return render(request, "index.html") def logout(request):
auth.logout(request) # 等同于request.session.flush()
return redirect("/login/") def get_valid_code_img(request):
"""
基于PIL模块动态生成响应状态码图片;
:param request:
:return:
"""
from blog.utils.validCode import get_valid_code_img
data = get_valid_code_img(request)
return HttpResponse(data) def register(request):
"""
注册;
:param request:
:return:
"""
# if request.method == "POST":
if request.is_ajax():
# print(request.POST)
form = UserForm(request.POST) response = {"user": None, "msg": None}
if form.is_valid():
response["user"] = form.cleaned_data.get("user")
# 生成一条用户记录;
user = form.cleaned_data.get("user")
pwd = form.cleaned_data.get("pwd")
email = form.cleaned_data.get("email")
avatar_obj = request.FILES.get("avatar")
'''
if avatar_obj:
user_obj = UserInfo.objects.create_user(username=user,password=pwd,email=email,avatar = avatar_obj )
else:
user_obj = UserInfo.objects.create_user(username=user, password=pwd, email=email)
'''
extra = {} if avatar_obj:
extra["avatar"] = avatar_obj
UserInfo.objects.create_user(username=user, password=pwd, email=email, **extra)
else:
# print(form.cleaned_data)
# print(form.errors)
response["msg"] = form.errors
return JsonResponse(response)
form = UserForm()
return render(request, "register.html", {"form": form})
urls.py;
"""cnblogs URL Configuration The `urlpatterns` list routes URLs to views. For more information please see:
https://docs.djangoproject.com/en/2.1/topics/http/urls/
Examples:
Function views
1. Add an import: from my_app import views
2. Add a URL to urlpatterns: path('', views.home, name='home')
Class-based views
1. Add an import: from other_app.views import Home
2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
Including another URLconf
1. Import the include() function: from django.urls import include, path
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
"""
from django.contrib import admin
from django.urls import path, re_path
from blog import views
from django.views.static import serve
from cnblogs import settings urlpatterns = [
path('admin/', admin.site.urls),
path('login/', views.login),
path('index/', views.index),
path('logout/', views.logout),
re_path('^$', views.index),
path('get_validCode_img/', views.get_valid_code_img),
path('register/', views.register),
# media配置;
re_path(r"media/(?P<path>.*)$", serve, {"document_root": settings.MEDIA_ROOT}),
]
30-博客系统之系统首页的主体布局
1、基于Bootstrap的格栅系统,定制页面布局;
<!--引入BootStrap格栅系统-->
<div class="container-fluid">
<div class="row">
<div class="col-md-3">
<div class="panel panel-warning">
<div class="panel-heading">Panel heading without title</div>
<div class="panel-body">
Panel content
</div>
</div>
<div class="panel panel-info">
<div class="panel-heading">Panel heading without title</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>
</div>
<div class="col-md-6">222</div>
<div class="col-md-3">
<div class="panel panel-default">
<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-default">
<div class="panel-heading">Panel heading without title</div>
<div class="panel-body">
Panel content
</div>
</div>
</div>
</div>
</div>
31-博客系统之admin的简单实用
1、Django下的admin初识;
Django内部的一个组件:后台数据管理组件(Web界面);
创建超级用户的方法:python manage.py createsuperuser 针对用户认证,在blog_userinfo表中生成记录,is_super字段值为1;
2、settings.py中的admin说明;
3、基于应用名称的blog中的admin.py进行表的注册;
from django.contrib import admin # Register your models here.
from blog import models 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.ArticleUpDown)
admin.site.register(models.Article2Tag)
admin.site.register(models.Comment)
32-博客系统之基于admin录入文章数据
1、通过Web前端页面进行文章数据的录入;
33-博客系统之系统首页的文章列表的渲染1
1、文章列表的渲染展示;
<div class="col-md-6">
<div class="article_list">
{% for articel in article_list %}
<div class="articel-item">
<h5><a href="">{{ articel.title }}</a></h5>
<div class="article-desc">
<span class="media-left">
<a href=""><img width="56" height="56" src="media/{{ articel.user.avatar }}" alt=""></a>
</span>
<span class="media-right">
{{ articel.desc }}
</span>
</div>
<hr>
</div>
{% endfor %}
</div>
</div>
34-博客系统之系统首页的文章列表的渲染2
1、首页文章列表渲染;
index.html;
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>博客系统首页</title>
<link rel="stylesheet" href="/static/blog/bs/css/bootstrap.css">
<script src="/static/blog/js/jquery-3.3.1.min.js"></script>
<script src="/static/blog/bs/js/bootstrap.min.js"></script>
<style>
#user_icon {
font-size: 18px;
margin-right: 10px;
vertical-align: -3px;
}
.pub_info{
margin-top: 10px;
}
.pub_info .glyphicon-comment{
vertical-align: -1px;
}
</style>
</head>
<body>
<nav class="navbar navbar-default">
<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="#">博客园</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><a href="#">博文</a></li>
</ul> <ul class="nav navbar-nav navbar-right">
{% if request.user.is_authenticated %}
<li><a href="#"><span id="user_icon"
class="glyphicon glyphicon-user"></span>{{ request.user.username }}</a></li>
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true"
aria-expanded="false">Dropdown <span class="caret"></span></a>
<ul class="dropdown-menu">
<li><a href="#">修改密码</a></li>
<li><a href="#">修改头像</a></li>
<li><a href="/logout/">注销</a></li>
<li role="separator" class="divider"></li>
<li><a href="#">Separated link</a></li>
</ul>
</li> {% else %}
<li><a href="/login/">登录</a></li>
<li><a href="/register/">注册</a></li>
{% endif %} </ul>
</div><!-- /.navbar-collapse -->
</div><!-- /.container-fluid -->
</nav> <!--引入BootStrap格栅系统-->
<div class="container-fluid">
<div class="row">
<div class="col-md-3">
<div class="panel panel-warning">
<div class="panel-heading">Panel heading without title</div>
<div class="panel-body">
Panel content
</div>
</div>
<div class="panel panel-info">
<div class="panel-heading">Panel heading without title</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>
</div>
<div class="col-md-6">
<div class="article_list">
{% for articel in article_list %}
<div class="articel-item">
<h5><a href="">{{ articel.title }}</a></h5>
<div class="article-desc">
<span class="media-left">
<a href=""><img width="56" height="56" src="media/{{ articel.user.avatar }}" alt=""></a>
</span>
<span class="media-right">
{{ articel.desc }}
</span>
</div>
<hr>
</div>
<div class="small pub_info">
<span><a href="">{{ articel.user.username }}</a></span>
<span>发布于 {{ articel.create_time|date:"Y-m-d H:i" }}</span>
<span class="glyphicon glyphicon-comment"></span>评论({{ articel.comment_count }})
<span class="glyphicon glyphicon-thumbs-up"></span>点赞({{ articel.up_count }})
</div>
{% endfor %}
</div>
</div>
<div class="col-md-3">
<div class="panel panel-default">
<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-default">
<div class="panel-heading">Panel heading without title</div>
<div class="panel-body">
Panel content
</div>
</div>
</div>
</div>
</div>
</body>
</html>
35-博客系统之个人站点的页面文章的查询
1、urls.py新增个人站点home_site访问路径;
"""cnblogs URL Configuration The `urlpatterns` list routes URLs to views. For more information please see:
https://docs.djangoproject.com/en/2.1/topics/http/urls/
Examples:
Function views
1. Add an import: from my_app import views
2. Add a URL to urlpatterns: path('', views.home, name='home')
Class-based views
1. Add an import: from other_app.views import Home
2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
Including another URLconf
1. Import the include() function: from django.urls import include, path
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
"""
from django.contrib import admin
from django.urls import path, re_path
from blog import views
from django.views.static import serve
from cnblogs import settings urlpatterns = [
path('admin/', admin.site.urls),
path('login/', views.login),
path('index/', views.index),
path('logout/', views.logout),
re_path('^$', views.index),
path('get_validCode_img/', views.get_valid_code_img),
path('register/', views.register),
# media配置;
re_path(r'media/(?P<path>.*)$', serve, {"document_root": settings.MEDIA_ROOT}),
# 个人站点URL;
re_path(r'^(?P<username>\w+)$', views.home_site),
]
2、views.py;
from django.shortcuts import render, HttpResponse, redirect # Create your views here. from django.http import JsonResponse
from django.contrib import auth
from blog.Myforms import UserForm
from blog.models import UserInfo
from blog import models def login(request):
"""
登录;
:param request:
:return:
"""
if request.method == "POST":
response = {"user": None, "msg": None}
user = request.POST.get("user")
pwd = request.POST.get("pwd")
valid_code = request.POST.get("valid_code") valid_code_str = request.session.get("valid_code_str")
if valid_code.upper() == valid_code_str.upper():
user = auth.authenticate(username=user, password=pwd)
if user:
auth.login(request, user)
response["user"] = user.username
else:
response["msg"] = "用户名或者密码错误!"
else:
response["msg"] = "验证码错误!"
return JsonResponse(response)
return render(request, "login.html") def index(request):
"""
首页;
:param request:
:return:
"""
article_list = models.Article.objects.all() return render(request, "index.html", {"article_list": article_list}) def logout(request):
auth.logout(request) # 等同于request.session.flush()
return redirect("/login/") def get_valid_code_img(request):
"""
基于PIL模块动态生成响应状态码图片;
:param request:
:return:
"""
from blog.utils.validCode import get_valid_code_img
data = get_valid_code_img(request)
return HttpResponse(data) def register(request):
"""
注册;
:param request:
:return:
"""
# if request.method == "POST":
if request.is_ajax():
# print(request.POST)
form = UserForm(request.POST) response = {"user": None, "msg": None}
if form.is_valid():
response["user"] = form.cleaned_data.get("user")
# 生成一条用户记录;
user = form.cleaned_data.get("user")
pwd = form.cleaned_data.get("pwd")
email = form.cleaned_data.get("email")
avatar_obj = request.FILES.get("avatar")
'''
if avatar_obj:
user_obj = UserInfo.objects.create_user(username=user,password=pwd,email=email,avatar = avatar_obj )
else:
user_obj = UserInfo.objects.create_user(username=user, password=pwd, email=email)
'''
extra = {} if avatar_obj:
extra["avatar"] = avatar_obj
UserInfo.objects.create_user(username=user, password=pwd, email=email, **extra)
else:
# print(form.cleaned_data)
# print(form.errors)
response["msg"] = form.errors
return JsonResponse(response)
form = UserForm()
return render(request, "register.html", {"form": form}) def home_site(request, username):
"""
个人站点视图函数;
:param request:
:return:
"""
print("username", username)
user = UserInfo.objects.filter(username=username).first()
# 判断用户是否存在;
if not user:
return render(request, "not_found.html")
# 查询当前站点对象:
blog = user.blog
# 当前用户或者当前站点对应的所有文章;
# 基于对象查询;
# article_list = user.article_set.all()
# 基于双下划线的查询;
article_list = models.Article.objects.filter(user=user)
return render(request, "home_site.html")
36-博客系统之个人站点页面的标签与分类查询
1、标签以及分类的查询方法;
def home_site(request, username):
"""
个人站点视图函数;
:param request:
:return:
"""
print("username", username)
user = UserInfo.objects.filter(username=username).first()
# 判断用户是否存在;
if not user:
return render(request, "not_found.html")
# 查询当前站点对象:
blog = user.blog
# 当前用户或者当前站点对应的所有文章;
# 基于对象查询;
# article_list = user.article_set.all()
# 基于双下划线的查询;
article_list = models.Article.objects.filter(user=user) # 每一个后表的模型.objedts.values("pk").annotate(聚合函数(关联表__统计字段)).values("表模型的所有字段以及统计字段") # 1、查询每一个分类名称以及对应的文章数;
ret = models.Category.objects.values("pk").annotate(c=Count("article__title")).values("title", "c")
print(ret)
# 2、查询当前站点的每一个分类名称以及对应的文章数;
cate_list = models.Category.objects.filter(blog=blog).values("pk").annotate(c=Count("article__title")).values_list(
"title", "c")
print(cate_list) # 3、查询当前站点的每一个标签名称对应的文章数;
tag_list = models.Tag.objects.filter(blog=blog).values("pk").annotate(c=Count("article")).values_list("title", "c")
print(tag_list) return render(request, "home_site.html")
37-博客系统之个人站点页面的日期查询1
1、SQL查询语句,针对于显示的格式化方法:data_format;
38-博客系统之个人站点页面的日期查询2
1、日期查询之extra()函数;
from django.shortcuts import render, HttpResponse, redirect # Create your views here. from django.http import JsonResponse
from django.contrib import auth
from blog.Myforms import UserForm
from blog.models import UserInfo
from blog import models
from django.db.models import Count def login(request):
"""
登录;
:param request:
:return:
"""
if request.method == "POST":
response = {"user": None, "msg": None}
user = request.POST.get("user")
pwd = request.POST.get("pwd")
valid_code = request.POST.get("valid_code") valid_code_str = request.session.get("valid_code_str")
if valid_code.upper() == valid_code_str.upper():
user = auth.authenticate(username=user, password=pwd)
if user:
auth.login(request, user)
response["user"] = user.username
else:
response["msg"] = "用户名或者密码错误!"
else:
response["msg"] = "验证码错误!"
return JsonResponse(response)
return render(request, "login.html") def index(request):
"""
首页;
:param request:
:return:
"""
article_list = models.Article.objects.all() return render(request, "index.html", {"article_list": article_list}) def logout(request):
auth.logout(request) # 等同于request.session.flush()
return redirect("/login/") def get_valid_code_img(request):
"""
基于PIL模块动态生成响应状态码图片;
:param request:
:return:
"""
from blog.utils.validCode import get_valid_code_img
data = get_valid_code_img(request)
return HttpResponse(data) def register(request):
"""
注册;
:param request:
:return:
"""
# if request.method == "POST":
if request.is_ajax():
# print(request.POST)
form = UserForm(request.POST) response = {"user": None, "msg": None}
if form.is_valid():
response["user"] = form.cleaned_data.get("user")
# 生成一条用户记录;
user = form.cleaned_data.get("user")
pwd = form.cleaned_data.get("pwd")
email = form.cleaned_data.get("email")
avatar_obj = request.FILES.get("avatar")
'''
if avatar_obj:
user_obj = UserInfo.objects.create_user(username=user,password=pwd,email=email,avatar = avatar_obj )
else:
user_obj = UserInfo.objects.create_user(username=user, password=pwd, email=email)
'''
extra = {} if avatar_obj:
extra["avatar"] = avatar_obj
UserInfo.objects.create_user(username=user, password=pwd, email=email, **extra)
else:
# print(form.cleaned_data)
# print(form.errors)
response["msg"] = form.errors
return JsonResponse(response)
form = UserForm()
return render(request, "register.html", {"form": form}) def home_site(request, username):
"""
个人站点视图函数;
:param request:
:return:
"""
print("username", username)
user = UserInfo.objects.filter(username=username).first()
# 判断用户是否存在;
if not user:
return render(request, "not_found.html")
# 查询当前站点对象:
blog = user.blog
# 当前用户或者当前站点对应的所有文章;
# 基于对象查询;
# article_list = user.article_set.all()
# 基于双下划线的查询;
article_list = models.Article.objects.filter(user=user) # 每一个后表的模型.objedts.values("pk").annotate(聚合函数(关联表__统计字段)).values("表模型的所有字段以及统计字段") # 1、查询每一个分类名称以及对应的文章数;
ret1 = models.Category.objects.values("pk").annotate(c=Count("article__title")).values("title", "c")
print(ret1)
# 2、查询当前站点的每一个分类名称以及对应的文章数;
cate_list = models.Category.objects.filter(blog=blog).values("pk").annotate(c=Count("article__title")).values_list(
"title", "c")
print(cate_list) # 3、查询当前站点的每一个标签名称对应的文章数;
tag_list = models.Tag.objects.filter(blog=blog).values("pk").annotate(c=Count("article")).values_list("title", "c")
print(tag_list)
#4、查询当前站点每一个年月的名称以及对应的文章数之extra函数;
ret2 = models.Article.objects.extra(select={"is_recent":"create_time > '2018-08-26'"}).values("title","is_recent")
print(ret2)
ret3 = models.Article.objects.extra(select={"y_m_d_date":"date_format(create_time,'%%Y-%%m-%%d')"}).values("title","y_m_d_date")
print("这里是ret3",ret3)
return render(request, "home_site.html")
39-博客系统之个人站点页面的日期查询3
1、以年和月的格式显示;
date_list = models.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")
print("这里是date_list",date_list)
40-博客系统之个人站点页面的日期查询4
1、使用Django内置的TruncMonth代理extra()方法以及date_format;
from django.shortcuts import render, HttpResponse, redirect # Create your views here. from django.http import JsonResponse
from django.contrib import auth
from blog.Myforms import UserForm
from blog.models import UserInfo
from blog import models
from django.db.models import Count def login(request):
"""
登录;
:param request:
:return:
"""
if request.method == "POST":
response = {"user": None, "msg": None}
user = request.POST.get("user")
pwd = request.POST.get("pwd")
valid_code = request.POST.get("valid_code") valid_code_str = request.session.get("valid_code_str")
if valid_code.upper() == valid_code_str.upper():
user = auth.authenticate(username=user, password=pwd)
if user:
auth.login(request, user)
response["user"] = user.username
else:
response["msg"] = "用户名或者密码错误!"
else:
response["msg"] = "验证码错误!"
return JsonResponse(response)
return render(request, "login.html") def index(request):
"""
首页;
:param request:
:return:
"""
article_list = models.Article.objects.all() return render(request, "index.html", {"article_list": article_list}) def logout(request):
auth.logout(request) # 等同于request.session.flush()
return redirect("/login/") def get_valid_code_img(request):
"""
基于PIL模块动态生成响应状态码图片;
:param request:
:return:
"""
from blog.utils.validCode import get_valid_code_img
data = get_valid_code_img(request)
return HttpResponse(data) def register(request):
"""
注册;
:param request:
:return:
"""
# if request.method == "POST":
if request.is_ajax():
# print(request.POST)
form = UserForm(request.POST) response = {"user": None, "msg": None}
if form.is_valid():
response["user"] = form.cleaned_data.get("user")
# 生成一条用户记录;
user = form.cleaned_data.get("user")
pwd = form.cleaned_data.get("pwd")
email = form.cleaned_data.get("email")
avatar_obj = request.FILES.get("avatar")
'''
if avatar_obj:
user_obj = UserInfo.objects.create_user(username=user,password=pwd,email=email,avatar = avatar_obj )
else:
user_obj = UserInfo.objects.create_user(username=user, password=pwd, email=email)
'''
extra = {} if avatar_obj:
extra["avatar"] = avatar_obj
UserInfo.objects.create_user(username=user, password=pwd, email=email, **extra)
else:
# print(form.cleaned_data)
# print(form.errors)
response["msg"] = form.errors
return JsonResponse(response)
form = UserForm()
return render(request, "register.html", {"form": form}) def home_site(request, username):
"""
个人站点视图函数;
:param request:
:return:
"""
print("username", username)
user = UserInfo.objects.filter(username=username).first()
# 判断用户是否存在;
if not user:
return render(request, "not_found.html")
# 查询当前站点对象:
blog = user.blog
# 当前用户或者当前站点对应的所有文章;
# 基于对象查询;
# article_list = user.article_set.all()
# 基于双下划线的查询;
article_list = models.Article.objects.filter(user=user) # 每一个后表的模型.objedts.values("pk").annotate(聚合函数(关联表__统计字段)).values("表模型的所有字段以及统计字段") # 1、查询每一个分类名称以及对应的文章数;
ret1 = models.Category.objects.values("pk").annotate(c=Count("article__title")).values("title", "c")
print(ret1)
# 2、查询当前站点的每一个分类名称以及对应的文章数;
cate_list = models.Category.objects.filter(blog=blog).values("pk").annotate(c=Count("article__title")).values_list(
"title", "c")
print(cate_list) # 3、查询当前站点的每一个标签名称对应的文章数;
"""
方式1:
tag_list = models.Tag.objects.filter(blog=blog).values("pk").annotate(c=Count("article")).values_list("title", "c")
print(tag_list)
#4、查询当前站点每一个年月的名称以及对应的文章数之extra函数;
ret2 = models.Article.objects.extra(select={"is_recent":"create_time > '2018-08-26'"}).values("title","is_recent")
print(ret2)
date_list = models.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")
print("这里是date_list",date_list)
""" """
方式2:
"""
from django.db.models.functions import TruncMonth
ret4 = models.Article.objects.filter(user=user).annotate(month = TruncMonth("Create_time")).values("month").annotate(c= Count("nid")).values_list("month","c")
print("ret----->",ret4) return render(request, "home_site.html")
41-博客系统之个人站点页面的渲染布局1
1、顶部导航栏设置,右浮动设置;
2、基于博客的title向页面渲染数据;
home_site.html;
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>home_site</title>
<style type="text/css">
* {
margin: 0;
padding: 0;
} .header {
width: 100%;
height: 60px;
background-color: #369;
} .header .title {
font-size: 18px;
font-weight: 100;
line-height: 60px;
color: white;
margin-left: 15px;
margin-top: -10px; }
.backend{
float: right;
color: white;
text-decoration: none;
font-size: 14px;
margin-right: 10px;
margin-top: 10px;
}
</style>
</head>
<body>
<div class="header">
<div class="content">
<p class="title">
<span>{{ blog.title }}</span>
<a href="" class="backend">管理</a>
</p>
</div>
</div>
</body>
</html>
42-博客系统之个人站点页面的渲染布局2
1、站点页面的格栅系统布局渲染;
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>home_site</title>
<link rel="stylesheet" href="/static/blog/bs/css/bootstrap.css">
<script src="/static/blog/js/jquery-3.3.1.min.js"></script>
<script src="/static/blog/bs/js/bootstrap.min.js"></script>
<style type="text/css">
* {
margin: 0;
padding: 0;
} .header {
width: 100%;
height: 60px;
background-color: #369;
} .header .title {
font-size: 18px;
font-weight: 100;
line-height: 60px;
color: white;
margin-left: 15px;
margin-top: -10px; } .backend {
float: right;
color: white;
text-decoration: none;
font-size: 14px;
margin-right: 10px;
margin-top: 10px;
} .pub_info {
margin-top: 10px;
color: darkgray;
} </style>
</head>
<body>
<div class="header">
<div class="content">
<p class="title">
<span>{{ blog.title }}</span>
<a href="" class="backend">管理</a>
</p>
</div>
</div>
<div class="container">
<div class="row">
<div class="col-md-3"> </div>
<div class="col-md-9">
<div class="article_list">
{% for article in article_list %}
<div class="article-item clearfix">
<h5><a href="">{{ article.title }}</a></h5>
<div class="article-desc">
{{ article.desc }}
</div>
</div>
<div class="small pub_info pull-right">
<span>发布于 {{ article.create_time|date:"Y-m-d H:i" }}</span>
<span class="glyphicon glyphicon-comment"></span>评论({{ article.comment_count }})
<span class="glyphicon glyphicon-thumbs-up"></span>点赞({{ article.up_count }})
</div>
<hr>
{% endfor %}
</div>
</div>
</div>
</div>
</body>
</html>
43-博客系统之个人站点页面的渲染布局3
1、在Bootstrap的栅格系统col-md-3左侧栏设置“我的标签”、“随笔分类”、“随笔归档”等分类tag;
<div class="col-md-3">
<div class="panel panel-warning">
<div class="panel-heading">我的标签</div>
<div class="panel-body">
{% for tag in tag_list %}
<p>{{ tag.0 }}({{ tag.1 }})</p>
{% endfor %} </div>
</div>
<div class="panel panel-danger">
<div class="panel-heading">随笔分类</div>
<div class="panel-body">
{% for cate in cate_list %}
<p>{{ cate.0 }}({{ cate.1 }})</p>
{% endfor %} </div>
</div>
<div class="panel panel-success">
<div class="panel-heading">随笔归档</div>
<div class="panel-body">
{% for date in date_list %}
<p>{{ date.0 }}({{ date.1 }})</p>
{% endfor %} </div>
</div> </div>
2、通过数据库进行渲染的到数据并循环进行前端展示;
views.py;
def home_site(request, username, **kwargs):
"""
个人站点视图函数
:param request:
:return:
""" print("kwargs", kwargs) # 区分访问是的站点页面还是站点下的跳转页面
print("username", username)
user = UserInfo.objects.filter(username=username).first()
# 判断用户是否存在!
if not user:
return render(request, "not_found.html") # 查询当前站点对象 blog = user.blog # 1 当前用户或者当前站点对应所有文章
# 基于对象查询
# article_list=user.article_set.all()
# 基于 __ article_list = models.Article.objects.filter(user=user) if kwargs:
condition = kwargs.get("condition")
param = kwargs.get("param") # 2012-12 if condition == "category":
article_list = article_list.filter(category__title=param)
elif condition == "tag":
article_list = article_list.filter(tags__title=param)
else:
year, month = param.split("/")
article_list = article_list.filter(create_time__year=year, create_time__month=month) # 每一个后的表模型.objects.values("pk").annotate(聚合函数(关联表__统计字段)).values("表模型的所有字段以及统计字段") # 查询每一个分类名称以及对应的文章数 # ret=models.Category.objects.values("pk").annotate(c=Count("article__title")).values("title","c")
# print(ret) # 查询当前站点的每一个分类名称以及对应的文章数 cate_list = models.Category.objects.filter(blog=blog).values("pk").annotate(c=Count("article__title")).values_list(
"title", "c")
print(cate_list) # 查询当前站点的每一个标签名称以及对应的文章数 tag_list = models.Tag.objects.filter(blog=blog).values("pk").annotate(c=Count("article")).values_list("title", "c")
print(tag_list) # 查询当前站点每一个年月的名称以及对应的文章数 # ret=models.Article.objects.extra(select={"is_recent":"create_time > '2018-09-05'"}).values("title","is_recent")
# print(ret) # 方式1:
date_list = models.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_list("y_m_date", "c")
print(date_list) # 方式2: # from django.db.models.functions import TruncMonth
#
# ret=models.Article.objects.filter(user=user).annotate(month=TruncMonth("create_time")).values("month").annotate(c=Count("nid")).values_list("month","c")
# print("ret----->",ret) return render(request, "home_site.html",
{"username": username, "blog": blog, "article_list": article_list, "tag_list": tag_list,
"cate_list": cate_list,"date_list":date_list})
3、展示效果如图所示;
44-博客系统之个人站点的跳转过滤功能的实现1
1、urls.py之路由设计,添加有名分组;
"""cnblogs URL Configuration The `urlpatterns` list routes URLs to views. For more information please see:
https://docs.djangoproject.com/en/2.1/topics/http/urls/
Examples:
Function views
1. Add an import: from my_app import views
2. Add a URL to urlpatterns: path('', views.home, name='home')
Class-based views
1. Add an import: from other_app.views import Home
2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
Including another URLconf
1. Import the include() function: from django.urls import include, path
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
"""
from django.contrib import admin
from django.urls import path, re_path
from blog import views
from django.views.static import serve
from cnblogs import settings urlpatterns = [
path('admin/', admin.site.urls),
path('login/', views.login),
path('index/', views.index),
path('logout/', views.logout),
re_path('^$', views.index),
path('get_validCode_img/', views.get_valid_code_img),
path('register/', views.register),
# media配置;
re_path(r'media/(?P<path>.*)$', serve, {"document_root": settings.MEDIA_ROOT}),
# 个人站点URL;
re_path(r'^(?P<username>\w+)/(?P<condition>tag|category|archive)/(?P<param>.*)/$', views.home_site),
#re_path(r'^(?P<username>\w+)/category/.*/$', views.home_site),
#re_path(r'^(?P<username>\w+)/archive/.*/$', views.home_site),
re_path(r'^(?P<username>\w+)$', views.home_site),
]
45-博客系统之个人站点页面的跳转过滤功能实现2
1、urls.py中,针对于相同视图函数匹配不同的路径,需要传入不同的参数的解决办法;
"""cnblogs URL Configuration The `urlpatterns` list routes URLs to views. For more information please see:
https://docs.djangoproject.com/en/2.1/topics/http/urls/
Examples:
Function views
1. Add an import: from my_app import views
2. Add a URL to urlpatterns: path('', views.home, name='home')
Class-based views
1. Add an import: from other_app.views import Home
2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
Including another URLconf
1. Import the include() function: from django.urls import include, path
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
"""
from django.contrib import admin
from django.urls import path, re_path
from blog import views
from django.views.static import serve
from cnblogs import settings urlpatterns = [
path('admin/', admin.site.urls),
path('login/', views.login),
path('index/', views.index),
path('logout/', views.logout),
re_path('^$', views.index),
path('get_validCode_img/', views.get_valid_code_img),
path('register/', views.register),
# media配置;
re_path(r'media/(?P<path>.*)$', serve, {"document_root": settings.MEDIA_ROOT}),
# 个人站点下的跳转URL;# 接受的参数,home_site(request,username,condition,param)
re_path(r'^(?P<username>\w+)/(?P<condition>tag|category|archive)/(?P<param>.*)/$', views.home_site), # re_path(r'^(?P<username>\w+)/category/.*/$', views.home_site),
# re_path(r'^(?P<username>\w+)/archive/.*/$', views.home_site),
# 个人站点URL;#接收的参数home_site(request,username="cxz")
re_path(r'^(?P<username>\w+)/$', views.home_site),
]
2、MySQL数据库针对于create_time有约束的限制,需调整settings.py中的USE_TZ = True改为False;
3、针对于年和月的匹配规则,使用split()方法进行分割处理;
def home_site(request, username, **kwargs):
"""
个人站点视图函数
:param request:
:return:
""" print("kwargs", kwargs) # 区分访问是的站点页面还是站点下的跳转页面;
print("username", username)
user = UserInfo.objects.filter(username=username).first()
# 判断用户是否存在!
if not user:
return render(request, "not_found.html") # 查询当前站点对象 blog = user.blog # 1 当前用户或者当前站点对应所有文章
# 基于对象查询
# article_list=user.article_set.all()
# 基于 __ article_list = models.Article.objects.filter(user=user) if kwargs:
condition = kwargs.get("condition")
param = kwargs.get("param") # 2012-12 if condition == "category":
article_list = article_list.filter(category__title=param)
elif condition == "tag":
article_list = article_list.filter(tags__title=param)
else:
year, month = param.split("/")
article_list = article_list.filter(create_time__year=year, create_time__month=month) # 每一个后的表模型.objects.values("pk").annotate(聚合函数(关联表__统计字段)).values("表模型的所有字段以及统计字段") # 查询每一个分类名称以及对应的文章数 # ret=models.Category.objects.values("pk").annotate(c=Count("article__title")).values("title","c")
# print(ret) # 查询当前站点的每一个分类名称以及对应的文章数 cate_list = models.Category.objects.filter(blog=blog).values("pk").annotate(c=Count("article__title")).values_list(
"title", "c")
print(cate_list) # 查询当前站点的每一个标签名称以及对应的文章数 tag_list = models.Tag.objects.filter(blog=blog).values("pk").annotate(c=Count("article")).values_list("title", "c")
print(tag_list) # 查询当前站点每一个年月的名称以及对应的文章数 # ret=models.Article.objects.extra(select={"is_recent":"create_time > '2018-09-05'"}).values("title","is_recent")
# print(ret) # 方式1:
date_list = models.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_list("y_m_date", "c")
print(date_list) # 方式2: # from django.db.models.functions import TruncMonth
#
# ret=models.Article.objects.filter(user=user).annotate(month=TruncMonth("create_time")).values("month").annotate(c=Count("nid")).values_list("month","c")
# print("ret----->",ret) return render(request, "home_site.html",
{"username": username, "blog": blog, "article_list": article_list, "tag_list": tag_list,
"cate_list": cate_list,"date_list":date_list})
46-博客系统之个人站点页面的跳转过滤功能的实现3
1、基于“我的标签”、“随笔分类”、“随笔归档”进行跳转;
home_site.html;
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>home_site</title>
<link rel="stylesheet" href="/static/blog/bs/css/bootstrap.css">
<script src="/static/blog/js/jquery-3.3.1.min.js"></script>
<script src="/static/blog/bs/js/bootstrap.min.js"></script>
<style type="text/css">
* {
margin: 0;
padding: 0;
} .header {
width: 100%;
height: 60px;
background-color: #369;
} .header .title {
font-size: 18px;
font-weight: 100;
line-height: 60px;
color: white;
margin-left: 15px;
margin-top: -10px; } .backend {
float: right;
color: white;
text-decoration: none;
font-size: 14px;
margin-right: 10px;
margin-top: 10px;
} .pub_info {
margin-top: 10px;
color: darkgray;
} </style>
</head>
<body>
<div class="header">
<div class="content">
<p class="title">
<span>{{ blog.title }}</span>
<a href="" class="backend">管理</a>
</p>
</div>
</div>
<div class="container">
<div class="row">
<div class="col-md-3">
<div class="panel panel-warning">
<div class="panel-heading">我的标签</div>
<div class="panel-body">
{% for tag in tag_list %}
<p><a href="/{{ username }}/tag/{{ tag.0 }}"> {{ tag.0 }}({{ tag.1 }})</a></p>
{% endfor %} </div>
</div>
<div class="panel panel-danger">
<div class="panel-heading">随笔分类</div>
<div class="panel-body">
{% for cate in cate_list %}
<p><a href="/{{ username }}/category/{{ cate.0 }}"> {{ cate.0 }}({{ tag.1 }})</a></p>
{% endfor %} </div>
</div>
<div class="panel panel-success">
<div class="panel-heading">随笔归档</div>
<div class="panel-body">
{% for date in date_list %}
<p><a href="/{{ username }}/archive/{{ date.0 }}"> {{ date.0 }}({{ tag.1 }})</a></p>
{% endfor %} </div>
</div> </div>
<div class="col-md-9">
<div class="article_list">
{% for article in article_list %}
<div class="article-item clearfix">
<h5><a href="">{{ article.title }}</a></h5>
<div class="article-desc">
{{ article.desc }}
</div>
</div>
<div class="small pub_info pull-right">
<span>发布于 {{ article.create_time|date:"Y-m-d H:i" }}</span>
<span class="glyphicon glyphicon-comment"></span>评论({{ article.comment_count }})
<span class="glyphicon glyphicon-thumbs-up"></span>点赞({{ article.up_count }})
</div>
<hr>
{% endfor %}
</div>
</div>
</div>
</div>
</body>
</html>
views.py;
from django.shortcuts import render, HttpResponse, redirect # Create your views here. from django.http import JsonResponse
from django.contrib import auth
from blog.Myforms import UserForm
from blog.models import UserInfo
from blog import models
from django.db.models import Count def login(request):
"""
登录;
:param request:
:return:
"""
if request.method == "POST":
response = {"user": None, "msg": None}
user = request.POST.get("user")
pwd = request.POST.get("pwd")
valid_code = request.POST.get("valid_code") valid_code_str = request.session.get("valid_code_str")
if valid_code.upper() == valid_code_str.upper():
user = auth.authenticate(username=user, password=pwd)
if user:
auth.login(request, user)
response["user"] = user.username
else:
response["msg"] = "用户名或者密码错误!"
else:
response["msg"] = "验证码错误!"
return JsonResponse(response)
return render(request, "login.html") def index(request):
"""
首页;
:param request:
:return:
"""
article_list = models.Article.objects.all() return render(request, "index.html", {"article_list": article_list}) def logout(request):
auth.logout(request) # 等同于request.session.flush()
return redirect("/login/") def get_valid_code_img(request):
"""
基于PIL模块动态生成响应状态码图片;
:param request:
:return:
"""
from blog.utils.validCode import get_valid_code_img
data = get_valid_code_img(request)
return HttpResponse(data) def register(request):
"""
注册;
:param request:
:return:
"""
# if request.method == "POST":
if request.is_ajax():
# print(request.POST)
form = UserForm(request.POST) response = {"user": None, "msg": None}
if form.is_valid():
response["user"] = form.cleaned_data.get("user")
# 生成一条用户记录;
user = form.cleaned_data.get("user")
pwd = form.cleaned_data.get("pwd")
email = form.cleaned_data.get("email")
avatar_obj = request.FILES.get("avatar")
'''
if avatar_obj:
user_obj = UserInfo.objects.create_user(username=user,password=pwd,email=email,avatar = avatar_obj )
else:
user_obj = UserInfo.objects.create_user(username=user, password=pwd, email=email)
'''
extra = {} if avatar_obj:
extra["avatar"] = avatar_obj
UserInfo.objects.create_user(username=user, password=pwd, email=email, **extra)
else:
# print(form.cleaned_data)
# print(form.errors)
response["msg"] = form.errors
return JsonResponse(response)
form = UserForm()
return render(request, "register.html", {"form": form}) def home_site(request, username, **kwargs):
"""
个人站点视图函数
:param request:
:return:
""" print("kwargs", kwargs) # 区分访问是的站点页面还是站点下的跳转页面;
print("username", username)
user = UserInfo.objects.filter(username=username).first()
# 判断用户是否存在!
if not user:
return render(request, "not_found.html") # 查询当前站点对象 blog = user.blog # 1 当前用户或者当前站点对应所有文章
# 基于对象查询
# article_list=user.article_set.all()
# 基于 __ article_list = models.Article.objects.filter(user=user) if kwargs:
condition = kwargs.get("condition")
param = kwargs.get("param") # 2012-12 if condition == "category":
article_list = article_list.filter(category__title=param)
elif condition == "tag":
article_list = article_list.filter(tags__title=param)
else:
year, month = param.split("-")
article_list = article_list.filter(create_time__year=year, create_time__month=month) # 每一个后的表模型.objects.values("pk").annotate(聚合函数(关联表__统计字段)).values("表模型的所有字段以及统计字段") # 查询每一个分类名称以及对应的文章数 # ret=models.Category.objects.values("pk").annotate(c=Count("article__title")).values("title","c")
# print(ret) # 查询当前站点的每一个分类名称以及对应的文章数 cate_list = models.Category.objects.filter(blog=blog).values("pk").annotate(c=Count("article__title")).values_list(
"title", "c")
print(cate_list) # 查询当前站点的每一个标签名称以及对应的文章数 tag_list = models.Tag.objects.filter(blog=blog).values("pk").annotate(c=Count("article")).values_list("title", "c")
print(tag_list) # 查询当前站点每一个年月的名称以及对应的文章数 # ret=models.Article.objects.extra(select={"is_recent":"create_time > '2018-09-05'"}).values("title","is_recent")
# print(ret) # 方式1:
date_list = models.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_list("y_m_date", "c")
print(date_list) # 方式2: # from django.db.models.functions import TruncMonth
#
# ret=models.Article.objects.filter(user=user).annotate(month=TruncMonth("create_time")).values("month").annotate(c=Count("nid")).values_list("month","c")
# print("ret----->",ret) return render(request, "home_site.html",
{"username": username, "blog": blog, "article_list": article_list, "tag_list": tag_list,
"cate_list": cate_list,"date_list":date_list}) # def get_classification_data(username):
# user = UserInfo.objects.filter(username=username).first()
# blog = user.blog
# cate_list = models.Category.objects.filter(blog=blog).values("pk").annotate(c=Count("article__title")).values_list(
# "title", "c")
# tag_list = models.Tag.objects.filter(blog=blog).values("pk").annotate(c=Count("article")).values_list("title", "c")
# date_list = models.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_list("y_m_date", "c")
# return {"blog": blog, "cate_list": cate_list, "date_list": date_list, "tag_list": tag_list}
47-博客系统之文章详情页的设计
1、路由urls.py配置;
re_path(r'^(?P<username>\w+)/articles/(?P<article_id>\d+)$', views.article_detail),
48-博客系统之文章详情页的构建
1、进行程序解耦合操作,分离base.html,基于{% extends 'base.html' %}进行调用;
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>home_site</title>
<link rel="stylesheet" href="/static/blog/bs/css/bootstrap.css">
<script src="/static/blog/js/jquery-3.3.1.min.js"></script>
<script src="/static/blog/bs/js/bootstrap.min.js"></script>
<style type="text/css">
* {
margin: 0;
padding: 0;
} .header {
width: 100%;
height: 60px;
background-color: #369;
} .header .title {
font-size: 18px;
font-weight: 100;
line-height: 60px;
color: white;
margin-left: 15px;
margin-top: -10px; } .backend {
float: right;
color: white;
text-decoration: none;
font-size: 14px;
margin-right: 10px;
margin-top: 10px;
} .pub_info {
margin-top: 10px;
color: darkgray;
} </style>
</head>
<body>
<div class="header">
<div class="content">
<p class="title">
<span>{{ blog.title }}</span>
<a href="" class="backend">管理</a>
</p>
</div>
</div>
<div class="container">
<div class="row">
<div class="col-md-3">
<div class="panel panel-warning">
<div class="panel-heading">我的标签</div>
<div class="panel-body">
{% for tag in tag_list %}
<p><a href="/{{ username }}/tag/{{ tag.0 }}"> {{ tag.0 }}({{ tag.1 }})</a></p>
{% endfor %} </div>
</div>
<div class="panel panel-danger">
<div class="panel-heading">随笔分类</div>
<div class="panel-body">
{% for cate in cate_list %}
<p><a href="/{{ username }}/category/{{ cate.0 }}"> {{ cate.0 }}({{ tag.1 }})</a></p>
{% endfor %} </div>
</div>
<div class="panel panel-success">
<div class="panel-heading">随笔归档</div>
<div class="panel-body">
{% for date in date_list %}
<p><a href="/{{ username }}/archive/{{ date.0 }}"> {{ date.0 }}({{ tag.1 }})</a></p>
{% endfor %} </div>
</div> </div>
<div class="col-md-9">
{% block content %} {% endblock %}
</div>
</div>
</div>
</body>
</html>
2、生成article_detail.html;
{% extends 'base.html' %}
3、重用的代码优化,编写独立函数进行调用;
from django.shortcuts import render, HttpResponse, redirect # Create your views here. from django.http import JsonResponse
from django.contrib import auth
from blog.Myforms import UserForm
from blog.models import UserInfo
from blog import models
from django.db.models import Count def login(request):
"""
登录;
:param request:
:return:
"""
if request.method == "POST":
response = {"user": None, "msg": None}
user = request.POST.get("user")
pwd = request.POST.get("pwd")
valid_code = request.POST.get("valid_code") valid_code_str = request.session.get("valid_code_str")
if valid_code.upper() == valid_code_str.upper():
user = auth.authenticate(username=user, password=pwd)
if user:
auth.login(request, user)
response["user"] = user.username
else:
response["msg"] = "用户名或者密码错误!"
else:
response["msg"] = "验证码错误!"
return JsonResponse(response)
return render(request, "login.html") def index(request):
"""
首页;
:param request:
:return:
"""
article_list = models.Article.objects.all() return render(request, "index.html", {"article_list": article_list}) def logout(request):
auth.logout(request) # 等同于request.session.flush()
return redirect("/login/") def get_valid_code_img(request):
"""
基于PIL模块动态生成响应状态码图片;
:param request:
:return:
"""
from blog.utils.validCode import get_valid_code_img
data = get_valid_code_img(request)
return HttpResponse(data) def register(request):
"""
注册;
:param request:
:return:
"""
# if request.method == "POST":
if request.is_ajax():
# print(request.POST)
form = UserForm(request.POST) response = {"user": None, "msg": None}
if form.is_valid():
response["user"] = form.cleaned_data.get("user")
# 生成一条用户记录;
user = form.cleaned_data.get("user")
pwd = form.cleaned_data.get("pwd")
email = form.cleaned_data.get("email")
avatar_obj = request.FILES.get("avatar")
'''
if avatar_obj:
user_obj = UserInfo.objects.create_user(username=user,password=pwd,email=email,avatar = avatar_obj )
else:
user_obj = UserInfo.objects.create_user(username=user, password=pwd, email=email)
'''
extra = {} if avatar_obj:
extra["avatar"] = avatar_obj
UserInfo.objects.create_user(username=user, password=pwd, email=email, **extra)
else:
# print(form.cleaned_data)
# print(form.errors)
response["msg"] = form.errors
return JsonResponse(response)
form = UserForm()
return render(request, "register.html", {"form": form}) def get_query_data(username):
user = UserInfo.objects.filter(username=username).first()
# 判断用户是否存在!
if not user:
return render(request, "not_found.html") # 查询当前站点对象 blog = user.blog
# 查询当前站点的每一个分类名称以及对应的文章数 cate_list = models.Category.objects.filter(blog=blog).values("pk").annotate(c=Count("article__title")).values_list(
"title", "c")
tag_list = models.Tag.objects.filter(blog=blog).values("pk").annotate(c=Count("article")).values_list("title", "c")
date_list = models.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_list("y_m_date", "c")
return {"blog": blog, "cate_list": cate_list, "date_list": date_list, "tag_list": tag_list} def article_detail(request, username, article_id):
context = get_query_data(username)
return render(request, "article_detail.html", context) def home_site(request, username, **kwargs):
"""
个人站点视图函数
:param request:
:return:
""" print("kwargs", kwargs) # 区分访问是的站点页面还是站点下的跳转页面;
print("username", username)
user = UserInfo.objects.filter(username=username).first()
# 判断用户是否存在!
if not user:
return render(request, "not_found.html") # 查询当前站点对象 blog = user.blog # 1 当前用户或者当前站点对应所有文章
# 基于对象查询
# article_list=user.article_set.all()
# 基于 __ article_list = models.Article.objects.filter(user=user) if kwargs:
condition = kwargs.get("condition")
param = kwargs.get("param") # 2012-12 if condition == "category":
article_list = article_list.filter(category__title=param)
elif condition == "tag":
article_list = article_list.filter(tags__title=param)
else:
year, month = param.split("-")
article_list = article_list.filter(create_time__year=year, create_time__month=month) # 每一个后的表模型.objects.values("pk").annotate(聚合函数(关联表__统计字段)).values("表模型的所有字段以及统计字段") # 查询每一个分类名称以及对应的文章数 # ret=models.Category.objects.values("pk").annotate(c=Count("article__title")).values("title","c")
# print(ret) # 查询当前站点的每一个分类名称以及对应的文章数 cate_list = models.Category.objects.filter(blog=blog).values("pk").annotate(c=Count("article__title")).values_list(
"title", "c")
print(cate_list) # 查询当前站点的每一个标签名称以及对应的文章数 tag_list = models.Tag.objects.filter(blog=blog).values("pk").annotate(c=Count("article")).values_list("title", "c")
print(tag_list) # 查询当前站点每一个年月的名称以及对应的文章数 # ret=models.Article.objects.extra(select={"is_recent":"create_time > '2018-09-05'"}).values("title","is_recent")
# print(ret) # 方式1:
date_list = models.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_list("y_m_date", "c")
print(date_list) # 方式2: # from django.db.models.functions import TruncMonth
#
# ret=models.Article.objects.filter(user=user).annotate(month=TruncMonth("create_time")).values("month").annotate(c=Count("nid")).values_list("month","c")
# print("ret----->",ret) return render(request, "home_site.html",
{"username": username, "blog": blog, "article_list": article_list, "tag_list": tag_list,
"cate_list": cate_list, "date_list": date_list})
49-博客系统之文章详情页的inclution_tag
1、配置base.html;
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>home_site</title>
<link rel="stylesheet" href="/static/blog/bs/css/bootstrap.css">
<script src="/static/blog/js/jquery-3.3.1.min.js"></script>
<script src="/static/blog/bs/js/bootstrap.min.js"></script>
<style type="text/css">
* {
margin: 0;
padding: 0;
} .header {
width: 100%;
height: 60px;
background-color: #369;
} .header .title {
font-size: 18px;
font-weight: 100;
line-height: 60px;
color: white;
margin-left: 15px;
margin-top: -10px; } .backend {
float: right;
color: white;
text-decoration: none;
font-size: 14px;
margin-right: 10px;
margin-top: 10px;
} .pub_info {
margin-top: 10px;
color: darkgray;
} </style>
</head>
<body>
<div class="header">
<div class="content">
<p class="title">
<span>{{ blog.title }}</span>
<a href="" class="backend">管理</a>
</p>
</div>
</div>
<div class="container">
<div class="row">
<div class="col-md-3 menu">
{% load my_tags %}
{% get_classification_style username %} </div>
<div class="col-md-9">
{% block content %} {% endblock %}
</div>
</div>
</div>
</body>
</html>
2、引入自定义标签templatetags;
3、使用register.inclusion_tag("classification.html")
# @register.simple_tag
@register.inclusion_tag("classification.html")
def get_classification_style(username):
user = models.UserInfo.objects.filter(username=username).first()
# 查询当前站点对象
blog = user.blog
# 查询当前站点的每一个分类名称以及对应的文章数
cate_list = models.Category.objects.filter(blog=blog).values("pk").annotate(c=Count("article__title")).values_list(
"title", "c")
tag_list = models.Tag.objects.filter(blog=blog).values("pk").annotate(c=Count("article")).values_list("title", "c")
date_list = models.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_list("y_m_date", "c")
return {"blog": blog, "cate_list": cate_list, "date_list": date_list, "tag_list": tag_list}
4、总体来讲,降低程序耦合度,提高可拓展性;
<div>
<div class="panel panel-warning">
<div class="panel-heading">我的标签</div>
<div class="panel-body">
{% for tag in tag_list %}
<p><a href="/{{ username }}/tag/{{ tag.0 }}"> {{ tag.0 }}({{ tag.1 }})</a></p>
{% endfor %} </div>
</div>
<div class="panel panel-danger">
<div class="panel-heading">随笔分类</div>
<div class="panel-body">
{% for cate in cate_list %}
<p><a href="/{{ username }}/category/{{ cate.0 }}"> {{ cate.0 }}({{ tag.1 }})</a></p>
{% endfor %} </div>
</div>
<div class="panel panel-success">
<div class="panel-heading">随笔归档</div>
<div class="panel-body">
{% for date in date_list %}
<p><a href="/{{ username }}/archive/{{ date.0 }}"> {{ date.0 }}({{ tag.1 }})</a></p>
{% endfor %} </div>
</div>
</div>
50-博客系统之文章详情页渲染标签的标签字符串转义1
1、Django内部将标签字符串做了转义处理;
2、article_detail.html;
{% extends 'base.html' %} {% block content %}
<h3 class="text-center">{{ article_obj.title }}</h3>
<div class="cont">
{{ article_obj.content }} </div>
{% endblock %}
51-博客系统之文章详情页渲染的标签字符串转义2
1、设置safe属性,防止xss攻击;
2、article_detail.html;
{% extends 'base.html' %} {% block content %}
<h3 class="text-center">{{ article_obj.title }}</h3>
<div class="cont">
{{ article_obj.content|safe }} </div>
{% endblock %}
52-博客系统之文章点赞样式的构建
1、点赞的样式仿照博客园进行构建,并且拆分独立css样式文件,使用link方法进行引入;
article_detail.html;
{% extends 'base.html' %} {% block content %}
<h3 class="text-center">{{ article_obj.title }}</h3>
<div class="cont">
{{ article_obj.content|safe }} </div>
<div id="div_digg">
<div class="diggit">
<span class="diggnum" id="digg_count">1</span>
</div>
<div class="buryit">
<span class="burynum" id="bury_count">0</span>
</div>
<div class="clear"></div>
<div class="diggword" id="digg_tips" style="color:red;"></div>
</div>
{% endblock %}
home_site.css;
* {
margin: 0;
padding: 0;
} .header {
width: 100%;
height: 60px;
background-color: #369;
} .header .title {
font-size: 18px;
font-weight: 100;
line-height: 60px;
color: white;
margin-left: 15px;
margin-top: -10px; } .backend {
float: right;
color: white;
text-decoration: none;
font-size: 14px;
margin-right: 10px;
margin-top: 10px;
} .pub_info {
margin-top: 10px;
color: darkgray;
}
article_detail.css;
.article_info .title {
margin-bottom: 20px;
} #div_digg {
float: right;
margin-bottom: 10px;
margin-right: 30px;
font-size: 12px;
width: 125px;
text-align: center;
margin-top: 10px;
} .diggit {
float: left;
width: 46px;
height: 52px;
background: url(/static/blog/img/upup.gif) no-repeat;
text-align: center;
cursor: pointer;
margin-top: 2px;
padding-top: 5px;
} .buryit {
float: right;
margin-left: 20px;
width: 46px;
height: 52px;
background: url(/static/blog/img/downdown.gif) no-repeat;
text-align: center;
cursor: pointer;
margin-top: 2px;
padding-top: 5px;
}
.clear{
clear: both;
}
53-博客系统之文章点赞事件的绑定
1、base.html引入本地静态jQuery文件;
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>home_site</title>
<link rel="stylesheet" href="/static/blog/bs/css/bootstrap.css">
<script src="/static/blog/js/jquery-3.3.1.min.js"></script>
<script src="/static/blog/bs/js/bootstrap.min.js"></script>
<link rel="stylesheet" href="/static/blog/css/home_site.css">
<link rel="stylesheet" href="/static/blog/css/article_detail.css">
</head>
2、article_detail.html添加js事件;
{% extends 'base.html' %} {% block content %}
<h3 class="text-center">{{ article_obj.title }}</h3>
<div class="cont">
{{ article_obj.content|safe }} </div>
<div id="div_digg">
<div class="diggit action">
<span class="diggnum" id="digg_count">1</span>
</div>
<div class="buryit action">
<span class="burynum" id="bury_count">0</span>
</div>
<div class="clear"></div>
<div class="diggword" id="digg_tips" style="color:red;"></div>
</div>
<script>
$("#div_digg .action").click(function () {
var is_up = $(this).hasClass("diggit")
alert(is_up)
})
</script>
{% endblock %}
54-博客系统之文章点赞的保存
1、绑定Ajax事件;
{% extends 'base.html' %} {% block content %}
{% csrf_token %}
<h3 class="text-center">{{ article_obj.title }}</h3>
<div class="cont">
{{ article_obj.content|safe }} </div>
<div id="div_digg">
<div class="diggit action">
<span class="diggnum" id="digg_count">{{ article_obj.up_count }}</span>
</div>
<div class="buryit action">
<span class="burynum" id="bury_count">{{ article_obj.down_count }}</span>
</div>
<div class="clear"></div>
<div class="diggword" id="digg_tips" style="color:red;"></div>
</div>
<script>
$("#div_digg .action").click(function () {
var is_up = $(this).hasClass("diggit");
alert(is_up) //传Ajax事件;
$.ajax({
url: "/digg/",
type: "post",
data: {
"csrfmiddlewaretoken": $("[name = 'csrfmiddlewaretoken']").val(),
"is_up": is_up,
"article_id": "{{ article_obj.pk }}", },
success: function (data) {
console.log(data)
}
})
});
</script>
{% endblock %}
2、基于digg视图函数进行数据的添加;
def digg(request):
"""
点赞视图函数;
:param request:
:return:
""" print(request.POST)
article_id = request.POST.get("article_id")
is_up = json.loads(request.POST.get("is_up"))
# 点赞人即当前登录人; user_id = request.user.pk
ard = models.ArticleUpDown.objects.create(user_id=user_id, article_id=article_id, is_up=is_up)
return HttpResponse("ok")
3、新增urls.py下的digg视图;
"""cnblogs URL Configuration The `urlpatterns` list routes URLs to views. For more information please see:
https://docs.djangoproject.com/en/2.1/topics/http/urls/
Examples:
Function views
1. Add an import: from my_app import views
2. Add a URL to urlpatterns: path('', views.home, name='home')
Class-based views
1. Add an import: from other_app.views import Home
2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
Including another URLconf
1. Import the include() function: from django.urls import include, path
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
"""
from django.contrib import admin
from django.urls import path, re_path
from blog import views
from django.views.static import serve
from cnblogs import settings urlpatterns = [
path('admin/', admin.site.urls),
path('login/', views.login),
path('index/', views.index),
path('logout/', views.logout),
re_path('^$', views.index),
path('get_validCode_img/', views.get_valid_code_img),
path('register/', views.register),
path('digg/', views.digg),
# media配置;
re_path(r'media/(?P<path>.*)$', serve, {"document_root": settings.MEDIA_ROOT}),
re_path(r'^(?P<username>\w+)/articles/(?P<article_id>\d+)$', views.article_detail),
# 个人站点下的跳转URL;# 接受的参数,home_site(request,username,condition,param)
re_path(r'^(?P<username>\w+)/(?P<condition>tag|category|archive)/(?P<param>.*)/$', views.home_site), # re_path(r'^(?P<username>\w+)/category/.*/$', views.home_site),
# re_path(r'^(?P<username>\w+)/archive/.*/$', views.home_site),
# 个人站点URL;#接收的参数home_site(request,username="cxz")
re_path(r'^(?P<username>\w+)/$', views.home_site),
]
55-博客系统之文章点赞数的数据同步
1、通过update方法像数据库中新增数据;
def digg(request):
"""
点赞视图函数;
:param request:
:return:
""" print(request.POST)
article_id = request.POST.get("article_id")
is_up = json.loads(request.POST.get("is_up"))
# 点赞人即当前登录人; user_id = request.user.pk
ard = models.ArticleUpDown.objects.create(user_id=user_id, article_id=article_id, is_up=is_up)
if is_up:
models.Article.objects.filter(pk=article_id).update(up_count=F("up_count") + 1)
return HttpResponse("ok")
56-博客系统之文章点赞的提示重复操作
1、评论与点赞只允许一次的逻辑以及提示消息;
def digg(request):
"""
点赞视图函数;
:param request:
:return:
""" print(request.POST)
article_id = request.POST.get("article_id")
is_up = json.loads(request.POST.get("is_up"))
# 点赞、点踩的人即当前登录人; user_id = request.user.pk
obj = models.ArticleUpDown.objects.filter(user_id=user_id, article_id=article_id).first()
response = {"state": True}
if not obj:
ard = models.ArticleUpDown.objects.create(user_id=user_id, article_id=article_id, is_up=is_up) queryset = models.Article.objects.filter(pk=article_id)
if is_up:
queryset.update(up_count=F("up_count") + 1)
else:
queryset.update(down_count=F("down_count") + 1)
else:
response["state"] = False
response["handled"] = obj.is_up return JsonResponse(response)
2、前端js代码的业务逻辑;
<script>
$("#div_digg .action").click(function () {
var is_up = $(this).hasClass("diggit");
// alert(is_up) //传Ajax事件;
$.ajax({
url: "/digg/",
type: "post",
data: {
"csrfmiddlewaretoken": $("[name = 'csrfmiddlewaretoken']").val(),
"is_up": is_up,
"article_id": "{{ article_obj.pk }}", },
success: function (data) {
console.log(data);
if (data.state){ }else {
if (data.handled){
$("#digg_tips").html("您已经推荐过!")
}else{
$("#digg_tips").html("您已经反对过!")
}
setTimeout(function () {
$("#digg_tips").html("")
},1000)
}
}
})
});
</script>
57-博客系统之文章点赞数的Ajax更新
1、点赞功能的局部刷新功能;
<script>
$("#div_digg .action").click(function () {
var is_up = $(this).hasClass("diggit");
// alert(is_up) //传Ajax事件;
$.ajax({
url: "/digg/",
type: "post",
data: {
"csrfmiddlewaretoken": $("[name = 'csrfmiddlewaretoken']").val(),
"is_up": is_up,
"article_id": "{{ article_obj.pk }}", },
success: function (data) {
console.log(data);
if (data.state) {
if (is_up) {
var val = parseInt($("#digg_count").text());
$("#digg_count").text(val + 1);
} else {
var val = parseInt($("#bury_count").text());
$("#bury_count").text(val + 1);
}
} else {
if (data.handled) {
$("#digg_tips").html("您已经推荐过!")
} else {
$("#digg_tips").html("您已经反对过!")
}
setTimeout(function () {
$("#digg_tips").html("")
}, 1000)
}
}
})
});
</script>
58-博客系统之文章点赞代码的优化
1、减少重复代码,提高程序精简性;
2、三元运算以及$obj = $(this).children("span")的使用;
{% extends 'base.html' %} {% block content %}
{% csrf_token %}
<h3 class="text-center">{{ article_obj.title }}</h3>
<div class="cont">
{{ article_obj.content|safe }} </div>
<div id="div_digg">
<div class="diggit action">
<span class="diggnum" id="digg_count">{{ article_obj.up_count }}</span>
</div>
<div class="buryit action">
<span class="burynum" id="bury_count">{{ article_obj.down_count }}</span>
</div>
<div class="clear"></div>
<div class="diggword" id="digg_tips" style="color:red;"></div>
</div>
<script>
$("#div_digg .action").click(function () {
var is_up = $(this).hasClass("diggit");
// alert(is_up) //传Ajax事件;
$obj = $(this).children("span");
$.ajax({
url: "/digg/",
type: "post",
data: {
"csrfmiddlewaretoken": $("[name = 'csrfmiddlewaretoken']").val(),
"is_up": is_up,
"article_id": "{{ article_obj.pk }}", },
success: function (data) {
console.log(data);
if (data.state) {
var val = parseInt($("#digg_count").text());
$obj.text(val + 1); } else {
var val = data.handled ? "您已经推荐过!" : "您已经反对过!";
$("#digg_tips").html(val);
setTimeout(function () {
$("#digg_tips").html("")
}, 1000)
}
}
})
});
</script>
{% endblock %}
59-博客系统之评论功能的实现流程
1、评论功能设计思路;
60-博客系统之评论样式
1、评论功能样式确定;
article_detail.html;
{% extends 'base.html' %} {% block content %}
{% csrf_token %}
<h3 class="text-center">{{ article_obj.title }}</h3>
<div class="cont">
{{ article_obj.content|safe }} </div>
<div class="clearfix">
<div id="div_digg">
<div class="diggit action">
<span class="diggnum" id="digg_count">{{ article_obj.up_count }}</span>
</div>
<div class="buryit action">
<span class="burynum" id="bury_count">{{ article_obj.down_count }}</span>
</div>
<div class="clear"></div>
<div class="diggword" id="digg_tips" style="color:red;"></div>
</div>
</div>
<div class="commonts">
<p>发表评论</p>
<p>
昵称:<input type="text" id="tbCommentAuthor" class="author" disabled="disabled" size="50"
value="{{ request.user.username }}">
</p>
<p>评论内容:</p>
<textarea name="" id="" cols="60" rows="10"></textarea>
<p>
<button class="btn btn-default common_btn">提交评论</button>
</p>
</div>
<script>
$("#div_digg .action").click(function () {
var is_up = $(this).hasClass("diggit");
// alert(is_up) //传Ajax事件;
$obj = $(this).children("span");
$.ajax({
url: "/digg/",
type: "post",
data: {
"csrfmiddlewaretoken": $("[name = 'csrfmiddlewaretoken']").val(),
"is_up": is_up,
"article_id": "{{ article_obj.pk }}", },
success: function (data) {
console.log(data);
if (data.state) {
var val = parseInt($("#digg_count").text());
$obj.text(val + 1); } else {
var val = data.handled ? "您已经推荐过!" : "您已经反对过!";
$("#digg_tips").html(val);
setTimeout(function () {
$("#digg_tips").html("")
}, 1000)
}
}
})
});
</script>
{% endblock %}
2、评论功能组件设置之——发表评论、昵称(动态引用)、评论头像、评论内容、提交评论;
61-博客系统之提交根评论
1、提交评论&保存评论;
2、清除评论框操作;
{% extends 'base.html' %} {% block content %}
{% csrf_token %}
<h3 class="text-center">{{ article_obj.title }}</h3>
<div class="cont">
{{ article_obj.content|safe }} </div>
<div class="clearfix">
<div id="div_digg">
<div class="diggit action">
<span class="diggnum" id="digg_count">{{ article_obj.up_count }}</span>
</div>
<div class="buryit action">
<span class="burynum" id="bury_count">{{ article_obj.down_count }}</span>
</div>
<div class="clear"></div>
<div class="diggword" id="digg_tips" style="color:red;"></div>
</div>
</div>
<div class="commonts">
<p>发表评论</p>
<p>
昵称:<input type="text" id="tbCommentAuthor" class="author" disabled="disabled" size="50"
value="{{ request.user.username }}">
</p>
<p>评论内容:</p>
<textarea name="" id="comment_content" cols="60" rows="10"></textarea>
<p>
<button class="btn btn-default common_btn">提交评论</button>
</p>
</div>
<script>
//点赞请求;
$("#div_digg .action").click(function () {
var is_up = $(this).hasClass("diggit");
// alert(is_up) //传Ajax事件;
$obj = $(this).children("span");
$.ajax({
url: "/digg/",
type: "post",
data: {
"csrfmiddlewaretoken": $("[name = 'csrfmiddlewaretoken']").val(),
"is_up": is_up,
"article_id": "{{ article_obj.pk }}", },
success: function (data) {
console.log(data);
if (data.state) {
var val = parseInt($("#digg_count").text());
$obj.text(val + 1); } else {
var val = data.handled ? "您已经推荐过!" : "您已经反对过!";
$("#digg_tips").html(val);
setTimeout(function () {
$("#digg_tips").html("")
}, 1000)
}
}
})
});
//评论请求;
$(".common_btn").click(function () {
var pid = "";
var content = $("#comment_content").val();
$.ajax({
url: "/comment/",
type: "post",
data: {
"csrfmiddlewaretoken": $("[name = 'csrfmiddlewaretoken']").val(),
"article_id": "{{ article_obj.pk }}",
"content": content,
pid:pid,
},
success: function (data) {
console.log(data);
//清空评论框;
$("#comment_content").val("")
}
})
})
</script>
{% endblock %}
comment视图函数;
def comment(request):
print(request.POST)
article_id = request.POST.get("article_id")
pid = request.POST.get("pid")
content = request.POST.get("content")
user_id = request.user.pk
comment_obj = models.Comment.objects.create(user_id=user_id, article_id=article_id, content=content,
parent_comment_id=pid)
return HttpResponse("comment")
62-博客系统之render显示根评论
评论列表的render显示;
{% extends 'base.html' %} {% block content %}
{% csrf_token %}
<h3 class="text-center">{{ article_obj.title }}</h3>
<div class="cont">
{{ article_obj.content|safe }} </div>
<div class="clearfix">
<div id="div_digg">
<div class="diggit action">
<span class="diggnum" id="digg_count">{{ article_obj.up_count }}</span>
</div>
<div class="buryit action">
<span class="burynum" id="bury_count">{{ article_obj.down_count }}</span>
</div>
<div class="clear"></div>
<div class="diggword" id="digg_tips" style="color:red;"></div>
</div>
</div>
<div class="commonts">
<p>评论列表</p>
<ul class="list-group comment_list">
{% for comment in comment_list %}
<li class="list-group-item">
<div>
<a href="">#{{ forloop.counter }}楼</a>
<span>{{ comment.create_time|date:"Y-m-d H:i" }}</span>
<a href=""><span>{{ comment.user.username }}</span></a>
<a href="" class="pull-right">回复</a>
</div>
<div class="comment_con">
<p>{{ comment.content }}</p>
</div>
</li> {% endfor %} </ul>
<p>发表评论</p>
<p>
昵称:<input type="text" id="tbCommentAuthor" class="author" disabled="disabled" size="50"
value="{{ request.user.username }}">
</p>
<p>评论内容:</p>
<textarea name="" id="comment_content" cols="60" rows="10"></textarea>
<p>
<button class="btn btn-default common_btn">提交评论</button>
</p>
</div>
<script>
//点赞请求;
$("#div_digg .action").click(function () {
var is_up = $(this).hasClass("diggit");
// alert(is_up) //传Ajax事件;
$obj = $(this).children("span");
$.ajax({
url: "/digg/",
type: "post",
data: {
"csrfmiddlewaretoken": $("[name = 'csrfmiddlewaretoken']").val(),
"is_up": is_up,
"article_id": "{{ article_obj.pk }}", },
success: function (data) {
console.log(data);
if (data.state) {
var val = parseInt($("#digg_count").text());
$obj.text(val + 1); } else {
var val = data.handled ? "您已经推荐过!" : "您已经反对过!";
$("#digg_tips").html(val);
setTimeout(function () {
$("#digg_tips").html("")
}, 1000)
}
}
})
});
//评论请求;
$(".common_btn").click(function () {
var pid = "";
var content = $("#comment_content").val();
$.ajax({
url: "/comment/",
type: "post",
data: {
"csrfmiddlewaretoken": $("[name = 'csrfmiddlewaretoken']").val(),
"article_id": "{{ article_obj.pk }}",
"content": content,
pid: pid,
},
success: function (data) {
console.log(data);
//清空评论框;
$("#comment_content").val("")
}
})
})
</script>
{% endblock %}
article_detail.css;
.article_info .title {
margin-bottom: 20px;
} #div_digg {
float: right;
margin-bottom: 10px;
margin-right: 30px;
font-size: 12px;
width: 125px;
text-align: center;
margin-top: 10px;
} .diggit {
float: left;
width: 46px;
height: 52px;
background: url(/static/blog/img/upup.gif) no-repeat;
text-align: center;
cursor: pointer;
margin-top: 2px;
padding-top: 5px;
} .buryit {
float: right;
margin-left: 20px;
width: 46px;
height: 52px;
background: url(/static/blog/img/downdown.gif) no-repeat;
text-align: center;
cursor: pointer;
margin-top: 2px;
padding-top: 5px;
} .clear {
clear: both;
} input.author {
background-image: url(/static/blog/img/icon_form.gif);
background-repeat: no-repeat;
border: 1px solid #ccc;
padding: 4px 4px 4px 30px;
width: 300px;
font-size: 13px;
background-position: 3px -3px;
}
.comment_con{
margin-top: 10px;
}
63-博客系统之Ajax显示根评论
1、基于Ajax技术展示的评论效果;
2、article_detail.html;
{% extends 'base.html' %} {% block content %}
{% csrf_token %}
<h3 class="text-center">{{ article_obj.title }}</h3>
<div class="cont">
{{ article_obj.content|safe }} </div>
<div class="clearfix">
<div id="div_digg">
<div class="diggit action">
<span class="diggnum" id="digg_count">{{ article_obj.up_count }}</span>
</div>
<div class="buryit action">
<span class="burynum" id="bury_count">{{ article_obj.down_count }}</span>
</div>
<div class="clear"></div>
<div class="diggword" id="digg_tips" style="color:red;"></div>
</div>
</div>
<div class="commonts">
<p>评论列表</p>
<ul class="list-group comment_list">
{% for comment in comment_list %}
<li class="list-group-item">
<div>
<a href="">#{{ forloop.counter }}楼</a>
<span>{{ comment.create_time|date:"Y-m-d H:i" }}</span>
<a href=""><span>{{ comment.user.username }}</span></a>
<a href="" class="pull-right">回复</a>
</div>
<div class="comment_con">
<p>{{ comment.content }}</p>
</div>
</li> {% endfor %} </ul>
<p>发表评论</p>
<p>
昵称:<input type="text" id="tbCommentAuthor" class="author" disabled="disabled" size="50"
value="{{ request.user.username }}">
</p>
<p>评论内容:</p>
<textarea name="" id="comment_content" cols="60" rows="10"></textarea>
<p>
<button class="btn btn-default common_btn">提交评论</button>
</p>
</div>
<script>
//点赞请求;
$("#div_digg .action").click(function () {
var is_up = $(this).hasClass("diggit");
// alert(is_up) //传Ajax事件;
$obj = $(this).children("span");
$.ajax({
url: "/digg/",
type: "post",
data: {
"csrfmiddlewaretoken": $("[name = 'csrfmiddlewaretoken']").val(),
"is_up": is_up,
"article_id": "{{ article_obj.pk }}", },
success: function (data) {
console.log(data);
if (data.state) {
var val = parseInt($("#digg_count").text());
$obj.text(val + 1); } else {
var val = data.handled ? "您已经推荐过!" : "您已经反对过!";
$("#digg_tips").html(val);
setTimeout(function () {
$("#digg_tips").html("")
}, 1000)
}
}
})
});
//评论请求;
$(".common_btn").click(function () {
var pid = "";
var content = $("#comment_content").val();
$.ajax({
url: "/comment/",
type: "post",
data: {
"csrfmiddlewaretoken": $("[name = 'csrfmiddlewaretoken']").val(),
"article_id": "{{ article_obj.pk }}",
"content": content,
pid: pid,
},
success: function (data) {
console.log(data);
var create_time = data.create_time;
var username = data.username;
var content = data.content;
var s = `
<li class="list-group-item">
<div>
<span>${create_time}</span>
<a href=""><span>${username}</span></a>
</div>
<div class="comment_con">
<p>${content}</p>
</div>
</li>`;
$("ul.comment_list").append(s); //清空评论框;
$("#comment_content").val("")
}
})
})
</script>
{% endblock %}
3、comment视图函数;
def comment(request):
print(request.POST)
article_id = request.POST.get("article_id")
pid = request.POST.get("pid")
content = request.POST.get("content")
user_id = request.user.pk
comment_obj = models.Comment.objects.create(user_id=user_id, article_id=article_id, content=content,
parent_comment_id=pid)
response = {}
response["create_time"] = comment_obj.create_time.strftime("%Y-%m-%d %X")
response["username"] = request.user.username
response["content"] = content
return JsonResponse(response)
64-博客系统之回复按钮事件
1、点击“回复”按钮,光标自动跳转至回复栏;
2、回复的对象@形式出现;
{% extends 'base.html' %} {% block content %}
{% csrf_token %}
<h3 class="text-center">{{ article_obj.title }}</h3>
<div class="cont">
{{ article_obj.content|safe }} </div>
<div class="clearfix">
<div id="div_digg">
<div class="diggit action">
<span class="diggnum" id="digg_count">{{ article_obj.up_count }}</span>
</div>
<div class="buryit action">
<span class="burynum" id="bury_count">{{ article_obj.down_count }}</span>
</div>
<div class="clear"></div>
<div class="diggword" id="digg_tips" style="color:red;"></div>
</div>
</div>
<div class="commonts">
<p>评论列表</p>
<ul class="list-group comment_list">
{% for comment in comment_list %}
<li class="list-group-item">
<div>
<a href="">#{{ forloop.counter }}楼</a>
<span>{{ comment.create_time|date:"Y-m-d H:i" }}</span>
<a href=""><span>{{ comment.user.username }}</span></a>
<a class="pull-right reply_btn" username = "{{ comment.user.username }}" >回复</a>
</div>
<div class="comment_con">
<p>{{ comment.content }}</p>
</div>
</li> {% endfor %} </ul>
<p>发表评论</p>
<p>
昵称:<input type="text" id="tbCommentAuthor" class="author" disabled="disabled" size="50"
value="{{ request.user.username }}">
</p>
<p>评论内容:</p>
<textarea name="" id="comment_content" cols="60" rows="10"></textarea>
<p>
<button class="btn btn-default common_btn">提交评论</button>
</p>
</div>
<script>
//点赞请求;
$("#div_digg .action").click(function () {
var is_up = $(this).hasClass("diggit");
// alert(is_up) //传Ajax事件;
$obj = $(this).children("span");
$.ajax({
url: "/digg/",
type: "post",
data: {
"csrfmiddlewaretoken": $("[name = 'csrfmiddlewaretoken']").val(),
"is_up": is_up,
"article_id": "{{ article_obj.pk }}", },
success: function (data) {
console.log(data);
if (data.state) {
var val = parseInt($("#digg_count").text());
$obj.text(val + 1); } else {
var val = data.handled ? "您已经推荐过!" : "您已经反对过!";
$("#digg_tips").html(val);
setTimeout(function () {
$("#digg_tips").html("")
}, 1000)
}
}
})
});
//评论请求;
$(".common_btn").click(function () {
var pid = "";
var content = $("#comment_content").val();
$.ajax({
url: "/comment/",
type: "post",
data: {
"csrfmiddlewaretoken": $("[name = 'csrfmiddlewaretoken']").val(),
"article_id": "{{ article_obj.pk }}",
"content": content,
pid: pid,
},
success: function (data) {
console.log(data);
var create_time = data.create_time;
var username = data.username;
var content = data.content;
var s = `
<li class="list-group-item">
<div>
<span>${create_time}</span>
<a href=""><span>${username}</span></a>
</div>
<div class="comment_con">
<p>${content}</p>
</div>
</li>`;
$("ul.comment_list").append(s); //清空评论框;
$("#comment_content").val("")
}
})
}); //回复按钮事件;
$(".reply_btn").click(function () {
$("#comment_content").focus();
var val = "@"+$(this).attr("username")+"\n";
$("#comment_content").val(val)
})
</script>
{% endblock %}
65-博客系统之提交子评论
1、字评论提交之pid号判断处理;
2、清空评论框的pid值;
{% extends 'base.html' %} {% block content %}
{% csrf_token %}
<h3 class="text-center">{{ article_obj.title }}</h3>
<div class="cont">
{{ article_obj.content|safe }} </div>
<div class="clearfix">
<div id="div_digg">
<div class="diggit action">
<span class="diggnum" id="digg_count">{{ article_obj.up_count }}</span>
</div>
<div class="buryit action">
<span class="burynum" id="bury_count">{{ article_obj.down_count }}</span>
</div>
<div class="clear"></div>
<div class="diggword" id="digg_tips" style="color:red;"></div>
</div>
</div>
<div class="commonts">
<p>评论列表</p>
<ul class="list-group comment_list">
{% for comment in comment_list %}
<li class="list-group-item">
<div>
<a href="">#{{ forloop.counter }}楼</a>
<span>{{ comment.create_time|date:"Y-m-d H:i" }}</span>
<a href=""><span>{{ comment.user.username }}</span></a>
<a class="pull-right reply_btn" username="{{ comment.user.username }} "
comment_pk="{{ comment.pk }}">回复</a>
</div>
<div class="comment_con">
<p>{{ comment.content }}</p>
</div>
</li> {% endfor %} </ul>
<p>发表评论</p>
<p>
昵称:<input type="text" id="tbCommentAuthor" class="author" disabled="disabled" size="50"
value="{{ request.user.username }}">
</p>
<p>评论内容:</p>
<textarea name="" id="comment_content" cols="60" rows="10"></textarea>
<p>
<button class="btn btn-default common_btn">提交评论</button>
</p>
</div>
<script>
//点赞请求;
$("#div_digg .action").click(function () {
var is_up = $(this).hasClass("diggit");
// alert(is_up) //传Ajax事件;
$obj = $(this).children("span");
$.ajax({
url: "/digg/",
type: "post",
data: {
"csrfmiddlewaretoken": $("[name = 'csrfmiddlewaretoken']").val(),
"is_up": is_up,
"article_id": "{{ article_obj.pk }}", },
success: function (data) {
console.log(data);
if (data.state) {
var val = parseInt($("#digg_count").text());
$obj.text(val + 1); } else {
var val = data.handled ? "您已经推荐过!" : "您已经反对过!";
$("#digg_tips").html(val);
setTimeout(function () {
$("#digg_tips").html("")
}, 1000)
}
}
})
});
//评论请求;
var pid = "";
$(".common_btn").click(function () { var content = $("#comment_content").val();
if (pid) {
var index = content.indexOf("\n");
content = content.slice(index + 1)
}
$.ajax({
url: "/comment/",
type: "post",
data: {
"csrfmiddlewaretoken": $("[name = 'csrfmiddlewaretoken']").val(),
"article_id": "{{ article_obj.pk }}",
"content": content,
pid: pid,
},
success: function (data) {
console.log(data);
var create_time = data.create_time;
var username = data.username;
var content = data.content;
var s = `
<li class="list-group-item">
<div>
<span>${create_time}</span>
<a href=""><span>${username}</span></a>
</div>
<div class="comment_con">
<p>${content}</p>
</div>
</li>`;
$("ul.comment_list").append(s); //清空评论框;
pid = "";
$("#comment_content").val("")
}
})
}); //回复按钮事件;
$(".reply_btn").click(function () {
$("#comment_content").focus();
var val = "@" + $(this).attr("username") + "\n";
$("#comment_content").val(val);
pid = $(this).attr("comment_pk");
})
</script>
{% endblock %}
66-博客系统之render显示
1、通过render方法进行子评论的显示;
2、Bootstrap的well属性的引用;
{% if comment.parent_comment_id %}
<div class="pid_info well">
<p>
{{ comment.parent_comment.user.username }}:{{ comment.parent_comment.content }}
</p>
</div> {% endif %}
67-博客系统之Ajax显示子评论的思路
1、基于Ajax实现局部刷新功能;
var pid = "";
$(".common_btn").click(function () { var content = $("#comment_content").val();
if (pid) {
var index = content.indexOf("\n");
content = content.slice(index + 1)
}
$.ajax({
url: "/comment/",
type: "post",
data: {
"csrfmiddlewaretoken": $("[name = 'csrfmiddlewaretoken']").val(),
"article_id": "{{ article_obj.pk }}",
"content": content,
pid: pid,
},
success: function (data) {
console.log(data);
var create_time = data.create_time;
var username = data.username;
var content = data.content;
var s = `
<li class="list-group-item">
<div>
<span>${create_time}</span>
<a href=""><span>${username}</span></a>
</div>
<div class="comment_con">
<p>${content}</p>
</div>
</li>`;
$("ul.comment_list").append(s); //清空评论框;
pid = "";
$("#comment_content").val("")
}
})
});
68-博客系统之评论树简介
1、递归展示数据的优缺点;
69-博客系统之评论树的请求数据
1、构建评论树的url以及视图函数;
path('get_comment_tree/', views.get_comment_tree),
def get_comment_tree(request):
article_id = request.GET.get("article_id")
ret = list(models.Comment.objects.filter(article_id=article_id).values("pk", "content", "parent_comment_id"))
return JsonResponse(ret, safe=False)
70-博客系统之展开评论树
1、点击评论树展示根评论;
{% extends 'base.html' %} {% block content %}
{% csrf_token %}
<h3 class="text-center">{{ article_obj.title }}</h3>
<div class="cont">
{{ article_obj.content|safe }} </div>
<div class="clearfix">
<div id="div_digg">
<div class="diggit action">
<span class="diggnum" id="digg_count">{{ article_obj.up_count }}</span>
</div>
<div class="buryit action">
<span class="burynum" id="bury_count">{{ article_obj.down_count }}</span>
</div>
<div class="clear"></div>
<div class="diggword" id="digg_tips" style="color:red;"></div>
</div>
</div>
<div class="commonts list-group">
<p class="tree_btn">评论树</p>
<div class="comment_tree"> </div>
<script>
$(".tree_btn").click(function () { $.ajax({
url: "/get_comment_tree",
type: "get",
data: {
article_id: "{{ article_obj.pk }}"
},
success: function (data) {
console.log(data)
$.each(data, function (index, comment_object) { var pk = comment_object.pk;
var content = comment_object.content;
var parent_comment_id = comment_object.parent_comment_id;
if (!parent_comment_id) {
var s = '<div comment_id=' + pk + '><span>' + content + '</span> </div>';
$(".comment_tree").append(s)
}
}) }
})
})
</script>
<p>评论列表</p>
<ul class="list-group comment_list">
{% for comment in comment_list %}
<li class="list-group-item">
<div>
<a href="">#{{ forloop.counter }}楼</a>
<span>{{ comment.create_time|date:"Y-m-d H:i" }}</span>
<a href=""><span>{{ comment.user.username }}</span></a>
<a class="pull-right reply_btn" username="{{ comment.user.username }} "
comment_pk="{{ comment.pk }}">回复</a>
</div>
{% if comment.parent_comment_id %}
<div class="pid_info well">
<p>
{{ comment.parent_comment.user.username }}:{{ comment.parent_comment.content }}
</p>
</div> {% endif %}
<div class="comment_con">
<p>{{ comment.content }}</p>
</div>
</li> {% endfor %} </ul>
<p>发表评论</p>
<p>
昵称:<input type="text" id="tbCommentAuthor" class="author" disabled="disabled" size="50"
value="{{ request.user.username }}">
</p>
<p>评论内容:</p>
<textarea name="" id="comment_content" cols="60" rows="10"></textarea>
<p>
<button class="btn btn-default common_btn">提交评论</button>
</p>
</div>
<script>
//点赞请求;
$("#div_digg .action").click(function () {
var is_up = $(this).hasClass("diggit");
// alert(is_up) //传Ajax事件;
$obj = $(this).children("span");
$.ajax({
url: "/digg/",
type: "post",
data: {
"csrfmiddlewaretoken": $("[name = 'csrfmiddlewaretoken']").val(),
"is_up": is_up,
"article_id": "{{ article_obj.pk }}", },
success: function (data) {
console.log(data);
if (data.state) {
var val = parseInt($("#digg_count").text());
$obj.text(val + 1); } else {
var val = data.handled ? "您已经推荐过!" : "您已经反对过!";
$("#digg_tips").html(val);
setTimeout(function () {
$("#digg_tips").html("")
}, 1000)
}
}
})
});
//评论请求;
var pid = "";
$(".common_btn").click(function () { var content = $("#comment_content").val();
if (pid) {
var index = content.indexOf("\n");
content = content.slice(index + 1)
}
$.ajax({
url: "/comment/",
type: "post",
data: {
"csrfmiddlewaretoken": $("[name = 'csrfmiddlewaretoken']").val(),
"article_id": "{{ article_obj.pk }}",
"content": content,
pid: pid,
},
success: function (data) {
console.log(data);
var create_time = data.create_time;
var username = data.username;
var content = data.content;
var s = `
<li class="list-group-item">
<div>
<span>${create_time}</span>
<a href=""><span>${username}</span></a>
</div>
<div class="comment_con">
<p>${content}</p>
</div>
</li>`;
$("ul.comment_list").append(s); //清空评论框;
pid = "";
$("#comment_content").val("")
}
})
}); //回复按钮事件;
$(".reply_btn").click(function () {
$("#comment_content").focus();
var val = "@" + $(this).attr("username") + "\n";
$("#comment_content").val(val);
pid = $(this).attr("comment_pk");
})
</script>
{% endblock %}
71-博客系统之展开评论树2
article_detail.html;
{% extends 'base.html' %} {% block content %}
{% csrf_token %}
<h3 class="text-center">{{ article_obj.title }}</h3>
<div class="cont">
{{ article_obj.content|safe }} </div>
<div class="clearfix">
<div id="div_digg">
<div class="diggit action">
<span class="diggnum" id="digg_count">{{ article_obj.up_count }}</span>
</div>
<div class="buryit action">
<span class="burynum" id="bury_count">{{ article_obj.down_count }}</span>
</div>
<div class="clear"></div>
<div class="diggword" id="digg_tips" style="color:red;"></div>
</div>
</div>
<div class="commonts list-group">
<p class="tree_btn">评论树</p>
<div class="comment_tree"> </div>
<script>
$(".tree_btn").click(function () { $.ajax({
url: "/get_comment_tree",
type: "get",
data: {
article_id: "{{ article_obj.pk }}"
},
success: function (data) {
console.log(data)
$.each(data, function (index, comment_object) { var pk = comment_object.pk;
var content = comment_object.content;
var parent_comment_id = comment_object.parent_comment_id;
var s = '<div class = "comment_item" comment_id=' + pk + '><span>' + content + '</span> </div>';
if (!parent_comment_id) { $(".comment_tree").append(s)
} else {
$("[comment_id=" + parent_comment_id + "]").append(s);
}
}) }
})
})
</script>
<p>评论列表</p>
<ul class="list-group comment_list">
{% for comment in comment_list %}
<li class="list-group-item">
<div>
<a href="">#{{ forloop.counter }}楼</a>
<span>{{ comment.create_time|date:"Y-m-d H:i" }}</span>
<a href=""><span>{{ comment.user.username }}</span></a>
<a class="pull-right reply_btn" username="{{ comment.user.username }} "
comment_pk="{{ comment.pk }}">回复</a>
</div>
{% if comment.parent_comment_id %}
<div class="pid_info well">
<p>
{{ comment.parent_comment.user.username }}:{{ comment.parent_comment.content }}
</p>
</div> {% endif %}
<div class="comment_con">
<p>{{ comment.content }}</p>
</div>
</li> {% endfor %} </ul>
<p>发表评论</p>
<p>
昵称:<input type="text" id="tbCommentAuthor" class="author" disabled="disabled" size="50"
value="{{ request.user.username }}">
</p>
<p>评论内容:</p>
<textarea name="" id="comment_content" cols="60" rows="10"></textarea>
<p>
<button class="btn btn-default common_btn">提交评论</button>
</p>
</div>
<script>
//点赞请求;
$("#div_digg .action").click(function () {
var is_up = $(this).hasClass("diggit");
// alert(is_up) //传Ajax事件;
$obj = $(this).children("span");
$.ajax({
url: "/digg/",
type: "post",
data: {
"csrfmiddlewaretoken": $("[name = 'csrfmiddlewaretoken']").val(),
"is_up": is_up,
"article_id": "{{ article_obj.pk }}", },
success: function (data) {
console.log(data);
if (data.state) {
var val = parseInt($("#digg_count").text());
$obj.text(val + 1); } else {
var val = data.handled ? "您已经推荐过!" : "您已经反对过!";
$("#digg_tips").html(val);
setTimeout(function () {
$("#digg_tips").html("")
}, 1000)
}
}
})
});
//评论请求;
var pid = "";
$(".common_btn").click(function () { var content = $("#comment_content").val();
if (pid) {
var index = content.indexOf("\n");
content = content.slice(index + 1)
}
$.ajax({
url: "/comment/",
type: "post",
data: {
"csrfmiddlewaretoken": $("[name = 'csrfmiddlewaretoken']").val(),
"article_id": "{{ article_obj.pk }}",
"content": content,
pid: pid,
},
success: function (data) {
console.log(data);
var create_time = data.create_time;
var username = data.username;
var content = data.content;
var s = `
<li class="list-group-item">
<div>
<span>${create_time}</span>
<a href=""><span>${username}</span></a>
</div>
<div class="comment_con">
<p>${content}</p>
</div>
</li>`;
$("ul.comment_list").append(s); //清空评论框;
pid = "";
$("#comment_content").val("")
}
})
}); //回复按钮事件;
$(".reply_btn").click(function () {
$("#comment_content").focus();
var val = "@" + $(this).attr("username") + "\n";
$("#comment_content").val(val);
pid = $(this).attr("comment_pk");
})
</script>
{% endblock %}
72-博客系统之评论树的思考1
基于主键的order_by设置;
def get_comment_tree(request):
article_id = request.GET.get("article_id")
ret = list(models.Comment.objects.filter(article_id=article_id).order_by("pk").values("pk", "content", "parent_comment_id"))
return JsonResponse(ret, safe=False)
73-博客系统之评论树的思考2
1、评论树不再绑定click事件,直接通过Ajax进行展示;
<script>
$.ajax({
url: "/get_comment_tree",
type: "get",
data: {
article_id: "{{ article_obj.pk }}"
},
success: function (comment_list) {
console.log(comment_list);
$.each(comment_list, function (index, comment_object) { var pk = comment_object.pk;
var content = comment_object.content;
var parent_comment_id = comment_object.parent_comment_id;
var s = '<div class = "comment_item" comment_id=' + pk + '><span>' + content + '</span> </div>';
if (!parent_comment_id) { $(".comment_tree").append(s)
} else {
$("[comment_id=" + parent_comment_id + "]").append(s);
}
}) }
});
</script>
74-博客系统之评论事务操作
1、Django中事物的引入,with transaction.atomic()方法的使用;
2、comment视图函数的写法;
def comment(request):
print(request.POST)
article_id = request.POST.get("article_id")
pid = request.POST.get("pid")
content = request.POST.get("content")
user_id = request.user.pk
#绑定事物操作;
with transaction.atomic():
comment_obj = models.Comment.objects.create(user_id=user_id, article_id=article_id, content=content,
parent_comment_id=pid)
models.Article.objects.filter(pk=article_id).update(comment_count=F("comment_count") + 1)
response = {}
response["create_time"] = comment_obj.create_time.strftime("%Y-%m-%d %X")
response["username"] = request.user.username
response["content"] = content
return JsonResponse(response)
75-博客系统之评论的邮件发送new
1、settings.py文件中配置邮箱信息;
EMAIL_HOST = 'smtp.exmail.qq.com' # 如果是 163 改成 smtp.163.com
EMAIL_PORT = 465
EMAIL_HOST_USER = '290799238@qq.com' # 帐号
EMAIL_HOST_PASSWORD = 'hmefdscufpbnxafbjib' # 密码
DEFAULT_FROM_EMAIL = EMAIL_HOST_USER
EMAIL_USE_SSL = True
2、comment视图函数;
def comment(request):
print(request.POST)
article_id = request.POST.get("article_id")
pid = request.POST.get("pid")
content = request.POST.get("content")
user_id = request.user.pk
article_obj = models.Article.objects.filter(pk=article_id).first()
# 绑定事物操作;
with transaction.atomic():
comment_obj = models.Comment.objects.create(user_id=user_id, article_id=article_id, content=content,
parent_comment_id=pid)
models.Article.objects.filter(pk=article_id).update(comment_count=F("comment_count") + 1)
response = {}
response["create_time"] = comment_obj.create_time.strftime("%Y-%m-%d %X")
response["username"] = request.user.username
response["content"] = content
# 发送邮件;
from django.core.mail import send_mail
from cnblogs import settings
send_mail(
"您的文章%s新增了一条内容" % article_obj.title,
content,
settings.EMAIL_HOST_USER,
["tqtl@tqtl.org"]
)
# import threading
# t = threading.Thread(target=send_mail,args = (
# "您的文章%s新增了一条内容" % article_obj.title,
# content,
# settings.EMAIL_HOST_USER,
# ["tqtl@tqtl.org"]
#
# ))
# t.start()
return JsonResponse(response)
76-博客系统之后台管理页面的编辑功能
77-博客系统之后台管理的编辑器引入和参数
1、kindeditor官网;http://kindeditor.net/demo.php
2、kindEditor在线使用说明;http://kindeditor.net/doc.php
78-博客系统之文本编辑器的上传功能1
79-博客系统之文本编辑器的上传功能2
80-博客系统之文章摘要的保存
81-博客系统之bs4的简单应用
82-博客系统之bs4模块防御xss攻击
项目实战:BBS+Blog项目开发的更多相关文章
- Django学习笔记(20)——BBS+Blog项目开发(4)Django如何使用Bootstrap
本文学习如何通过Django使用Bootstrap.其实在之前好几个Django项目中已经尝试使用过了Bootstrap,而且都留有学习记录,我已经大概有了一个大的框架,那么本文就从头再走一遍流程,其 ...
- Django学习笔记(19)——BBS+Blog项目开发(3)细节知识点补充
本文将BBS+Blog项目开发中所需要的细节知识点进行补充,其中内容包括KindEditor编辑器的使用,BeautifulSoup 模块及其防XSS攻击,Django中admin管理工具的使用,me ...
- Django学习笔记(18)——BBS+Blog项目开发(2)主体思路及流程
这篇博客主要完成一个BBS+Blog项目,那么主要是模仿博客园的博客思路,使用Django框架进行练习. 准备:项目需求分析 在做一个项目的时候,我们首先做的就是谈清楚项目需求,功能需求,然后才开始写 ...
- Django学习笔记(17)——BBS+Blog项目开发(1)验证码功能的实现
本文主要学习验证码功能的实现,为了项目BBS+Blog项目打下基础. 为了防止机器人频繁登陆网站或者破坏分子恶意登陆,很多用户登录和注册系统都提供了图形验证码功能. 验证码(CAPTCHA)是“Com ...
- BBS+Blog项目流程及补充知识点
项目流程: 1. 产品需求 (1)基于用户认证组件和Ajax实现登陆验证(图片验证码) (2)基于forms组件和Ajax实现注册功能 (3)设计系统首页(文章列表渲染) (4)设计个人站点页面 (5 ...
- BBS+Blog项目开发
BBS+Blog项目开发 目前本项目已经上线,可以直接在GEEK浏览本项目效果:GEEK 1.项目需求 基于ajax和用户认证组件实现登录验证 基于ajax和form组件实现注册功能 系统首页文章列表 ...
- OA项目实战(二) 开发准备
上次的博文OA系统实践(一) 概述中,我们已经了解了OA的相关概念.从本次博文开始,我们做一个简单的OA实例. 在OA开发之前,有几个工作们需要提前做.一个是对需求进行分析,另一个是对开发环境进行选择 ...
- BBS+Blog项目代码
项目目录结构: cnblog/ |-- blog/(APP) |-- migrations(其中文件略) |-- templatetags/ |-- my_tags.py |-- utils/ |-- ...
- 小D课堂-SpringBoot 2.x微信支付在线教育网站项目实战_5-8.用户模块开发之保存微信用户信息
笔记 8.用户模块开发之保存微信用户信息 简介:开发User数据访问层,保存微信用户信息 问题: 微信回调 用户昵称乱码 解决: ...
随机推荐
- [转]解決 IE10 瀏覽器無法使用 ASP.NET 表單驗證登入的問題
今天凌晨在客戶端上線,當程式佈署到正式機後發現我們的網站唯獨只有 IE10 瀏覽器無法成功登入,任何其他瀏覽器版本或使用較低的 IE 版本都可以正常登入,使用 IE 相容性檢視也都可以正常登入,想說會 ...
- Git--将本地代码提交到服务器分支上
直接使用git push origin [branch-name],往往会出错,有可能本地版本比分支的版本更低 这个时候需要先将解决冲突,再将本地代码推送到服务器分支上 1. 在自己分支cs上提交代码 ...
- 大数据框架-YARN
YARN(Yet Another Resource Negotiator): 是一种新的 Hadoop 资源管理器 [ResourceManager:纯粹的调度器,基于应用程序对资源的需求进行调度的, ...
- 史上最简单的SpringCloud教程 | 第十三篇: 断路器聚合监控(Hystrix Turbine)(Finchley版本)
转载请标明出处: 原文首发于:https://www.fangzhipeng.com/springcloud/2018/08/30/sc-f13-turbine/ 本文出自方志朋的博客 上一篇文章讲述 ...
- 创建oracle数据库表空间并分配用户
我们在本地的oracle上或者virtualbox的oracle上 创建新的数据库表空间操作:通过system账号来创建并授权/*--创建表空间create tablespace YUJKDATAda ...
- iOS之iOS中的(null)、<null>、 nil 的问题
摘要: 你有没有过这样的经历,就是界面上显示出类似<null>.(null)这样一些东西,有时候还会莫名其妙的闪退.反反复复真是曰了犬,今天来总结一下这个问题的解决方法 前段时间开发过 ...
- Spring知识点小结(二)
一.配置非自定义的Bean(数据源DataSource模型) DBCP数据源: 导入dbcp的jar包:dbcp+pool+connector 代码实现: ...
- E. K Balanced Teams
类比背包问题,为每个学生附加一个权重$pos[i]$,意思是选择该学生后,之后可以选择$p[i]~p[i]+5$的学生. 转换公式: $$d[i][j]=max(d[i+1][q],d[i+pos][ ...
- try...catch..finally..语句中,finally是否必须存在?作用是什么
try { } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); }finally{ } 1: ...
- (Linux学习笔记一:压缩)[20180209]
学习笔记一:压缩 2015年2月5日 上午 10:23 压缩命令 压缩文件的扩展名大多是*.tar.*.tar.gz.*.tgz.*.gz.*.Z.*.bz2 常见的压缩命令gzip与bzip2,其中 ...