项目开发基本流程
- 需求分析
- 架构设计
- 分组开发
- 提交测试
- 交付上线
项目分析流程
仿BBS项目:
- 仿造博客园项目
- 核心:文章的增删改查
- 技术栈
Django
、MySQL
- 功能
- 注册 (forms校验,页面渲染,上传头像)
- 登录 (自定义图片验证码)
- 首页:文章展示、侧边栏过滤(分类,标签,时间)
- 文章详情:点赞点踩、评论(父评论和子评论)
- 后台管理:当前用户文章展示(文章增删改查)
- 发布文章
- 项目版本信息:
python3.8
、django2.2.2
、mysql:5.7
、jquery2.x
、bootstrap3
项目表设计及关联
创建数据库bbs
create database bbs
表分析
一共需要创建七张表
- 用户表(基于
auth
模块的user
表扩写) - 个人站点表(跟用户表一对一关系)
- 分类表(和个人站点表一对多、和文章表一对多)
- 标签表(和个人站点表一对多、和文章表多对多)
- 点赞点踩表(和用户表一对多、和文章表一对多)
- 评论表(和用户表一对多,和文章表一对多)
- 文章表(和个人站点表一对多)
前期准备
创建项目
1.安装django 2.2.2
版本
pip3 install django==2.2.2
2.使用pycharm
创建django
项目
配置
setting.py
TEMPLATES = {
"DIRS":[os.path.joi(BASE_DIR, "templates")]
}
配置语言环境
LANGUAGE = 'zh-hans' # 语言汉化
TIME-ZONE = 'Asia/Shanghai' # 时区使用上海时区
USE_I18N = True
USE_L10N = True
USE_TZ = False
配置数据库
DATABASES = {
'default': {
'ENGINE':'django.db.backends.mysql',
'NAME': 'bbs',
'HOST': '127.0.0.1',
'PORT': 3306,
'USER': 'root',
'PASSWORD':'password'
}
}
在models中写表模型
from django.db import models
# Create your models here.
from django.contrib.auth.models import AbstractUser
class UserInfo(AbstractUser): # 继承AbstractUser表 只用写auth表中没有的字段
phone = models.CharField(max_length=32, null=True, verbose_name='用户手机号')
# upload_to是文件保存在什么路径
icon = models.FileField(upload_to='icon/', default='icon/default.png', null=True, verbose_name='用户头像')
# 用户表和博客表一对一
blog = models.OneToOneField(to='Blog', on_delete=models.CASCADE, null=True)
class Blog(models.Model):
title = models.CharField(max_length=32, null=True, verbose_name='主标题')
site_title = models.CharField(max_length=32, null=True, verbose_name='副标题')
site_style = models.CharField(max_length=64, null=True, verbose_name='站点样式')
class Tag(models.Model):
name = models.CharField(max_length=32, verbose_name='标签名', null=True)
# 标签和博客是一对多 一个博客有多个标签
blog = models.ForeignKey(to='Blog', on_delete=models.CASCADE)
class Classify(models.Model):
name = models.CharField(max_length=32, verbose_name='分类名')
# 分类和博客是一对多关系 一个博客有多个分类
blog = models.ForeignKey(to='Blog', on_delete=models.CASCADE)
class Article(models.Model):
title = models.CharField(max_length=32, verbose_name='文章标题')
desc = models.CharField(max_length=255, verbose_name='文章摘要')
content = models.TextField(verbose_name='文章内容')
create_time = models.DateTimeField(auto_now_add=True) # 第一次创建时自动添加时间
# 文章和分类表是一对多 一个分类有多篇文章
classify = models.ForeignKey(to='Classify', on_delete=models.CASCADE)
# 文章和标签是多对多关系 自动创建第三张表
tag = models.ManyToManyField(to='Tag')
# 文章和博客是一对多关系 一个博客对应多篇文章
blog = models.ForeignKey(to='Blog', on_delete=models.CASCADE)
class UpAndDown(models.Model):
create_time = models.DateTimeField(auto_now_add=True, verbose_name='点赞点踩时间')
# 和用户表是一对多关系 一个用户可以有多条点赞点踩记录
user = models.ForeignKey(to='UserInfo', on_delete=models.CASCADE)
# 和文章也是一对多
article = models.ForeignKey(to='Article', on_delete=models.CASCADE)
# 1代表点赞 0代表点踩
is_up = models.BooleanField(verbose_name='是否点赞')
class Comment(models.Model):
content = models.CharField(max_length=64, verbose_name='评论内容')
create_time = models.DateTimeField(auto_now_add=True, verbose_name='评论时间')
user = models.ForeignKey(to='UserInfo', on_delete=models.CASCADE, null=True)
article = models.ForeignKey(to='Article', on_delete=models.CASCADE, null=True)
# 自关联字段 只能存已有评论的主键值
parent = models.ForeignKey('self', on_delete=models.CASCADE, null=True)
# 自关联的其他方式
# parent = models.ForeignKey(to='Comment', on_delete=models.CASCADE)
# parent = models.IntegerField(null=Ture)
终端执行数据库迁移命令
python38 manage.py makemigretions
python38 manage.py migrater
注册功能
注册forms
编写
在根目录下创建
blog_forms.py
文件
from django import forms
from django.forms import widgets
from blog.models import UserInfo
from django.core.exceptions import ValidationError # 合法性错误
class User(forms.Form):
# 用户名 密码 确认密码 邮箱
username = forms.CharField(max_length=8, min_length=3, label='用户名', required=True,
error_messages={'max_length': '用户名最多只能输入8位',
'min_length': '用户名最少输入3位',
'required': '用户名必须填'
},
# 添加bootstr样式
widget=widgets.TextInput(attrs={'class': 'form-control'})
)
password = forms.CharField(max_length=16, min_length=8, required=True, label='密码',
error_messages={
'max_length': '密码最长16位',
'min_length': '密码最短8位',
'required': '密码不能为空',
},
widget=widgets.PasswordInput(attrs={'class': 'form-control'})
)
re_password = forms.CharField(max_length=16, min_length=8, required=True, label='密码',
error_messages={
'max_length': '密码最长16位',
'min_length': '密码最短8位',
'required': '密码不能为空',
},
widget=widgets.PasswordInput(attrs={'class': 'form-control'})
)
email = forms.EmailField(label='邮箱地址', widget=widgets.EmailInput(attrs={'class': 'form-control'}))
# 局部钩子 校验用户名是否存在
def clean_username(self):
name = self.cleaned_data.get('username')
if UserInfo.objects.filter(username=name).first():
# 用户已存在
raise ValidationError('用户名已存在') # 校验错误抛出异常
else:
return name
# 局部钩子 校验用户名是否存在
# def clean_username(self):
# username = self.cleaned_data.get('username')
# try:
# UserInfo.objects.get(username=username)
# print(UserInfo.objects.get(username=username), type(UserInfo.objects.get(username=username)))
# raise ValidationError('用户名已存在')
# except Exception:
# return username
# 全局钩子 校验两次输入密码是否一致
def clean(self):
pwd = self.cleaned_data.get('password')
re_pwd = self.cleaned_data.get('re_password')
if pwd != re_pwd:
raise ValidationError('两次密码不一致') # 主动抛出合法性错误
else:
return self.cleaned_data
路由配置
在项目同名文件夹下的
urls.py
中配置路由
from django.contrib import admin
from django.urls import path
from blog import views
urlpatterns = [
path('admin/', admin.site.urls),
path('register/', views.register),
]
编写视图函数
views.py
from django.shortcuts import render
from blog.blog_forms import User
def register(request):
form_obj = User()
if request.method == 'GET': # 当请求为get时返回注册界面,并返回forms组件对象进行数据校验
return render(request, 'register.html', {'form_obj': form_obj})