Online question and answer system -- page function development

Posted by gillypogi on Tue, 21 Dec 2021 20:46:49 +0100

Page function development

It has been said before that the online Q & a system is divided into two user functions, one is a registered user and the other is a tourist
Registered users: answer, post questions, comment, edit my
Visitor: browse page function
Today, let's first realize the pages that visitors can browse, the question list page and the question details page

1. Problem list page function development (paging function is not completed)

1.1 transform the detail page using template syntax

We've transformed this before

1.2 extract the problem list information to be displayed from the database

qa-online/templates/qa/view.py

@qa.route('/follow')
def follow():
    """ follow """
    qa_list = Question.query.filter_by(is_valid=True).all()# Remove valid data from the question table 
# Don't forget to import: from model import Question
    return render_template('follow.html',qa_list=qa_list)# Transfer the extracted question list to ` follow HTML page

1.3 page display

The page display only shows the main function development
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("No description",True)}}...
                    <span class="more">Read the original text<i class="glyphicon glyphicon-menu-down"></i></span>
                  </p>
                  <p class="show-all hidden">
                   {% autoescape false %}
                      {{ question.content }}
                     {% endautoescape %}
                    <span class="more">Put away<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">Attention</button>
                </div>
                <div class="txt-menu"><i class="glyphicon glyphicon-comment"></i> {{ question.comment_count|d('0',True) }}Comments</div>
                <div class="txt-menu"><i class="glyphicon glyphicon-send"></i>share</div>
                <div class="txt-menu"><i class="glyphicon glyphicon-flag"></i>report</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="#"> not interested</a></li>
                  </ul>
                </div>
              </div>
            </div>
              {% endfor %}
          </div>
  1. Turn off auto escape
    Automatic escape refers to the automatic escape of special characters. Special characters refer to &, >, <, 'and' in HTML (or XML and XHTML). Because these special characters represent special meaning, if you want to use them in text, you must replace them with "entities". If you do not escape, users will not be able to use these characters, and there will be security problems.
  • There are three ways to control automatic escape

    • In Python code, you can wrap the HTML string with a Markup object before passing it to the template. This method is generally recommended.
    • In the template, a string is explicitly marked as safe HTML using the | safe filter
        {{question.content|safe}}
      
    • Temporarily turn off the automatic escape of the whole system.
      To turn off the auto escape system in a template, you can use the {% autoescape%} block:
     {% autoescape false %}
        {{ question.content }}
     {% endautoescape %}                   
    
  1. Comment number display
 <div class="txt-menu"><i class="glyphicon glyphicon-comment"></i> {{ question.comment_count|d('0',True) }}Comments</div>

Question. Is involved in the above code comment_ Count, how did it come from? Don't worry, please look below

qa-online/model.py

class Question(db.Model):
    """ problem """
    __tablename__ = 'qa_question'
    id = db.Column(db.Integer, primary_key=True)  # Primary key
    # Question title
    title = db.Column(db.String(128), nullable=False)
    # Problem description
    desc = db.Column(db.String(256))
    # Problem picture
    img = db.Column(db.String(256))
    # Question details
    content = db.Column(db.Text, nullable=False)
    # Number of visitors
    view_count = db.Column(db.Integer, default=0)
    # Logical deletion
    is_valid = db.Column(db.Boolean, default=True)
    # sort
    reorder = db.Column(db.Integer, default=0)
    # Creation time
    created_at = db.Column(db.DateTime, default=datetime.now)
    # Last modified time
    updated_at = db.Column(db.DateTime, default=datetime.now, onupdate=datetime.now)
    # Associated user
    user_id = db.Column(db.Integer, db.ForeignKey('accounts_user.id'))
    # Create a one to many attribute with the user, user question_ list
    user = db.relationship('User', backref=db.backref('question_list', lazy='dynamic'))

    @property
    def comment_count(self):
        """ Number of comments """
        return self.question_comment_list.filter_by(is_valid=True).count()
class AnswerComment(db.Model):
    """ Comments answered """
    __tablename__ = 'qa_answer_comment'
    id = db.Column(db.Integer, primary_key=True)  # Primary key
    # Comment content
    content = db.Column(db.String(512), nullable=False)
    # Number of endorsements
    love_count = db.Column(db.Integer, default=0)
    # Is the comment public
    is_public = db.Column(db.Boolean, default=True)
    # Logical deletion
    is_valid = db.Column(db.Boolean, default=True)
    # Creation time
    created_at = db.Column(db.DateTime, default=datetime.now)
    # Last modified time
    updated_at = db.Column(db.DateTime, default=datetime.now, onupdate=datetime.now)
    # Reply ID
    reply_id = db.Column(db.Integer, db.ForeignKey('qa_answer_comment.id'), nullable=True)
    # Associated user
    user_id = db.Column(db.Integer, db.ForeignKey('accounts_user.id'))
    # Relevance answer
    answer_id = db.Column(db.Integer, db.ForeignKey('qa_answer.id'))
    # Correlation problem
    q_id = db.Column(db.Integer, db.ForeignKey('qa_question.id'))

    # Establish one to many attributes with users
    user = db.relationship('User', backref=db.backref('answer_comment_list', lazy='dynamic'))
    # Establish one to many attributes with answers
    answer = db.relationship('Answer', backref=db.backref('answer_comment_list', lazy='dynamic'))
    # Establish one to many attributes of the problem
    question = db.relationship('Question', backref=db.backref('question_comment_list', lazy='dynamic'))

We need the ORM model PY_ count

  def comment_count(self):
      return self.question_comment_list.filter_by(is_valid_.count()# Where ` question_comment_list 'is the data table we created before, which is a binding foreign key relationship,

2. Function development of problem details page

The question details page is slightly more complex than the question list page Let's abbreviate it here
Like the question list page, our first step is to transform the details page using template syntax. We have completed this step

2.1 take the comments that need to be displayed and the information concerned from the database

If we need to check the details of a problem, we first know the problem, so we have such an operation on the problem list page

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

therefore
qa-online/templates/qa/view.py

@qa.route('/detail/<int:q_id>')
def detail(q_id):
    """ Question details """
    # 1. Query problem information
    qa_info = Question.query.get(q_id)# Retrieve the problem information according to the primary key
    if not qa_info.is_valid:
        abort(404)# Don't forget to import abort: from false 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('Not yet', True) }}...
                    <span class="btn-show-more">Show all<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">Put away<i class="glyphicon glyphicon-menu-up"></i></span>
                </div>
                <!-- menu bar -->
                <div class="qa-footer">
                    <div>
                        <button type="button" class="btn btn-info btn-sm">Attention</button>
                    </div>
                    <div>
                        <button type="button" class="btn btn-info btn-sm" data-toggle="modal" data-target="#addComment">
                            Write answers
                        </button>
                    </div>
                    <div class="txt-menu"><i class="glyphicon glyphicon-comment"></i> {{ question.comment_count|d('0',True) }}Comments
                    </div>
                    <div class="txt-menu"><i class="glyphicon glyphicon-send"></i>share</div>
                    <div class="txt-menu"><i class="glyphicon glyphicon-flag"></i>report</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="#"> not interested</a></li>
                        </ul>
                    </div>
                </div>
                <!-- //Menu bar -- >
            </div>
            <div class="col-md-3">
                <div class="que-stats-box">
                    <!-- Followers -->
                    <div>
                        <span>Followers</span>
                        <strong>{{ question.follow_count|number_split }}</strong>
                    </div>
                    <!-- //Followers -- >
                    <!-- Be browsed -->
                    <div>
                        <span>Be browsed</span>
                        <strong>{{ question.view_count|number_split }}</strong>
                    </div>
                    <!-- //Browsed -- >
                </div>
            </div>
  1. Number of followers
    Number of followers, we can qa_question_follow, because it is used for foreign key management and reverse with the question table,
# Correlation problem
    q_id = db.Column(db.Integer, db.ForeignKey('qa_question.id'))
 # Establish one to many attributes of the problem
    question = db.relationship('Question', backref=db.backref('question_follow_list', lazy='dynamic'))

So we can pass the question_follow_list, and write the following in the question table:

@property
    def follow_count(self):
        """ Number of concerns """
        return self.question_follow_list.filter_by(is_valid=True).count()
  1. Focus on number formatting for number and number of views
    Among them, the number of our attention and the number of views displayed on the page should be: for example: 1234566 = = > 1234566

    So here we say that we use the str.format() format number in python to operate
print("{:,2f}".format(3.14159265))# 3.14 = = > two decimal places are reserved

You can know how to format numbers through str.format above

  <span>Be browsed</span>
   <strong>{{ "{:,}".format(question.view_count) }}</strong>

For ease of operation, we encapsulate it into the tool class, filter. In utils py
qa-online/utils/filter.py

def number_split(num):
    """
    Number formatting
    12345678 => 12,345,678
    :param num: Number to format
    :return: Formatted string
    """
    return '{:,}'.format(int(num))

However, this alone is not enough. It should be in our detail Number cannot be known in HTML_ How did the split () method come from?
So we need to be on the app PY

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

So in this way, we can in detail Write in HTML page

<!-- Followers -->
                    <div>
                        <span>Followers</span>
                        <strong>{{ question.follow_count|number_split }}</strong>
                    </div>
                    <!-- //Followers -- >
                    <!-- Be browsed -->
                    <div>
                        <span>Be browsed</span>
                        <strong>{{ question.view_count|number_split }}</strong>
                    </div>
                    <!-- //Browsed -- >


The next part mainly shows the meeting band of the question, but here we only show the first question and answer and the comments of the first answer

2.2 take out the first answer and show how many answers are left

  1. First, we have to take the first answer to the question from the output library
    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. Show how many answers remain

 <a class="link-more" href="#"> view all {{question.answer_count|number_split}} answers</a>
  • answer_count
@property
def answer_count(self):
  return self.answer_list.filter_by(is_valid=True).count()
  1. The first answer is displayed
    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="User Avatar">
                            {% else %}
                                <img src="/assets/home/qa/user_head.jpg" alt="User Avatar">
                            {% endif %}
                        </div>
                        <div class="desc">
                            <h4>{{ answer.user.nickname }}</h4>
                            <p><The new book "notes on actual combat of Internet marketers" is on the market; The official account: Shu Dake.</p>
                        </div>
                    </div>
                    <div class="answer-stats">5,550 People agreed with the answer</div>
                    <div class="answer-txt">
                        {% autoescape false %}
                        {{ answer.content|safe }}
                        {% endautoescape %}
                    </div>
                    <div class="answer-time">Published in{{ answer.updated_at|dt_format_show}}</div>
                    <!-- Bottom menu -->
                    <div class="qa-footer">
                        <div>
                            <button type="button" class="btn btn-info btn-sm">
                                <i class="glyphicon glyphicon-thumbs-up"></i> Agree 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)}}Comments
                        </div>
                        <div class="txt-menu"><i class="glyphicon glyphicon-send"></i>share</div>
                        <div class="txt-menu"><i class="glyphicon glyphicon-heart"></i>Collection</div>
                        <div class="txt-menu"><i class="glyphicon glyphicon-flag"></i>report</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="#"> not interested</a></li>
                            </ul>
                        </div>
                    </div>
  • User avatar, nickname
    Why do we get the content of the first answer and how to display user information such as user avatar and nickname?
    I don't know how to explain here, probably because our Answer table has a bound user ID foreign key, directly on the code
 # Associated user
    user_id = db.Column(db.Integer, db.ForeignKey('accounts_user.id'))
 # Establish one to many attributes with users
    user = db.relationship('User', backref=db.backref('answer_list', lazy='dynamic'))# Because of backref, we have the user key in the answer table to access the information in the 'user' table

such as

<div class="user-info">
                        <div class="avater">
                            {% if answer.user.avatar %}
                                <img src="{{ answer.user.avatar }}" alt="User Avatar">
                            {% else %}
                                <img src="/assets/home/qa/user_head.jpg" alt="User Avatar">
                            {% endif %}
                        </div>
                        <div class="desc">
                            <h4>{{ answer.user.nickname }}</h4>
  • Time formatting
    The time format here is different from what we said above. Its specific meaning is: a few minutes ago, an hour ago

Here we use timeago

# 1. Download timeago
pip install timegao
# 2. Use, import
import timeago # The usage of timeago is not discussed in detail here

We will talk about the function of formatting time. As before, we uninstall the filter in the utils tool class pyt

def dt_format_show(dt):
    """
    Date and time, formatted display
    3 Minutes ago
    1 Hours ago
    :param dt: datetime time
    """
    now = datetime.now()
    return timeago.format(dt, now, 'zh_CN')

Similarly, we need to be in app Py

from utils.filter import dt_format_show
# Register filter

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

3. Page display

  1. Question list page

  2. Question details page

Topics: Python Flask