flask 新闻管理系统搭建试验

本文分前后两个部分,前半部分是跑通该项目的过程,后半部分是提炼其中的评论功能到其他项目的过程

带评论模板的项目:

博客https://github.com/goalong/flask-demo

https://github.com/13697165025/FlaskMyBlog

*新闻https://github.com/blue-harddisk/flask

一个新闻web,具体功能:登录注册,首页有分类,新闻详情页面有点赞,评论,回复评论功能,个人中心,后台管理

新建news_website的py3.7虚拟环境

activate

使用requirements安装:

pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple

pip install -r requirements.txt -i http://pypi.douban.com/simple

pip install -r requirements.txt -i http://pypi.hustunique.com/simple

pip install -r requirements.txt -i http://pypi.sdutlinux.org/simple

pip install -r requirements.txt -i http://pypi.mirrors.ustc.edu.cn/simple

MarkupSafe无法正常安装

报错:

Command errored out with exit status 1: python setup.py egg_info Check the logs for full command output.

cannot import name 'Feature' from 'setuptools'

setuptool版本的问题,python3源中的setuptools已经升级到46以上。所以导致pip安装失败.参考:https://blog.csdn.net/pengshengege/article/details/105113561

pip install --upgrade pip setuptools==45.2.0

报错:

Failed to build mysqlclient

无法打开包括文件: “mysql.h”: No such file or directory

参考:

https://blog.csdn.net/weixin_30564785/article/details/99818573?depth_1-utm_source=distribute.pc_relevant.none-task&utm_source=distribute.pc_relevant.none-task

https://segmentfault.com/a/1190000016563585

https://blog.csdn.net/cn_1937/article/details/81533544?depth_1-utm_source=distribute.pc_relevant.none-task&utm_source=distribute.pc_relevant.none-task

感觉都很麻烦,最后把它从requirement中删除了,运行时自动装上了最新版mysqlclient

使用migrations迁移数据库

https://www.cnblogs.com/senlinyang/p/8387007.html

为了导出数据库迁移命令,Flask-Migrate 提供了一个MigrateCommand 类,可附加到Flask-Script 的manager 对象上。在这个例子中,MigrateCommand 类使用db 命令附加。

manager.add_command('db', MigrateCommand)

在维护数据库迁移之前,要使用init 子命令创建迁移仓库:

python hello.py db init

python manage.py mysql init

报错:Directory migrations already exists

删除文件后重新运行

migrate 子命令用来自动创建迁移脚本:

python hello.py db migrate -m "initial migration"

python manage.py mysql migrate -m "initial migration"

报错:

File "D:\ProgramData\Anaconda3\envs\news_website\lib\site-packages\sqlalchemy\dialects\mysql\mysqldb.py", line 102, in dbapi

    return __import__('MySQLdb')

ModuleNotFoundError: No module named 'MySQLdb'

尝试安装MySQLdb

pip install mysql-python -i https://pypi.tuna.tsinghua.edu.cn/simple

报错:

_mysql.c(42): fatal error C1083: 无法打开包括文件: “config-win.h”: No such file or directory

    error: command 'C:\\Program Files (x86)\\Microsoft Visual Studio\\2017\\BuildTools\\VC\\Tools\\MSVC\\14.16.27023\\bin\\HostX86\\x64\\cl.exe' failed with exit status 2

参考:https://blog.csdn.net/weixin_42840933/article/details/85274313

https://www.cnblogs.com/SH170706/p/10082987.html

下载包,从dos命令行进入下载后的文件夹,执行下面命令:

pip install mysqlclient-1.3.14-cp37-cp37m-win_amd64.whl

这个版本是对的,可以用MySQLdb模块

    spec.loader.exec_module(module)

  File "<frozen importlib._bootstrap_external>", line 728, in exec_module

  File "<frozen importlib._bootstrap>", line 219, in _call_with_frames_removed

  File "migrations\env.py", line 87, in <module>

    run_migrations_online()

  File "migrations\env.py", line 72, in run_migrations_online

    connection = engine.connect()

Traceback (most recent call last):

  File "manage.py", line 30, in <module>

    manager.run()

配置:

SQLALCHEMY_DATABASE_URI

mysql://username:password@hostname/database

匹配数据库名与配置database名,再次运行,成功

使用db upgrade 命令把迁移应用到数据库中:

python hello.py db upgrade

python manage.py mysql upgrade

尝试启动项目报错:

redis.exceptions.ConnectionError: Error 10061 connecting to 127.0.0.1:6379. 由于目标计算机积极拒绝,无法连接。.

参考:https://blog.csdn.net/qq_41192383/article/details/86559296

下载并安装Redis-x64-3.0.503.msi,地址:

https://github.com/MicrosoftArchive/redis/releases

安装完成后,启动服务(找到安装路径,双击redis-cli.exe文件即可)

requirement装的redis==2.10.6为python提供的模块redis-py

启动项目:

python manage.py runserver

http://localhost:5000/

点击新闻列表时报错:

File "D:\anacondaProject\flask\info\user\views.py", line 138, in news_release

    category_list.pop(0)

IndexError: pop from empty list

判断是否为空:

users = session.query(user).filter(user.id == '234).all()#users为object列表

if(len(user) == 0):

print "数据库中没有id为234的用户‘’

else:

print "数据库中包含id为234的用户“

if(len(categorys)!=0):

            for category in categorys:

                category_list.append(category.to_dict())

            # 删除第0个元素

            category_list.pop(0)

发布报错:

File "D:\anacondaProject\flask\info\user\views.py", line 151, in news_release

    index_image = request.files.get("index_image").read()

AttributeError: 'NoneType' object has no attribute 'read'

判断是否为空

成功发布新闻与评论:

其中评论部分的数据库实现:

Comment表:

Comment-like表:

Sql转储并导入

Info/models中News类有有关评论的数据库查询方法

class News(BaseModel, db.Model):

    """新闻"""

    __tablename__ = "info_news"

 

    id = db.Column(db.Integer, primary_key=True)  # 新闻编号

comments = db.relationship("Comment", lazy="dynamic")

# dynamic: 不加载记录,但提供加载记录的查询,也就是生成query对象,只可以用在一对多和多对多关系中,不可以用在一对一和多对一中

即 lazy屬性的對向關系模型只能是多的一側

relationship的第一个参数表示这个关系的另一个数据库模型是哪个,这里是User,第二个参数backref表示给关联的数据库模型添加一个属性,这里是role。

也就是说,你可以通过User模型的role这个属性去访问Role模型,比如你在views.py中的查询结果,你可以通过user.role.name得到roles表中对应记录的name,user.role.id则得到roles表中对应记录的id

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)  # 用户编号

添加:

注意user_id news_id等原表不统一的字段处理--user_id只去除外键,news_id去除外键并改名music_id,同时在数据库中修改字段名,删除两表外键

在基类时间报错:

module 'datetime' has no attribute 'now'

我引入的是datetime,而非第二层,参照用户处使用datetime.datetime.now()

修改comment中todict方法下用户相关:

"user_id": self.user_id,

"user": User.query.get(self.user_id).name,

 

Info/news/views中有新闻详情展示所有评论,添加评论,与赞的方法

# 详情页

@news_blue.route("/<int:news_id>")

@user_login_data

#使用了装饰器,增加@functools.wraps(f), 可以保持当前装饰器去装饰的函数的 __name__ 的值不变

def news_detail(news_id):

    user = g.user

#。。。省略部分代码

    """

        获取到新闻评论列表

        1 :我们需要查询新闻评论表,在查询的时候,直接通过新闻id就可以查询,因为所有的评论都是针对新闻产生的

    """

    comments = Comment.query.filter(Comment.news_id ==

                                    news_id).order_by(Comment.create_time.desc()).all()

    comments_list = []

    # 获取到新闻详情页面评论点赞的数据

    # TODO

    commentLike_list = []

    comment_like_ids = []

 

    if user:

        # 根据user.id,查出用户点赞过的CommentLike对象

        commentLike_list = CommentLike.query.filter(CommentLike.user_id == user.id).all()

        # 遍历CommentLike所有对象,查出点赞评论的id

        comment_like_ids = [comment_like.comment_id for comment_like in commentLike_list]

 

    for comment in comments:

 

        comment_dict = comment.to_dict()

        comment_dict["is_like"] = False

        # comment_like_ids:所有的评论点赞id

        if comment.id in comment_like_ids:

            comment_dict["is_like"] = True

        comments_list.append(comment_dict)

 

    data = {

        "user_info": user.to_dict() if user else None,

        "click_news_list": news_list,

        "news": news.to_dict(),

        "is_collected": is_collected,

        "comments": comments_list,

        "is_followed": is_followed

    }

 

    return render_template("news/detail.html", data=data)

先再play中加,成功后移植到detail路由

原先的user一直使用session保存,但没有为他申明变量,多次调用有点繁琐:

如果没有设置session时,获取到的session就是None

尝试获取后再判user是否为None

判空:

`if x is not None`是最好的写法,清晰,不会出现错误

删除了data中对user\news的储存,因为该用法把user\news当作带to_dict方法的对象了,

"click_news_list": news_list,

"is_followed": is_followed

"is_collected": is_collected,

也因为不需要删除了

click_news_list

登陆后报错:

name 'Comment' is not defined

因为没有导入到view

 

 

# 点赞

@news_blue.route("/comment_like", methods=["POST"])

@user_login_data

def comment_like():

    user = g.user

    if not user:

        return jsonify(errno=RET.SESSIONERR, errmsg="请登陆")

 

    comment_id = request.json.get("comment_id")

    news_id = request.json.get("news_id")

    # 判断当前用户的动作,到底是想点赞,还是想取消点赞

    action = request.json.get("action")

 

    comment = Comment.query.get(comment_id)

 

    if action == "add":

        # 用户想点赞

        comment_like = CommentLike.query.filter(CommentLike.comment_id == comment_id,

                                                CommentLike.user_id == user.id).first()

        # 查询出来之后,需要判断当前这条评论用户是否已经点赞,如果查询出来为空,说明之前没有点赞,那么就可以点赞

        if not comment_like:

            comment_like = CommentLike()

            comment_like.comment_id = comment_id

            comment_like.user_id = user.id

            db.session.add(comment_like)

            # 因为点赞了,所以需要把当前的评论进行加1

            comment.like_count += 1

 

    else:

        # 取消点赞的动作

        comment_like = CommentLike.query.filter(CommentLike.comment_id == comment_id,

                                                CommentLike.user_id == user.id).first()

        if comment_like:

            db.session.delete(comment_like)

            comment.like_count -= 1

 

    db.session.commit()

    return jsonify(errno=RET.OK, errmsg="点赞成功")

 

# 评论

@news_blue.route("/news_comment", methods=["POST"])

@user_login_data

def news_comment():

    user = g.user

    if not user:

        return jsonify(errno=RET.SESSIONERR, errmsg="请登陆")

    news_id = request.json.get("news_id")

    # 评论的内容

    comment_str = request.json.get("comment")

    # 评论的父id

    parent_id = request.json.get("parent_id")

    """

       用户评论:

           用户如果在登录的情况下,可以进行评论,未登录,点击评论弹出登录框

           用户可以直接评论当前新闻,也可以回复别人发的评论

           1:用户必须先登陆才能进行评论,如果不登陆,直接返回

           2:如果需要评论,那么就需要知道当前评论的是哪条新闻,如果想知道是哪条新闻,那么就可以通过news_id 查询出来新闻

           3:如果评论成功之后,那么我们需要把用户的评论信息存储到数据库,为了方便下次用户在进来的时候可以看到评论

    """

    news = News.query.get(news_id)

    comment = Comment()

    comment.user_id = user.id

    comment.news_id = news.id

    comment.content = comment_str

    # 不是所有的新闻都有评论

    if parent_id:

        comment.parent_id = parent_id

 

    db.session.add(comment)

    db.session.commit()

    return jsonify(errno=RET.OK, errmsg="评论成功", data=comment.to_dict())

前端何处news_id传到后端request.json.get("news_id"),确定是Json

和表单中其他内容接收方式相同,可能是submit时js传过来的

去除news = News.query.get(news_id)查数据库部分

新增评论路由时报错:

@news_blue.route("/news_comment", methods=["POST"])

NameError: name 'news_blue' is not defined

记得将路由名改为该蓝图名

报错:

line 521, in news_comment

    comment.user_id = user.id

AttributeError: 'str' object has no attribute 'id'

改为session取出的user_id

报错:

sqlalchemy.exc.OperationalError: (_mysql_exceptions.OperationalError) (1048, "Column 'music_id' cannot be null") [SQL: 'INSERT INTO info_comment (create_time, update_time, user_id, music_id, content, parent_id, like_count) VALUES (%s, %s, %s, %s, %s, %s, %s)'] [parameters: (datetime.datetime(2020, 4, 7, 16, 21, 17, 132015), datetime.datetime(2020, 4, 7, 16, 21, 17, 132015), 48, None, '1', None, 0)] (Background on this error at: http://sqlalche.me/e/e3q8)

line 530, in news_comment

    db.session.commit()

提交前为comment.music_id赋值

报错:

return jsonify(errno=RET.OK, errmsg="评论成功", data=comment.to_dict())

NameError: name 'jsonify' is not defined

from flask import jsonify

报错:

File "D:\anacondaProject\music-website-flask\app\home\views.py", line 433, in play

    comment_dict = comment.to_dict()

  File "D:\anacondaProject\music-website-flask\app\models.py", line 153, in to_dict

    "user": User.query.get(self.user_id).to_dict(),

AttributeError: 'User' object has no attribute 'to_dict'

在comment model中to_dic方法调用了

"user": User.query.get(self.user_id).to_dict(),

因此修改为:

"user": self.user_id,

"news_id": self.music_id,

报错:

return jsonify(errno=RET.SESSIONERR, errmsg="请登陆")

return jsonify(errno=RET.OK, errmsg="评论成功", data=comment.to_dict())

NameError: name 'RET' is not defined

原导入方法:

from info.utils.response_code import RET

是一堆常量,OK为0,SESSIONERR为4101,修改:

return jsonify(errno="4101", errmsg="请登陆")

return jsonify(errno="0", errmsg="评论成功", data=comment.to_dict())

点赞路由同上一样改

Html

{% if data.user_info %}

            <form action="" class="comment_form" data-newsid="{ { data.news.id }}">

                <div class=" person_pic">

                    <img src="{% if data.user_info.avatar_url %}

                    { { data.user_info.avatar_url }}

                {% else %}

                    ../../static/news/images/person01.png

                {% endif %}" alt="用户图标">

                </div>

                <textarea placeholder="请发表您的评论" class="comment_input"></textarea>

                <input type="submit" name="" value="评 论" class="comment_sub">

            </form>

        {% else %}

            <div class="comment_form_logout">

                登录发表你的评论

            </div>

        {% endif %}

 

        <div class="comment_count">

            { { data.news.comments_count }}条评论

        </div>

 

        <div class="comment_list_con">

            {% for comment in data.comments %}

                <div class="comment_list">

                    <div class="person_pic fl">

                        <img src="{% if comment.user.avatar_url %}

                { { comment.user.avatar_url }}

            {% else %}

                ../../static/news/images/person01.png

            {% endif %}" alt="用户图标">

                    </div>

                    <div class="user_name fl">{ { comment.user.nick_name }}</div>

                    <div class="comment_text fl">{ { comment.content }}</div>

                    {% if comment.parent %}

                        <div class="reply_text_con fl">

                            <div class="user_name2">{ { comment.parent.user.nick_name }}</div>

                            <div class="reply_text">

                                { { comment.parent.content }}

                            </div>

                        </div>

                    {% endif %}

                    <div class="comment_time fl">{ { comment.create_time }}</div>

 

                    <a href="javascript:;" class="comment_up

                    {% if comment.is_like %}

                        has_comment_up

                    {% endif %} fr"

                       data-commentid="{ { comment.id }}"

                       data-likecount="{ { comment.like_count }}"

                       data-newsid="{ { data.news.id }}">

                        {% if comment.like_count > 0 %}

                            { { comment.like_count }}

                        {% else %}

                            赞

                        {% endif %}</a>

 

                    <a href="javascript:;" class="comment_reply fr">回复</a>

                    <form class="reply_form fl" data-commentid="{ { comment.id }}" data-newsid="{ { data.news.id }}">

                        <textarea class="reply_input"></textarea>

                        <input type="button" value="回复" class="reply_sub fr">

                        <input type="reset" name="" value="取消" class="reply_cancel fr">

                    </form>

                </div>

            {% endfor %}

        </div>

 

新增form中:

data-newsid="{ { data.news.id }}"

改为musicid-仿照收藏逻辑

加入评论列表后报错:

{ { data.news.comments_count }}条评论

{% for comment in data.comments %}

jinja2.exceptions.UndefinedError: 'data' is undefined

原view的news = News.query.get(news_id)取出后

由于在model news中有

comments = db.relationship("Comment", lazy="dynamic")

data:{

"comments_count": self.comments.count(),

所以可以取出

此处加comments_count性价比不高,删除

但data也无法识别,是由于渲染模板的时候没传过去

报错:

jinja2.exceptions.UndefinedError: 'dict object' has no attribute 'news'

修改<a href="javascript:;" class="comment_up处的

data-newsid="{ { data.news.id }}

为mus.music_id

 

CSS

comment_form等class

将news文件夹放到static下,在网页中引入对应文件

比照不同之处,逻辑确实未登录

其中main.css中部分class如form-group的样式可能影响原有样式

导致时间滑到上一行的是原布局中的

box-sizing: border-box

解决参考:https://blog.csdn.net/qq_33644670/article/details/95071581

给不需要box-sizing:border-box的元素上加上 box-sizing:content-box;

限制浮动元素在div内,参考:

https://www.cnblogs.com/heqichang/archive/2011/08/01/2123887.html

我使用的是父div加fl

且Div宽度占满父div,参考:

https://segmentfault.com/q/1010000017319613?utm_source=tag-newest

style="width:100%"

评论按钮错位:

评论输入框样式:

.comment_form .comment_input {

    float: left;

    width: 690px;

    height: 60px;

    margin-left: 20px;

    border-radius: 4px;

    padding: 10px;

    outline: none;

    border: 1px solid #2185ed;

}

需要换行的浮动元素外包裹:

<div class="fl" style="width:750px">

JS

Detail.js使用jQuery,

添加评论后前端点击评论时url为:

http://localhost:5000/news/news_comment

修改:

$.ajax({

            url: "/news/news_comment",

url: "/home/news_comment",

http://localhost:5000/home/news_comment

依然404,参考收藏、链接等

收藏为:

{ { url_for('home.like',id=mus.music_id) }}

相似链接:

<a href="http://localhost:5000/detail?key=SHANGHAI">

评论成功,但显示用户名不对,原列表中的用户名也要加上

评论提交的Ajax中

if (comment.user.avatar_url) {

                        comment_html += '<img src="' + comment.user.avatar_url + '" alt="用户图标">'

                    } else {

                        comment_html += '<img src="../../static/news/images/person01.png" alt="用户图标">'

                    }

                    comment_html += '</div>'

                    comment_html += '<div class="user_name fl">' + comment.user.nick_name + '</div>'

改为:

comment_html += '<div class="user_name fl">' + comment.user + '</div>'

回复评论报错:

"POST /news/news_comment HTTP/1.1" 404

在class="comment_reply"调用js,其ajax请求

$.ajax({

                url: "/news/comment_like",

修改为:

url: "/comment_like",

详情页面无法搜索:

原本Home最下方处js

<script>

    $(document).ready(function () {

        $("img.lazy").lazyload({

            effect: "fadeIn"

        });

        $("#do_search").click(function () {

            var key = $("#key_music").val();

            location.href = "{ { url_for('home.search')}}?key=" + key

        });

        $("#do_search").keydown(function () {

            var key = $("#key_music").val();

            location.href = "{ { url_for('home.search')}}?key=" + key

        });

    });

 

 

</script>

通过注释文件引入,可以定位到导致问题产生的文件是news/js/jquery-1.12.4.min.js

注释之后完全没有影响所需几个功能,所以就不用了

猜你喜欢

转载自blog.csdn.net/lagoon_lala/article/details/105304461