新闻项目——项目准备——分析与测试
第一步:表间结构分析
表间结构示意图:
第二步: 模型类分析
模型类准备:
在info文件夹中存储着我们的所有的业务逻辑,所以,我们可以在info中新建一个名为models.py的python文件,用于存储模型类定义的相关代码。
在models.py中放入下面的代码
from datetime import datetime from werkzeug.security import generate_password_hash, check_password_hash from info import constants from . import db class BaseModel(object): """模型基类,为每个模型补充创建时间与更新时间""" create_time = db.Column(db.DateTime, default=datetime.now) # 记录的创建时间 update_time = db.Column(db.DateTime, default=datetime.now, onupdate=datetime.now) # 记录的更新时间 # 用户收藏表,建立用户与其收藏新闻多对多的关系 tb_user_collection = db.Table( "info_user_collection", db.Column("user_id", db.Integer, db.ForeignKey("info_user.id"), primary_key=True), # 新闻编号 db.Column("news_id", db.Integer, db.ForeignKey("info_news.id"), primary_key=True), # 分类编号 db.Column("create_time", db.DateTime, default=datetime.now) # 收藏创建时间 ) tb_user_follows = db.Table( "info_user_fans", db.Column('follower_id', db.Integer, db.ForeignKey('info_user.id'), primary_key=True), # 粉丝id db.Column('followed_id', db.Integer, db.ForeignKey('info_user.id'), primary_key=True) # 被关注人的id ) class User(BaseModel, db.Model): """用户""" __tablename__ = "info_user" id = db.Column(db.Integer, primary_key=True) # 用户编号 nick_name = db.Column(db.String(32), unique=True, nullable=False) # 用户昵称 password_hash = db.Column(db.String(128), nullable=False) # 加密的密码 mobile = db.Column(db.String(11), unique=True, nullable=False) # 手机号 avatar_url = db.Column(db.String(256)) # 用户头像路径 last_login = db.Column(db.DateTime, default=datetime.now) # 最后一次登录时间 is_admin = db.Column(db.Boolean, default=False) signature = db.Column(db.String(512)) # 用户签名 gender = db.Column( # 性别 db.Enum( "MAN", # 男 "WOMAN" # 女 ), default="MAN") # 当前用户收藏的所有新闻 collection_news = db.relationship("News", secondary=tb_user_collection, lazy="dynamic") # 用户收藏的新闻 # 用户所有的粉丝,添加了反向引用followed,代表用户都关注了哪些人 followers = db.relationship('User', secondary=tb_user_follows, primaryjoin=id == tb_user_follows.c.followed_id, secondaryjoin=id == tb_user_follows.c.follower_id, backref=db.backref('followed', lazy='dynamic'), lazy='dynamic') # 当前用户所发布的新闻 news_list = db.relationship('News', backref='user', lazy='dynamic') def to_dict(self): resp_dict = { "id": self.id, "nick_name": self.nick_name, "avatar_url": constants.QINIU_DOMIN_PREFIX + self.avatar_url if self.avatar_url else "", "mobile": self.mobile, "gender": self.gender if self.gender else "MAN", "signature": self.signature if self.signature else "", "followers_count": self.followers.count(), "news_count": self.news_list.count() } return resp_dict def to_admin_dict(self): resp_dict = { "id": self.id, "nick_name": self.nick_name, "mobile": self.mobile, "register": self.create_time.strftime("%Y-%m-%d %H:%M:%S"), "last_login": self.last_login.strftime("%Y-%m-%d %H:%M:%S"), } return resp_dict class News(BaseModel, db.Model): """新闻""" __tablename__ = "info_news" id = db.Column(db.Integer, primary_key=True) # 新闻编号 title = db.Column(db.String(256), nullable=False) # 新闻标题 source = db.Column(db.String(64), nullable=False) # 新闻来源 digest = db.Column(db.String(512), nullable=False) # 新闻摘要 content = db.Column(db.Text, nullable=False) # 新闻内容 clicks = db.Column(db.Integer, default=0) # 浏览量 index_image_url = db.Column(db.String(256)) # 新闻列表图片路径 category_id = db.Column(db.Integer, db.ForeignKey("info_category.id")) user_id = db.Column(db.Integer, db.ForeignKey("info_user.id")) # 当前新闻的作者id status = db.Column(db.Integer, default=0) # 当前新闻状态 如果为0代表审核通过,1代表审核中,-1代表审核不通过 reason = db.Column(db.String(256)) # 未通过原因,status = -1 的时候使用 # 当前新闻的所有评论 comments = db.relationship("Comment", lazy="dynamic") def to_review_dict(self): resp_dict = { "id": self.id, "title": self.title, "create_time": self.create_time.strftime("%Y-%m-%d %H:%M:%S"), "status": self.status, "reason": self.reason if self.reason else "" } return resp_dict def to_basic_dict(self): resp_dict = { "id": self.id, "title": self.title, "source": self.source, "digest": self.digest, "create_time": self.create_time.strftime("%Y-%m-%d %H:%M:%S"), "index_image_url": self.index_image_url, "clicks": self.clicks, } return resp_dict def to_dict(self): resp_dict = { "id": self.id, "title": self.title, "source": self.source, "digest": self.digest, "create_time": self.create_time.strftime("%Y-%m-%d %H:%M:%S"), "content": self.content, "comments_count": self.comments.count(), "clicks": self.clicks, "category": self.category.to_dict(), "index_image_url": self.index_image_url, "author": self.user.to_dict() if self.user else None } return resp_dict class Comment(BaseModel, db.Model): """评论""" __tablename__ = "info_comment" id = db.Column(db.Integer, primary_key=True) # 评论编号 user_id = db.Column(db.Integer, db.ForeignKey("info_user.id"), nullable=False) # 用户id news_id = db.Column(db.Integer, db.ForeignKey("info_news.id"), nullable=False) # 新闻id content = db.Column(db.Text, nullable=False) # 评论内容 parent_id = db.Column(db.Integer, db.ForeignKey("info_comment.id")) # 父评论id parent = db.relationship("Comment", remote_side=[id]) # 自关联 like_count = db.Column(db.Integer, default=0) # 点赞条数 def to_dict(self): resp_dict = { "id": self.id, "create_time": self.create_time.strftime("%Y-%m-%d %H:%M:%S"), "content": self.content, "parent": self.parent.to_dict() if self.parent else None, "user": User.query.get(self.user_id).to_dict(), "news_id": self.news_id, "like_count": self.like_count } return resp_dict class CommentLike(BaseModel, db.Model): """评论点赞""" __tablename__ = "info_comment_like" comment_id = db.Column("comment_id", db.Integer, db.ForeignKey("info_comment.id"), primary_key=True) # 评论编号 user_id = db.Column("user_id", db.Integer, db.ForeignKey("info_user.id"), primary_key=True) # 用户编号 class Category(BaseModel, db.Model): """新闻分类""" __tablename__ = "info_category" id = db.Column(db.Integer, primary_key=True) # 分类编号 name = db.Column(db.String(64), nullable=False) # 分类名 news_list = db.relationship('News', backref='category', lazy='dynamic') def to_dict(self): resp_dict = { "id": self.id, "name": self.name } return resp_dict
说明:
- 导入时间是因为我们在模型类中用到了时间
- 文件中导入了两个库 generate_password_hash, check_password_hash,第一个是用来加密密码的,第二个是用来校验密码是否正确的
- 导入的时候还导入了constants的文件,这个文件目前我们并没创建,里面存放着一些常量信息。
在info文件夹中新建一个constants.py文件用来存储一些常量信息:
# 图片验证码Redis有效期, 单位:秒 IMAGE_CODE_REDIS_EXPIRES = 300 # 短信验证码Redis有效期,单位:秒 SMS_CODE_REDIS_EXPIRES = 300 # 七牛空间域名 QINIU_DOMIN_PREFIX = "http://oyucyko3w.bkt.clouddn.com/" # 首页展示最多的新闻数量 HOME_PAGE_MAX_NEWS = 10 # 用户的关注每一页最多数量 USER_FOLLOWED_MAX_COUNT = 4 # 用户收藏最多新闻数量 USER_COLLECTION_MAX_NEWS = 10 # 其他用户每一页最多新闻数量 OTHER_NEWS_PAGE_MAX_COUNT = 10 # 点击排行展示的最多新闻数据 CLICK_RANK_MAX_NEWS = 10 # 管理员页面用户每页多最数据条数 ADMIN_USER_PAGE_MAX_COUNT = 10 # 管理员页面新闻每页多最数据条数 ADMIN_NEWS_PAGE_MAX_COUNT = 10
第三步:导入测试数据
我们在操作台使用命令来建立迁移所需要的文件夹
python manage.py mysql init
接着生成迁移表
python manage.py mysql migrate
这里执行之后会发现没有生成成功。
没有的成功的原因是我们并没有把models导入到启动文件中,导入代码:
from info import models
导入之后我们再重新执行:
执行完成后迁移文件夹中就有一个新的迁移文件了
迁移完成之后就开始建表
python manage.py mysql upgrade
显示如下:
再去数据库看看
这样就算成功了
接着去导入数据
现在这个表里都是空的,自己填是不可能的,接下来我们用sql的脚本来导入测试数据。
首先在命令提示符中进入mysql交互环境。
然后使用项目中绑定的数据库。
再从sql文件中导入数据
source D:\information_info_category.sql
source D:\information_info_news.sql
导入数据后你的表会是这样:(这里有个要注意的地方那就是你的数据库没有指定字符编码utf8的话下面那些会成乱码形式)
第四步:蓝图导入错误
我们之前的redis还有些错误没处理,现在就来处理
我们先来到views中导入redis_store,会发现报错
再来到info的init中会发现链接对象是灰色的(这也就是我们要处理的地方)
这里的情况你会发现和db的问题是差不多的,那我们就根据db来试着处理,这里我们要加入一个新东西golbal(golbal:声明全局的变量)
看见redis_store亮了之后以为成功,其实并没有
这个异常时ImportError,通常这个异常产生的原因有两个:
- 导入错误
- 导入某个变量的时候,这个变量还不存在
这里导入错误产生的原因就是第二种,此时变量还不存在。那么为啥呢?
在manage.py中我们导入了info,之前讲过,导入就会自动执行里面的代码,而info是一个文件夹,所以就会自动执行文件夹中的__init__初始化文件。
在info中,当执行到下面标注的地方时,直接去到index文件夹中的__init__里面执行初始化了。
来到index的__init__文件中,我们发现,又导入了views.py这个视图函数文件,
而在这个views.py视图函数中,它导入了这个redis_store的redis连接对象了。
这个就是原因了,此时这个变量还没生出来呢!
所以,使用蓝图的时候有一个地方要注意:蓝图在哪里注册,就在它之前导入,避免变量不存在的情况。
基于这个原因,我们修改一下之前的导入蓝图的地方
这样弄好就可以测试一下,成了就可以把views视图中的redis测试代码删掉或注释。
第五步:首页显示渲染
渲染主页肯定会用到html文件,而这些文件默认会放到和app同级目录下的templates文件夹中,而目前,我们的app在info的__init__文件中。
所以我们直接将templates和static拖到info目录下
拖放好之后,我们还要给templates创建一个存放新闻页面的html的文件夹news,并将static的news的html文件夹当中,将index.html文件使用鼠标拖到templates的news文件夹中(这样Pycharm会自动修改里面的文件导入路径)。
然后把views视图函数的相关内容修改,完成页面的渲染
显示如下:
第六步:左侧图标设置
当我们运行代码的时候运行结果会有一个不一样的东西就是下面框里的
这个东西其实就是我们的网页里的左侧图标,太给我们发送请求,但我们一直没有理他,所以会显示404
现在来处理一下
from flask import current_app @index_blue.route('/favicon.ico') def favicon(): """title左侧图标""" return current_app.send_static_file('news/favicon.ico')
注意:
current_app是全局的app,通常我们会使用它,避免启动文件换名字带来的修改问题。
再次访问之后,图标就来了:
注意:有时候请求会来的很慢导致左侧图标没变,这时候需要去改一下端口。