项目实战: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': '3306'#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="260" height="45" 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攻击
“终身”学习,生活充满诗意!