期末作品检查

在这个学期里,我们学习了Python+Flask+MysqL的web建设技术过程,刚接触Python,首先学习Python的基本语法、后期学习了flask框架,css、js以及mysql基本的增删查改和优化。在这里写自己制作一个简单项目的学习过程和总结,将自己学习的知识归纳总结一下。

参考学习网站:https://dormousehole.readthedocs.io/en/latest/

http://www.oschina.net/translate/the-flask-mega-tutorial-part-i-hello-world

一、所用软件

  Python3.5+mysql5.7+pycharm2017.2

二、创建数据库

1、连接数据看

import os
SQLALCHEMY_DATABASE_URI = 'mysql+pymysql://root:@127.0.0.1:3306/aaa?charset=utf8'
SQLALCHEMY_TRACK_MODIFICATIONS = False

SECRET_KEY=os.urandom(24)

2、创建数据库

app = Flask(__name__)
app.config.from_object(config)
db=SQLAlchemy(app)

# db = SQLAlchemy(app)
# 创建用户模型
class User(db.Model):
    __tablename__ = 'user'
    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    username = db.Column(db.String(20), nullable=False)
    _password = db.Column(db.String(200), nullable=False)  #内部使用

    @property
    def password(self):#外部使用,取值
        return self._password

    @password.setter
    def password(self, row_password):
        self._password = generate_password_hash(row_password)

    def check_password(self, row_password):#外部使用,赋值
        result = check_password_hash(self._password, row_password)
        return result

class Question(db.Model):
    __tablename__ = 'question'
    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    question = db.Column(db.String(100), nullable=False)
    questionDetail = db.Column(db.Text, nullable=False)
    creat_time = db.Column(db.DateTime, default=datetime.now)
    author_id = db.Column(db.Integer, db.ForeignKey('user.id'))
    author = db.relationship('User', backref=db.backref('question'))

class Comment(db.Model):
    __tablename__ = 'comment'
    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    author_id = db.Column(db.Integer, db.ForeignKey('user.id'))
    question_id = db.Column(db.Integer, db.ForeignKey('question.id'))
    creat_time = db.Column(db.DateTime, default=datetime.now())
    detail = db.Column(db.Text, nullable=False)
    question = db.relationship('Question', backref=db.backref('comments',order_by=creat_time.desc))
    author = db.relationship('User', backref=db.backref('comments'))

db.create_all()

三、页面设计

1、登录、注册界面

  1. js文件: onclick函数return True时才提交表单,return False时不提交表单。
  2. html文件:
    1. <form>中设置 action和method="post"
    2. <input> 中设置 name
    3. onclick='return fnlogin()'
  3. 主py文件中:
    1. from flask import  request, redirect, url_for
    2. @app.route('/regist/', methods=['GET', 'POST’])
    3. 设置`SECRET_KEY`
    4. `session`:增加用户名`session['username']=`username

主要代码:

HTML:

<div id="login">
    <h1>Login</h1>
    <form action="{{ url_for("login") }}" method="post">
            <input class="one" id="name" name="username" type="text" placeholder="请输入用户名"><br>
            <input class="one"id="password" name="password" type="password" placeholder="请输入密码"><br>
        <div id="error_box"><br></div>
        <button class="but" onclick="return myLogin()" >登陆</button></form></div>




<div id="register" >
    <h1>Register</h1>
    <form action="{{ url_for("register") }}" method="post">
        <input class="one" id="name" name="username"type="text" placeholder="请输入用户名"><br>
        <input class="one"id="password" name="password"type="password" placeholder="请输入密码"><br>
        <input class="one"id="passwordagain" name="passwordagain"type="password" placeholder="请再次输入密码"><br>
        <div id="error_box"><br></div>
        <button class="but"onclick="return fnLogin()" >注册</button>
    </form>
</div>

css:

html{
    width: 100%;
    height: 100%;
    overflow: hidden;
}
body{
    width: 100%;
    height: 100%;
    font-family: 'Open Sans',sans-serif;
    margin: 0;
    background-color: #4A374A;
}
#login{
    position: absolute;
    top: 50%;
    left:50%;
    margin: -150px 0 0 -150px;
    width: 300px;
    height: 300px;
}
#login h1{
    color: #fff;
    text-shadow:0 0 10px;
    letter-spacing: 1px;
    text-align: center;
}
h1{
    font-size: 2em;
    margin: 0.67em 0;
}
.one{
    width: 278px;
    height: 18px;
    margin-bottom: 10px;
    outline: none;
    padding: 10px;
    font-size: 13px;
    color: #fff;
    text-shadow:1px 1px 1px;
    border-top: 1px solid #312E3D;
    border-left: 1px solid #312E3D;
    border-right: 1px solid #312E3D;
    border-bottom: 1px solid #56536A;
    border-radius: 4px;
    background-color: #2D2D3F;
}
.but{
    width: 300px;
    min-height: 20px;
    display: block;
    background-color: #4a77d4;
    border: 1px solid #3762bc;
    color: #fff;
    padding: 9px 14px;
    font-size: 15px;
    line-height: normal;
    border-radius: 5px;
    margin: 0;
}

js:

function fnLogin() {
        var uname = document.getElementById("name")
        var uError = document.getElementById("error_box")
        var upassword = document.getElementById("password")
        var upasswordagain = document.getElementById("passwordagain")
        var isError =true;
         if(uname.value.length<6 || uname.value.length >20){
        uError.innerHTML="用户名应为6到20字符";
        isError = false;
         return isError;
    }else if ((uname.value.charCodeAt(0)>=48) && (uname.value.charCodeAt(0)<=57)){
        uError.innerHTML="第一位只能是字母";
        isError = false;
        return isError;
    } else for (var i=0 ; i<uname.value.length;i++){
        if (uname.value.charCodeAt(i)<48 || (uname.value.charCodeAt(i)>57)&&(uname.value.charCodeAt(i)<97)|| uname.value.charCodeAt(i)>122){
            uError.innerHTML="用户名只能为数字或者字母";
           isError = false;
         return isError;
        }
    }
    if(upassword.value.length<6 || upassword.value.length>12){
        uError.innerHTML="密码应为6到20字符";
       isError = false;
         return isError;
    }
    if (upasswordagain.value!=upassword.value ) {
        uError.innerHTML = "重新输入你的密码";
       isError = false;
         return isError;
    }
    return isError;
    window.alert("注册成功")
}

py:

@app.route('/login/',methods=['GET','POST'])
def login():
    if request.method == "GET":
        return render_template("login.html")
    else:
        username = request.form.get("username")
        password1 = request.form.get("password")
        user = User.query.filter(User.username == username).first()
        if user:
            if user.check_password(password1):
                session['user'] = username
                session['userid'] = user.id
                sessionpermanent = True
                return redirect(url_for('homepage'))
            else:
                return 'password error'
        else:
            return 'username is not existed'


@app.route("/register/",methods=['GET','POST']) 
def register():
    if request.method == "GET":
        return render_template("register.html")
    else:
        username = request.form.get("username")
        password = request.form.get("password")
        user = User.query.filter(User.username == username).first()
        if user:
            return 'username existed.'
        else:
            user = User(username=username, password=password)
            db.session.add(user)
            db.session.commit()
            return redirect(url_for('login'))

2、父模板、首页设计

HTML:

<body id="myBody">
<header>
    <img id="aaa" src="{{ url_for('static',filename='image/logo.png') }}"height="50px"width="270px">
  <nav>
    <ul>
        <li><a href="{{ url_for("homepage") }}">首页</a></li>
        <li><a href="{{ url_for("question") }}" title="问答" target="_blank">问答</a></li>
        <li><a href="">透明片</a></li>
        <li><a href="">彩片</a></li>
        <li><a href="">护理液</a></li>
        <li><a href="http://www.bauschlombchina.com/history/">品牌历史</a></li>

    <form action="{{ url_for('search') }}" method="get">
         <li><input type="text"id="search" class="form-control" name="search"placeholder="请输入搜索内容" ></li>
         <li><button id="button1"type="submit">搜索</button></li>
    </form>

        {% if username %}
            <li><a href="{{ url_for('personal',user_id=session.get("userid"),tag=1) }}">{{ session.get('user') }}</a></li>
            <li><a href="{{ url_for("logout") }}">注销</a></li>
        {% else %}
        <li><a href="{{ url_for("login") }}">登录</a></li>
        <li><a href="{{ url_for("register") }}">注册</a></li>
        {% endif %}
        <li><img id="myOnOff" onclick="mySwitch()" src="https://www.runoob.com/images/pic_bulbon.gif"height="30px" width="40px"></li>>
    </ul>

  </nav>
</header>

{% block main %}
{% endblock %}


<div id="bottom">
    <a href="">联系我们</a>
    <a href="">加入我们</a>
    <a href="">帮助中心</a>
    <a href="">合作伙伴</a>
</div>
<div class="copyright">
      Copyright@ 2017 个人版权,版权所有  
 </div>

</body>
<div style="margin:80px">
    {% for a in question %}

        <div note-list style="margin:0 auto;width:1000px;height:auto;border-style:groove;">
            <ul class="list-group" style="list-style: none;">
                <li class="list-group-item,">
                    <span class="glyphicon glyphicon-leaf" aria-hidden="true"></span>
                    <a href="{{ url_for('detail',question_id=a.id) }}"> {{ a.question }}</a><br><br>

                    <a href="{{ url_for('personal',user_id=a.author.id,tag=1) }}">{{ a.author.username }}</a>
                    <span class="badge">{{ a.creat_time }}</span>
                    <p style="text-indent: 18px">{{ a.questionDetail }}</p>
                </li>
            </ul></div>

css:

nav{
    float: left;
    width:100%;
    height:40px;
    background-image:url(../image/blue.jpg);
    margin: 0 0 0 0;
    padding: 0 ;
}

nav ul {
    float:left;
    margin: 0px;
    padding: 0 0 0 0;
    width:100%;
    list-style: none;
}

nav ul li {
    display: inline;
}

nav ul li a {
    float: left;
    padding: 11px 20px;
    font-size: 14px;
    text-align: center;
    text-decoration: none;
    color: #ffffff;
    font-family: Tahoma;
    outline: none;
}

nav li a:hover {
    color: #2a5f00;
}
#search{
    float:right;
    margin:5px;

}
#button1{
    margin-left: 15px;
    float:right;
    margin:5px;
}


#bottom{
    position:fixed;
    bottom:30px;
    left:600px;
}

#bottom a{
    font-size: 12px;
    color: #000000;
    text-decoration: none;
        }

.copyright {
  position:fixed;
  color: #000000;
  bottom:10px;
  left:600px;
  font-size: 12px;
  text-decoration: none;
  transition: color 0.3s ease-out;
}

body{
    background:url("../image/picture.jpg") no-repeat;
    width:100%;
    height:1000px;
    overflow:auto;
    background-size: cover;
    background-repeat: repeat-y;
    font-family: 华文楷体;
    font-size: 20px;
    font-weight: bolder;
}

 py:

def homepage():
    context = {
        'question': Question.query.order_by('-creat_time').all() #按时间显示
    }
    return render_template("homepage.html",**context)

 3、登陆后更新导航条

  1. 用上下文处理器app_context_processor定义函数
    1. 获取session中保存的值
    2. 返回字典
  2. 在父模板中更新导航,插入登录状态判断代码。
    1. 注意用{% ... %}表示指令。
    2. {{ }}表示变量
  3. 完成注销功能。
    1. 清除session
    2. 跳转

HTML:

{% if username %}
            <li><a href="#"id="aaa">{{ username }}</a></li>
            <li><a href="{{ url_for("logout") }}">注销</a></li>
        {% else %}
        <li><a href="{{ url_for("login") }}">登录</a></li>
        <li><a href="{{ url_for("register") }}">注册</a></li>
        {% endif %}  

 py:

@app.context_processor
def mycontext():
    usern = session.get('user')
    if usern:
        return {'username':usern}
    else:
        return {}
 
@app.route('/logout/')
def logout():
    session.clear()
    return render_template("homepage.html")

4、发布功能、首页显示

  • 编写要求登录的装饰器

from functools import wraps

def loginFirst(func): #参数是函数

@wraps(func)

      def wrapper(*args, ** kwargs): #定义个函数将其返回

          #要求登录

          return func(*args, ** kwargs)

      return wrapper #返回一个函数

  • 应用装饰器,要求在发布前进行登录,登录后可发布。
@app.route('/question/',methods=['GET','POST'])
@loginFirst
def question():
  • 建立发布内容的对象关系映射。
class Question(db.Model):

  • 完成发布函数。

保存到数据库。

重定向到首页。

html:

<div style="margin:80px">
    {% for a in question %}

        <div note-list style="margin:0 auto;width:1000px;height:auto;border-style:groove;">
            <ul class="list-group" style="list-style: none;">
                <li class="list-group-item,">
                    <span class="glyphicon glyphicon-leaf" aria-hidden="true"></span>
                    <a href="{{ url_for('detail',question_id=a.id) }}"> {{ a.question }}</a><br><br>

                    <a href="{{ url_for('personal',user_id=a.author.id,tag=1) }}">{{ a.author.username }}</a>
                    <span class="badge">{{ a.creat_time }}</span>
                    <p style="text-indent: 18px">{{ a.questionDetail }}</p>
                </li>
            </ul></div>
<div class="box" >
    <h1 align="center"> 发布问答</h1>
       <form action="{{ url_for("question") }}" method="post">
        <div class="Q1">
            问题:<label for="question"></label>
            <textarea id="question" name="question" cols="50" rows="1"></textarea>
        </div>
        <div class="form-group">
            详情:
            <label for="questionDetail"></label>
            <textarea class="form-control" id="questionDetail" name="questionDetail" cols="50" rows="5" style="font-family: 宋体"></textarea>
        </div>
       <br>
        <div class="ja">
            <button onclick="return fnQuestion">发布问答</button>
        </div>

py:

def log(func): #参数是函数
    @wraps(func)
    def wrapper(*args, **kwargs): #定义个函数将其返回
        if session.get('user'):
            return func(*args, **kwargs)
        else:
            return redirect(url_for('login'))
    return wrapper #返回一个函数
 
@app.route('/question/', methods=['GET' , 'POST'])
@log
def question():
    if request.method == 'GET':
        return render_template('question.html')
    else:
        question = request.form.get('question')
        questionDetail = request.form.get('questionDetail')
        author_id = User.query.filter(User.username == session.get('user')).first().id
        question = Question(question = question, questionDetail = questionDetail, author_id = author_id)
        db.session.add(question)
        db.session.commit()
        return redirect(url_for('homepage'))

5、首页链接到问答详情与评论页

 主PY文件写视图函数,带id参数。 

@app.route('/detail/<question_id>')
def detail(question_id):
    quest=Question.query.filter(Question.id == question_id).first()
    return render_template("detail.html",ques=quest)

首页标题的标签做带参数的链接。
      {{ url_for('detail',question_id = foo.id) }}

在详情页将数据的显示在恰当的位置。

div>
                 <h2>{{ ques.question }}<br><small>{{ ques.author.username }} <span class="badge">{{ ques.creat_time }}</span></small></h2></div>
             问题详情:<p class="lead">{{ ques.questionDetail }}</p>
             <p class="location">评论</p>
 
        <hr>
        <form action="{{ url_for('question') }}" method="post">
            <div>
            <textarea class="form-control" id="new_comment" rows="6" placeholder="write your comment"style="height:200px;width:800px" ></textarea><br></div>
        <button type="submit" class="fabu"style="width:100px">发布</button>
        </form>
        <ul class="list-group" style="margin: 10px"></ul>
        </div>

6、完成评论功能

①.定义评论的视图函数
@app.route('/comment/',methods=['POST'])
def comment():
读取前端页面数据,保存到数据库中

@app.route('/comment/', methods=['POST'])
@log
def comment():
    comment = request.form.get("new_comment")
    question_id = request.form.get("question_id")
    author_id = User.query.filter(User.username == session.get('user')).first().id
    comm = Comment(author_id=author_id,question_id=question_id,detail=comment)
    db.session.add(comm)
    db.session.commit()
    context = {
        'comments': Comment.query.order_by('creat_time').all()
    }
    question = Question.query.filter(Question.id == question_id).first()
    return render_template('detail.html', ques=question, **context)

②.用<input type="hidden" 方法获取前端的"question_id" 

<input name="question_id" type="hidden" value="{{ ques.id }}"/>

③.显示评论次数

<h4>评论:({{ ques.comments|length }})</h4>

④.要求评论前登录

@log

<ul class="list-group">
            {% for b in comments %}
                <li class="list-group-item" >
                    <a href="#">{{ b.author.username }}</a> <span class="badge">{{ b.creat_time }}</span>
                    <p class="abstract">{{ b.detail }}</p>
                </li>
            {% endfor %}
        </ul>

7、个人中心界面

①新页面userbase.html,用<ul ><li role="presentation"> 实现标签页导航。

{% extends'index.html' %}
{% block title %}个人中心{% endblock %}
{% block head %}
    <style>
        .nav nav-pills li{
            list-style: none;
            float:left;
            margin: 10px;
        }
    </style>
{% endblock %}
{% block main %}

    <link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
<ul class="nav nav-pills">
    <br><br><br><br><br><br>
    
        <li role="presentation"><a href="{{ url_for('personal',user_id=user.id,tag=1) }}">Question</a></li>
        <li role="presentation"><a href="{{ url_for('personal',user_id=user.id,tag=2) }}">Comment</a></li>
        <li role="presentation"><a href="{{ url_for('personal',user_id=user.id,tag=3) }}">Info</a></li>
    </ul>


{% block user %}{% endblock %}
{% endblock %}

②question、comment、info界面

HTML:

<div class="col-md-8 column">
         <h3><span class="glyphicon glyphicon-user" aria-hidden="true"></span>{{username}} <small>个人问题<span class="badge"></span> </small></h3>
        <ul class="list-unstyled">
        {% for foo in comments %}
                        <li class="list-group-item" style="list-style: none;">

                            <p>文章标题:<a href="#">{{ foo.question.question}}</a></p>
                            <p>主要内容:{{ foo.question.questionDetail }}</p>
                            <img style="width: 30px" src="{{ url_for('static',filename='image/baby.jpg') }}" alt="64">
                            <small><a>{{ foo.author.username }}</a></small>
                            <span class="badge">{{foo.creat_time}}</span>

                        </li>
                    {% endfor %}
                </ul>
            </div>
 <div class="col-md-8 column" >
        <ul class="list-unstyled">

{#    <div class="col-md-8 column"style="margin:0 auto;width:900px;height:auto;border-style:groove;" >#}
     <h3><span class="glyphicon glyphicon-user" aria-hidden="true"></span>
    {{username}} <small>个人评论<span class="badge"></span> </small></h3>
    <ul class="list-unstyled">
          {% for foo in comments %}
            <li class="list-group-item"style="list-style: none;">
                <span class="glyphicon glyphicon-heart-empty" aria-hidden="true"></span>
                <img style="width: 30px" src="{{ url_for('static',filename='image/baby.jpg') }}" alt="64">
                <a href="#">{{foo.author.username }}</a>
                <span class="badge">{{foo.creat_time}}</span>
                <p style="">{{foo.detail}}</p>
            </li>
<div class="col-md-8 column" >

     <h3><span class="glyphicon glyphicon-user" aria-hidden="true"></span>
    {{username}} <small>个人信息<span class="badge"></span> </small></h3>
      <ul class="list-group" style="list-style: none;">
          <li class="list-group-item">用户:{{username}} <a href="{{ url_for('ModifyPassword') }}">  修改密码</a></li>
          <li class="list-group-item">评论数: {{ comments|length }}</li>
          <li class="list-group-item">文章篇:{{ questions|length }}</li>
      </ul>
</div>

py:

@app.route('/personal/<user_id><tag>')
@log
def personal(user_id,tag):
    user=User.query.filter(User.id==user_id).first()
    mycontext={
        'user':user,
        'questions': user.question,
        'comments': user.comments,
    }
    if tag =='1':
        return render_template('user1.html',**mycontext)
    elif tag =='2':
        return render_template('user2.html',**mycontext)
    else:
        return render_template('user3.html',**mycontext)

 

8、搜索

①获取搜索关键字
q = request.args.get('q’)

②条件查询
qu = Question.query.filter(Question.title.contains(q)).order_by('-creat_time’)

③加载查询结果:
return render_template('index.html', question=qu)

④组合条件查询from sqlalchemy import or_, and_ 

py:

@app.route('/search/')
def search():
    qu = request.args.get('search')
    ques = Question.query.filter(
        or_(
             Question.question.contains(qu),
             Question.questionDetail.contains(qu)
         )
    ).order_by('creat_time')
    return render_template('homepage.html',question = ques)

9、密码保护

①更新User对象,设置对内的_password

_password = db.Column(db.String(200), nullable=False)  #内部使用

②编写对外的password

from werkzeug.security import generate_password_hash, check_password_hash

③密码验证的方法:

    def check_password(self, row_password): #密码验证

     result = check_password_hash(self._password,row_password)

      return result

@property
    def password(self):#外部使用,取值
        return self._password

    @password.setter
    def password(self, row_password):
        self._password = generate_password_hash(row_password)

    def check_password(self, row_password):#外部使用,赋值
        result = check_password_hash(self._password, row_password)
        return result
 if user:
            if user.check_password(password1):

  以上,感谢老师一学期的指导,虽然我还有很多不足,但我会继续完善我的代码。

posted @ 2018-01-04 17:57  JaTae  阅读(213)  评论(0编辑  收藏  举报