Python学习之路—2018/7/10
博客开发项目流程
一般来说,一个项目的开发流程分为:项目需求、设计表结构、功能开发、测试功能、产品上线,本次学习以博客园为蓝本进行开发。
1.项目需求
博客的开发的需求主要有以下几点:
- 基于auth模块和Ajax实现登录验证
- 基于forms组件和Ajax实现注册功能
- 设计博客首页
- 设计个人站点页面
- 设计文章详情页面
- 实现文章点赞功能
- 实现文章的评论功能,包括对文章的评论以及对文章评论的评论
- 实现富文本编辑框
- 防止xss攻击(例如当用户的文章中含有JS代码,其他用户进行访问时浏览器会执行JS代码,大大降低了用户的安全性)
2.设计表结构
根据功能分析主要有用户信息表(User)、博客信息表(Blog)、博客文章分类信息表(Sort)、博客文章标签信息表(Tag)、博客文章表(Article)、文章点赞表(Like)、文章评论表(Comment),它们之间的关系如下图所示:
models.py
from django.db import models
from django.contrib.auth.models import AbstractUser
class User(AbstractUser):
"""
用户信息表
"""
uid = models.AutoField(primary_key=True)
phone = models.CharField(max_length=11, unique=True, null=True)
avatar = models.FileField(upload_to="avatars/", default="/avatars/default.jpg")
create_time = models.DateField(verbose_name="创建日期", auto_now_add=True)
blog = models.OneToOneField(to="Blog", to_field="bid", null=True, on_delete=models.CASCADE) # 与博客建立一对一关系
def __str__(self):
return self.username
class Blog(models.Model):
"""
博客信息表
"""
bid = models.AutoField(primary_key=True)
title = models.CharField(max_length=32, verbose_name="个人博客标题")
theme = models.CharField(max_length=32, verbose_name="博客主题")
site = models.CharField(max_length=32, verbose_name="个人站点名称")
def __str__(self):
return self.site
class Sort(models.Model):
"""
博客文章分类信息表
"""
sid = models.AutoField(primary_key=True)
title = models.CharField(max_length=32, verbose_name="分类标题")
blog = models.ForeignKey(to="Blog", to_field="bid", on_delete=models.CASCADE) # 与博客建立一对多关系
def __str__(self):
return self.title
class Tag(models.Model):
"""
博客文章标签信息表
"""
tid = models.AutoField(primary_key=True)
name = models.CharField(max_length=32, verbose_name="标签名称")
blog = models.ForeignKey(to="Blog", to_field="bid", on_delete=models.CASCADE) # 与博客建立一对多关系
def __str__(self):
return self.name
class Article(models.Model):
"""
博客文章表
"""
aid = models.AutoField(primary_key=True)
title = models.CharField(max_length=32, verbose_name="文章标题")
abstract = models.CharField(max_length=32, verbose_name="文章摘要")
create_time = models.DateField(verbose_name="创建日期", auto_now_add=True)
content = models.TextField()
user = models.ForeignKey(to="User", to_field="uid", verbose_name="作者", on_delete=models.CASCADE)
sort = models.ForeignKey(to="Sort", to_field="sid", null=True, on_delete=models.CASCADE)
# 与标签建立多对多关系
tag = models.ManyToManyField(to="Tag", through="ArticleToTag", through_fields=("article", "tag"))
def __str__(self):
return self.title
class ArticleToTag(models.Model):
aid = models.AutoField(primary_key=True)
article = models.ForeignKey(to="Article", to_field="title", verbose_name="文章题目", on_delete=models.CASCADE)
tag = models.ForeignKey(to="Tag", to_field="name", verbose_name="文章标签", on_delete=models.CASCADE)
def __str__(self):
name = self.article.title + "---" + self.tag.name
return name
class Like(models.Model):
"""
文章点赞表
"""
lid = models.AutoField(primary_key=True)
user = models.ForeignKey(to="User", to_field="uid", null=True, on_delete=models.CASCADE)
article = models.ForeignKey(to="Article", to_field="aid", null=True, on_delete=models.CASCADE)
is_like = models.BooleanField(default=True)
class Comment(models.Model):
"""
文章评论表
"""
cid = models.AutoField(primary_key=True)
user = models.ForeignKey(to="User", to_field="uid", null=True, on_delete=models.CASCADE)
article = models.ForeignKey(to="Article", to_field="aid", null=True, on_delete=models.CASCADE)
create_time = models.DateField(verbose_name="创建日期", auto_now_add=True)
content = models.CharField(max_length=255, verbose_name="评论内容")
parent_comment = models.ForeignKey(to="Comment", to_field="cid", null=True, on_delete=models.CASCADE) # 根评论
def __str__(self):
return self.content
由于需要用到mysql,所以需要在setteings.py中配置数据库信息
在第一张User表中,由于是继承了AbstractUser,所以也需要配置信息
settings.py
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'blog',
'USER': 'root',
'PASSWORD': 'admin',
'HOST': 'localhost',
'PORT': 3306
}
}
AUTH_USER_MODEL = "app01.User"
注意,使用mysql时需要在项目的init.py中添加如下代码:
import pymysql
pymysql.install_as_MySQLdb()
3.功能开发
3.1 登录验证
login.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<link rel="stylesheet" href="/static/blog/css/bootstrap.min.css">
<link rel="icon" href="/static/blog/image/favicon.ico">
<style type="text/css">
body {
background: url(../static/blog/image/bk.jpeg) no-repeat;
background-size: 100%;
overflow: hidden;
}
h3 {
padding: 5px;
border-bottom: 1px solid #ddd;
}
</style>
</head>
<body>
<div class="row">
<div class="col-md-4 col-md-offset-4" style="margin-top: 100px">
<form>
<div class="well">
<h3 style="text-align: center">登录界面</h3>
<div class="form-group">
<label for="username">用户名</label>
<input type="text" class="form-control" id="username" placeholder="用户名" autocomplete="off">
</div>
<div class="form-group">
<label for="password">密码</label>
<input type="password" class="form-control" id="password" placeholder="密码" autocomplete="off">
</div>
<div class="row form-group">
<div class="col-md-6">
<label for="password">验证码</label>
<input type="text" class="form-control" id="verify_code" placeholder="请输入验证码" autocomplete="off">
</div>
<div class="col-md-6">
<label for="password"></label>
<img src="/verify_code" style="height: 40px; 183px">
</div>
</div>
<button type="button" class="btn btn-default">登录</button>
</div>
</form>
</div>
</div>
</body>
</html>
views.py
from django.shortcuts import render, HttpResponse
import random
from PIL import Image, ImageDraw, ImageFont, ImageFilter
from io import BytesIO
def login(request):
return render(request, "login.html")
def random_color():
color = (random.randint(64, 255), random.randint(64, 255), random.randint(64, 255))
return color
def random_color2():
color = (random.randint(32, 127), random.randint(32, 127), random.randint(32, 127))
return color
def random_char():
"""
随机数/字母
"""
random_num = str(random.randint(0, 9))
random_low = chr(random.randint(97, 122)) # a~z
random_upper = chr(random.randint(65, 90)) # A~Z
random_chars = random.choice([random_num, random_low, random_upper])
return random_chars
def verify_code(request):
"""
验证码
"""
image = Image.new("RGB", (183, 40), (255, 255, 255))
image_font = ImageFont.truetype("static/blog/font/Arial.ttf", 32)
draw = ImageDraw.Draw(image)
# 给每个坐标填充颜色
for x in range(183):
for y in range(40):
draw.point((x, y), fill=random_color())
for i in range(5):
draw.text((20+i*30, 0), random_char(), font=image_font, fill=random_color2())
image = image.filter(ImageFilter.BLUR) # 模糊处理
f = BytesIO()
image.save(f, "png")
data = f.getvalue()
return HttpResponse(data)
登录界面效果如下图所示: