在线问答系统--页面功能开发

页面功能开发

之前有说过,该在线问答系统分为两种用户功能,一个是注册用户,一个游客
注册用户:回答,发布问题,评论,编辑我的.
游客:浏览页面功能
今天就让我们先来实现游客所能浏览的页面,问题列表页面和问题详情页面

1. 问题列表页面功能开发(分页功能未完成)

1.1 使用模板语法将详情页改造

这个我们之前已经改造了的

1.2 将需要显示的问题列表信息从数据库种取出

qa-online/templates/qa/view.py

@qa.route('/follow')
def follow():
    """ 关注 """
    qa_list = Question.query.filter_by(is_valid=True).all()# 从question表中去有效数据 
# 不要忘记在导入:from model import Question
    return render_template('follow.html',qa_list=qa_list)# 将取出的问题列表传入`follow.html`页面中

1.3 页面显示

页面显示只显示主要功能开发
qa-online/templates/qa/templates/follow.html

<div class="qa-ls">
              {% for question in qa_list %}
            <div class="qa-item">
              <a href="{
     
     { url_for('qa.detail',q_id = question.id) }}" class="title">{
   
   { question.title }}</a>
              <div class="desc">
                  {% if question.img %}
                <div class="left">
                  <img src="{
     
     { question.img }}" alt="" srcset="">
                </div>
                  {% endif %}
                <div class="right">
                  <p class="show-desc">{
   
   { question.desc |d("暂无描述",True)}}...
                    <span class="more">阅读原文<i class="glyphicon glyphicon-menu-down"></i></span>
                  </p>
                  <p class="show-all hidden">
                   {% autoescape false %}
                      {
   
   { question.content }}
                     {% endautoescape %}
                    <span class="more">收起<i class="glyphicon glyphicon-menu-up"></i></span>
                  </p>
                </div>
              </div>
              <div class="qa-footer">
                <div>
                  <button type="button" class="btn btn-info btn-sm">关  注</button>
                </div>
                <div class="txt-menu"><i class="glyphicon glyphicon-comment"></i> {
   
   { question.comment_count|d('0',True) }}条评论</div>
                <div class="txt-menu"><i class="glyphicon glyphicon-send"></i>分享</div>
                <div class="txt-menu"><i class="glyphicon glyphicon-flag"></i>举报</div>
                <div class="txt-menu btn-group">
                  <i class="glyphicon glyphicon-option-horizontal dropdown-toggle" data-toggle="dropdown"
                    aria-haspopup="true" aria-expanded="false"></i>
                  <ul class="dropdown-menu">
                    <li><a href="#">不感兴趣</a></li>
                  </ul>
                </div>
              </div>
            </div>
              {% endfor %}
          </div>
  1. 关闭自动转义
    自动转义是指自动对特殊字符进行转义。特殊字符是指 HTML ( 或 XML 和 XHTML )中的 & 、 > 、 < 、 " 和 ’ 。因为这些特殊字 符代表了特殊的意思,所以如果要在文本中使用它们就必须把它们替换为“实 体”。如果不转义,那么用户就无法使用这些字符,而且还会带来安全问题。
  • 有三种方法可以控制自动转义

    • 在 Python 代码中,可以在把 HTML 字符串传递给模板之前,用 Markup 对象封装。一般情况下推荐使用这个方法。
    • 在模板中,使用 |safe 过滤器显式把一个字符串标记为安全的 HTML
        {
             
             {question.content|safe}}
      
    • 临时关闭整个系统的自动转义。
      在模板中关闭自动转义系统可以使用 {% autoescape %} 块:
     {% autoescape false %}
        {
         
         { question.content }}
     {% endautoescape %}                   
    
  1. 评论数量显示
 <div class="txt-menu"><i class="glyphicon glyphicon-comment"></i> {
   
   { question.comment_count|d('0',True) }}条评论</div>

在上述代码中涉及了question.comment_count,那个它是怎么来的呢?不要着急,请看下面

qa-online/model.py

class Question(db.Model):
    """ 问题 """
    __tablename__ = 'qa_question'
    id = db.Column(db.Integer, primary_key=True)  # 主键
    # 问题标题
    title = db.Column(db.String(128), nullable=False)
    # 问题描述
    desc = db.Column(db.String(256))
    # 问题图片
    img = db.Column(db.String(256))
    # 问题详情
    content = db.Column(db.Text, nullable=False)
    # 浏览人数
    view_count = db.Column(db.Integer, default=0)
    # 逻辑删除
    is_valid = db.Column(db.Boolean, default=True)
    # 排序
    reorder = db.Column(db.Integer, default=0)
    # 创建时间
    created_at = db.Column(db.DateTime, default=datetime.now)
    # 最后修改的时间
    updated_at = db.Column(db.DateTime, default=datetime.now, onupdate=datetime.now)
    # 关联用户
    user_id = db.Column(db.Integer, db.ForeignKey('accounts_user.id'))
    # 建立与用户的一对多属性,user.question_list
    user = db.relationship('User', backref=db.backref('question_list', lazy='dynamic'))

    @property
    def comment_count(self):
        """ 评论数量 """
        return self.question_comment_list.filter_by(is_valid=True).count()
class AnswerComment(db.Model):
    """ 回答的评论 """
    __tablename__ = 'qa_answer_comment'
    id = db.Column(db.Integer, primary_key=True)  # 主键
    # 评论内容
    content = db.Column(db.String(512), nullable=False)
    # 赞同人数
    love_count = db.Column(db.Integer, default=0)
    # 评论是否公开
    is_public = db.Column(db.Boolean, default=True)
    # 逻辑删除
    is_valid = db.Column(db.Boolean, default=True)
    # 创建时间
    created_at = db.Column(db.DateTime, default=datetime.now)
    # 最后修改的时间
    updated_at = db.Column(db.DateTime, default=datetime.now, onupdate=datetime.now)
    # 回复ID
    reply_id = db.Column(db.Integer, db.ForeignKey('qa_answer_comment.id'), nullable=True)
    # 关联用户
    user_id = db.Column(db.Integer, db.ForeignKey('accounts_user.id'))
    # 关联答案
    answer_id = db.Column(db.Integer, db.ForeignKey('qa_answer.id'))
    # 关联问题
    q_id = db.Column(db.Integer, db.ForeignKey('qa_question.id'))

    # 建立与用户的一对多属性
    user = db.relationship('User', backref=db.backref('answer_comment_list', lazy='dynamic'))
    # 建立与答案的一对多属性
    answer = db.relationship('Answer', backref=db.backref('answer_comment_list', lazy='dynamic'))
    # 建立与问题的一对多属性
    question = db.relationship('Question', backref=db.backref('question_comment_list', lazy='dynamic'))

我们需要在ORM模型model.py中填写comment_count

  def comment_count(self):
      return self.question_comment_list.filter_by(is_valid_.count()# 其中`question_comment_list`是我们之前建立数据表是绑定外键关系,

2. 问题详情页面功能开发

问题详情页面比问题列表页面稍微复杂一点点.这里我们就进行简写
和问题列表页面一样,我们的第一步是将详情页面使用模板语法进行改造,这一步我们是已经完成了的

2.1 将需要展示评论,关注的信息从数据库中取出

如果我们需要查看一个问题的详情,首先的知道是那个问题,所以我们写问题列表页面是有这样一个操作

 <a href="{
     
     { url_for('qa.detail',q_id = question.id) }}" class="title">{
   
   { question.title }}</a>

所以
qa-online/templates/qa/view.py

@qa.route('/detail/<int:q_id>')
def detail(q_id):
    """ 问题详情 """
    # 1. 查询问题信息
    qa_info = Question.query.get(q_id)# 根据主键取出问题信息
    if not qa_info.is_valid:
        abort(404)# 不要忘记导入abort :from falsk import abort
    return render_template('detail.html',question=qa_info)
```python
qa-online/templates/qa/templates/detail.html
```html
  <h3>{
    
    {
    
     question.title }}</h3>
                <div class="show-desc ">
                    {
    
    {
    
     question.desc|d('暂无', True) }}<span class="btn-show-more">显示全部<i class="glyphicon glyphicon-menu-down"></i></span>
                </div>
                <div class="show-all  hidden">
                    {
    
    % autoescape false %}
                    {
    
    {
    
     question.content|safe }}
                    {
    
    % endautoescape %}
                    <span class="btn-show-less">收起<i class="glyphicon glyphicon-menu-up"></i></span>
                </div>
                <!-- 菜单栏 -->
                <div class="qa-footer">
                    <div>
                        <button type="button" class="btn btn-info btn-sm">关 注</button>
                    </div>
                    <div>
                        <button type="button" class="btn btn-info btn-sm" data-toggle="modal" data-target="#addComment">
                            写回答
                        </button>
                    </div>
                    <div class="txt-menu"><i class="glyphicon glyphicon-comment"></i> {
    
    {
    
     question.comment_count|d('0',True) }}条评论
                    </div>
                    <div class="txt-menu"><i class="glyphicon glyphicon-send"></i>分享</div>
                    <div class="txt-menu"><i class="glyphicon glyphicon-flag"></i>举报</div>
                    <div class="txt-menu"></div>
                    <div class="txt-menu btn-group">
                        <i class="glyphicon glyphicon-option-horizontal dropdown-toggle" data-toggle="dropdown"
                           aria-haspopup="true" aria-expanded="false"></i>
                        <ul class="dropdown-menu">
                            <li><a href="#">不感兴趣</a></li>
                        </ul>
                    </div>
                </div>
                <!-- //菜单栏 -->
            </div>
            <div class="col-md-3">
                <div class="que-stats-box">
                    <!-- 关注者 -->
                    <div>
                        <span>关注者</span>
                        <strong>{
    
    {
    
     question.follow_count|number_split }}</strong>
                    </div>
                    <!-- //关注者 -->
                    <!-- 被浏览 -->
                    <div>
                        <span>被浏览</span>
                        <strong>{
    
    {
    
     question.view_count|number_split }}</strong>
                    </div>
                    <!-- //被浏览 -->
                </div>
            </div>
  1. 关注者数量
    关注者数量,我们可以qa_question_follow中来取得,因为它和question表有进行外键管理和反向,
# 关联问题
    q_id = db.Column(db.Integer, db.ForeignKey('qa_question.id'))
 # 建立与问题的一对多属性
    question = db.relationship('Question', backref=db.backref('question_follow_list', lazy='dynamic'))

所以我们可以通过question_follow_list来进行操作,在 question表下写入:

@property
    def follow_count(self):
        """ 关注数量 """
        return self.question_follow_list.filter_by(is_valid=True).count()
  1. 关注数量和浏览次数的数字格式化
    其中,我们的关注数量和浏览次数在页面显示情况应该是:比如:1234566===>1,234,566

    所以在这里我们说使用python中str.format()格式数字来进行操作
print("{:,2f}".format(3.14159265))# 3.14 ==> 保留小数点两位

数字格式化

通过上面str.format可知道如何来进行数字格式化

  <span>被浏览</span>
   <strong>{
   
   { "{:,}".format(question.view_count) }}</strong>

为了方便操作,我们将其封装到工具类中utils中的filter.py
qa-online/utils/filter.py

def number_split(num):
    """
    数字格式化
    12345678 => 12,345,678
    :param num: 需要格式化的数字
    :return: 格式化后的字符串
    """
    return '{:,}'.format(int(num))

但是,光这样还不行,应该在我们的detail.html中无法知道number_split()这个方法是怎么来的?
所以我们需要在app.py中进行注册过滤器

from utils.filter import number_split
# 注册
app.jinja_env.filters['number_split']=number_split

所以这样,我们就可以在detail.html页面中写

<!-- 关注者 -->
                    <div>
                        <span>关注者</span>
                        <strong>{
   
   { question.follow_count|number_split }}</strong>
                    </div>
                    <!-- //关注者 -->
                    <!-- 被浏览 -->
                    <div>
                        <span>被浏览</span>
                        <strong>{
   
   { question.view_count|number_split }}</strong>
                    </div>
                    <!-- //被浏览 -->


而下一部分:主要是显示该问题的会带,但是在这里我们只显示第一条问答和第一条回答的评论内容.

2.2 取出第一条回答内容并展示剩余多少回答

  1. 首先我们得从输出库中取出该问题的第一条回答.
    view.py
answer =qa_info.answer_list.filter_by(is_valid=True).first()# 
return render_template('detail.html',question=qa_info,answer=answer)

detail.html
2. 展示剩余多少条回答

 <a class="link-more" href="#">查看全部 {
   
   { question.answer_count|number_split }}个回答</a>
  • answer_count
@property
def answer_count(self):
  return self.answer_list.filter_by(is_valid=True).count()
  1. 第一条回答显示
    detail.html
<div class="answer-content box-wrap">
                    <div class="user-info">
                        <div class="avater">
                            {% if answer.user.avatar %}
                                <img src="{
     
     { answer.user.avatar }}" alt="用户头像">
                            {% else %}
                                <img src="/assets/home/qa/user_head.jpg" alt="用户头像">
                            {% endif %}
                        </div>
                        <div class="desc">
                            <h4>{
   
   { answer.user.nickname }}</h4>
                            <p>《互联网营销人实战手记》新书上市; 公众号:舒大克。</p>
                        </div>
                    </div>
                    <div class="answer-stats">5,550 人赞同了该回答</div>
                    <div class="answer-txt">
                        {% autoescape false %}
                        {
   
   { answer.content|safe }}
                        {% endautoescape %}
                    </div>
                    <div class="answer-time">发布于{
   
   { answer.updated_at|dt_format_show}}</div>
                    <!-- 底部菜单 -->
                    <div class="qa-footer">
                        <div>
                            <button type="button" class="btn btn-info btn-sm">
                                <i class="glyphicon glyphicon-thumbs-up"></i> 赞同 1780
                            </button>
                            <button type="button" class="btn btn-info btn-sm"><i
                                    class="glyphicon glyphicon-thumbs-down"></i></button>
                        </div>
                        <div class="txt-menu"><i
                                class="glyphicon glyphicon-comment"></i> {
   
   { answer.answer_comment_count |d('0',True)}}条评论
                        </div>
                        <div class="txt-menu"><i class="glyphicon glyphicon-send"></i>分享</div>
                        <div class="txt-menu"><i class="glyphicon glyphicon-heart"></i>收藏</div>
                        <div class="txt-menu"><i class="glyphicon glyphicon-flag"></i>举报</div>
                        <div class="txt-menu"></div>
                        <div class="txt-menu btn-group">
                            <i class="glyphicon glyphicon-option-horizontal dropdown-toggle" data-toggle="dropdown"
                               aria-haspopup="true" aria-expanded="false"></i>
                            <ul class="dropdown-menu">
                                <li><a href="#">不感兴趣</a></li>
                            </ul>
                        </div>
                    </div>
  • 用户头像,昵称
    为什么我们取到到时第一条回答的内容,怎么去显示用户头像和昵称等用户的信息呢?
    这里我也不知道怎么来解释,大概就是因为我们Answer表它有绑定user.id的外键,直接上代码吧
 # 关联用户
    user_id = db.Column(db.Integer, db.ForeignKey('accounts_user.id'))
 # 建立与用户的一对多属性
    user = db.relationship('User', backref=db.backref('answer_list', lazy='dynamic'))# 因为backref,所以我们在answer表是有了user这个键,通过它在去访问`user`表中的信息.

比如

<div class="user-info">
                        <div class="avater">
                            {% if answer.user.avatar %}
                                <img src="{
   
   { answer.user.avatar }}" alt="用户头像">
                            {% else %}
                                <img src="/assets/home/qa/user_head.jpg" alt="用户头像">
                            {% endif %}
                        </div>
                        <div class="desc">
                            <h4>{
   
   { answer.user.nickname }}</h4>
  • 时间格式化
    这里的时间格式化和我们上面讲的不同,它的具体显是:几分钟前,1小时前

这里我们使用timeago

# 1. 下载timeago
pip install timegao
# 2. 使用,导入
import timeago # timeago的用法这里不详细讲了哈

我们讲格式化的时间的函数和之前一样都卸载utils工具类中的filter.pyt

def dt_format_show(dt):
    """
    日期和时间,格式化显示
    3分钟前
    1小时前
    :param dt: datetime 时间
    """
    now = datetime.now()
    return timeago.format(dt, now, 'zh_CN')

同样的,我们需要在app.py中进行注册

from utils.filter import dt_format_show
# 注册过滤器

app.jinja_env.filters['dt_format_show'] = dt_format_show

3. 页面展示

  1. 问题列表页面

  2. 问题详情页面

猜你喜欢

转载自blog.csdn.net/AAIT11/article/details/119952178