這裡主要以後端為主,前端的代碼不做展示,如果需要代碼可以評論或者私信
用戶注冊、登錄相關:
用郵箱進行注冊,並通過向郵箱發送驗證碼來進行判斷,一個郵箱只能注冊一個賬號
首頁相關:
用戶登錄後可以進行發布問題和回答,同時也提供搜索功能,在首頁展示所有問題
搜索:
評論:
blueprints:
項目藍圖包括問答的邏輯實現和用戶的邏輯時間
migrations:
項目數據庫遷移
static、templates:
前端相關,css、html文件等
主要是需要設計一個用戶表user表,一個郵箱對應驗證碼EmailCaptcha的表,一個問題question表,一個評論Answer表
利用flask中提供的SQLAlchemy不用我們自己手動寫SQL代碼,用面向對象的思維解決就好
(1)新建db對象
因為在很多文件中都需要用到db對象,所以用一個專門的文件etx.py儲存,並在app.py中進行初始化:
etx.py:
from flask_mail import Mail from flask_sqlalchemy import SQLAlchemy db = SQLAlchemy() mail = Mail()
app.py:
app = Flask(__name__) # 配置項 app.config.from_object(config) db.init_app(app) mail.init_app(app) migrate = Migrate(app, db) # 組裝藍圖 將book、course、user模塊都組裝在main.py中 app.register_blueprint(qa_bp) app.register_blueprint(user_bp)
(2)配置數據庫:
# 數據庫的配置變量 HOSTNAME = '127.0.0.1' PORT = '3306' DATABASE = 'flask' USERNAME = 'root' PASSWORD = '*****' DB_URI = 'mysql+pymysql://{}:{}@{}:{}/{}'.format(USERNAME, PASSWORD, HOSTNAME, PORT, DATABASE) SQLALCHEMY_DATABASE_URI= DB_URI # 關閉數據庫修改跟蹤操作[提高性能],可以設置為True,這樣可以跟蹤操作: SQLALCHEMY_TRACK_MODIFICATIONS=False # 開啟輸出底層執行的sql語句 SQLALCHEMY_ECHO = True
(3)設計models:
from exts import db class EmailCaptchaModel(db.Model): __tablename__="email_captcha" id=db.Column(db.Integer,primary_key=True,autoincrement=True) email=db.Column(db.String(100),nullable=True,unique=True) captcha=db.Column(db.String(10),nullable=False) create_time=db.Column(db.DateTime) class UserModel(db.Model): __tablename__ = "user" id = db.Column(db.Integer, primary_key=True, autoincrement=True) username = db.Column(db.String(200),nullable=False,unique=True) email = db.Column(db.String(100),nullable=False,unique=True) password = db.Column(db.String(200),nullable=False) join_time = db.Column(db.DateTime) class QuestionModel(db.Model): __tablename__ = "question" id = db.Column(db.Integer, primary_key=True, autoincrement=True) title = db.Column(db.String(200), nullable=False) content = db.Column(db.Text,nullable=False) create_time = db.Column(db.DateTime) author_id = db.Column(db.Integer,db.ForeignKey("user.id")) author = db.relationship("UserModel",backref="questions") class AnswerModel(db.Model): __tablename__ = "answer" id = db.Column(db.Integer, primary_key=True, autoincrement=True) content = db.Column(db.Text,nullable=False) create_time = db.Column(db.DateTime) question_id = db.Column(db.Integer,db.ForeignKey("question.id")) author_id = db.Column(db.Integer, db.ForeignKey("user.id")) question = db.relationship("QuestionModel",backref=db.backref("answers",order_by=create_time.desc())) author = db.relationship("UserModel",backref="answers")
(4)生成數據庫
利用flask中提供的數據庫遷移功能可以在更新數據庫後直接遷移
在命令行進行輸入
step1:
flask db init
這條語句執行完後會生成上述文件中的migrate文件夾
step2:
flask db migrate
step3:
flask db upgrade
更新完成!
在注冊時點擊發送驗證碼後利用一個固定的郵箱給注冊的郵箱發送驗證碼信息,同時存儲到數據庫中驗證輸入的驗證碼是否與收到的驗證碼一致。
主要是利用flask_mail進行郵箱發送。
(1)郵箱設置
# 郵箱配置 # 項目中用的是QQ郵箱 MAIL_SERVER = "smtp.qq.com" MAIL_PORT = 465 MAIL_USE_TLS = False MAIL_USE_SSL = True MAIL_DEBUG = True MAIL_USERNAME = "[email protected]" MAIL_PASSWORD = "*****" MAIL_DEFAULT_SENDER = "[email protected]"
(2)得到驗證碼
可以在浏覽器中進行測試
@bp.route("/captcha", methods=['POST']) def get_captcha(): email = request.form.get("email") # 從letters集合中隨機取出4個生成驗證碼 letters集合是英文和數字的集合 letters = string.ascii_letters + string.digits captcha = "".join(random.sample(letters, 4)) if email: message = Message( subject="郵箱測試", recipients=[email], body=f"您的注冊驗證碼是:{captcha}" ) mail.send(message) # 存放到數據庫中:先通過email進行查詢,如果存在該email就直接更新captcha就行,如果不存在就添加一個記錄 captcha_model = EmailCaptchaModel.query.filter_by(email=email).first() if captcha_model: captcha_model.captcha = captcha # captcha_model.create_time=datetime.time.now() db.session.commit() else: captcha_model = EmailCaptchaModel(email=email, captcha=captcha) db.session.add(captcha_model) db.session.commit() print("captcha:", captcha) return jsonify({"code": 200, "message": "suceess"}) else: return jsonify({"code": 400, "message": "mail為空"})
(1)判斷注冊和登錄填寫的表單是否符合要求
可以直接利用flask中的wtforms進行格式限制:
新建一個forms.py:
import wtforms from wtforms.validators import length,email,EqualTo,InputRequired from models import EmailCaptchaModel,UserModel class LoginForm(wtforms.Form): email = wtforms.StringField(validators=[email()]) password = wtforms.StringField(validators=[length(min=6,max=20)]) class RegisterForm(wtforms.Form): username = wtforms.StringField(validators=[length(min=3,max=20,message="長度在3和20之間")]) email = wtforms.StringField(validators=[email()]) captcha = wtforms.StringField(validators=[length(min=4, max=4)]) password = wtforms.StringField(validators=[length(min=6,max=20,message="長度在6和20之間")]) password_confirm = wtforms.StringField(validators=[EqualTo("password")]) def validate_captcha(self,field): captcha = field.data email = self.email.data captcha_model = EmailCaptchaModel.query.filter_by(email=email).first() print(captcha_model.captcha) if not captcha_model or captcha_model.captcha.lower() != captcha.lower(): print("驗證碼錯誤") raise wtforms.ValidationError("郵箱驗證碼錯誤!") def validate_email(self,field): email = field.data user_model = UserModel.query.filter_by(email=email).first() if user_model: print("郵箱已存在") raise wtforms.ValidationError("郵箱已經存在!") class QuestionForm(wtforms.Form): title = wtforms.StringField(validators=[length(min=3, max=200)]) content = wtforms.StringField(validators=[length(min=5)]) class AnswerForm(wtforms.Form): content = wtforms.StringField(validators=[length(min=1)])
(2)注冊
注冊主要是判斷格式是否正確,並判斷驗證碼是否正確,如果正確後,新生成一個user對象插入到user表中。同時在存儲時進行密碼加密存儲
@bp.route('/register', methods=['GET', 'POST']) def register(): if request.method == 'GET': return render_template("register.html") else: form = RegisterForm(request.form) if form.validate(): print("驗證成功") username = form.username.data email = form.email.data password = form.password.data # 密碼加密 hash_password = generate_password_hash(password=password) captcha = form.captcha.data create_time = datetime.datetime.now() # 1.通過email查詢user表 如果存在就通知已存在該用戶 不存在就新建 user_model = UserModel.query.filter_by(email=email).first() if user_model: print("該郵箱已被注冊,請重新輸入") return redirect(url_for("user.register")) user = UserModel(username=username, email=email, password=hash_password, join_time=create_time) db.session.add(user) db.session.commit() return redirect(url_for("user.login")) else: print("注冊驗證失敗") return redirect(url_for("user.register"))
(3)登錄
先查詢是否存在這個用戶,如果存在進行密碼驗證
@bp.route('/login', methods=['GET', 'POST']) def login(): """登錄:guest1:123456 0.通過驗證 1.通過郵箱查找出user_model 2.如果存在就比較密碼是否正確 正確:登錄成功 不正確:密碼錯誤 3.不存在直接提示用戶不存在並返回到注冊頁面""" if request.method == 'GET': return render_template("login.html") else: form = LoginForm(request.form) if form.validate(): email = form.email.data password_input = form.password.data user_model = UserModel.query.filter_by(email=email).first() if user_model: if check_password_hash(user_model.password, password=password_input): print("登錄成功") session['user_id'] = user_model.id return redirect("/") else: print("密碼輸入錯誤") flash("密碼輸入錯誤") return redirect(url_for("user.login")) else: print("該用戶不存在,請注冊") flash("該用戶不存在,請注冊") return redirect(url_for("user.register")) else: print("請輸入正確格式的賬號或密碼") flash("請輸入正確格式的賬號或密碼") return redirect(url_for("user.login"))
(4)登出
刪除session即可
@bp.route('/logout') def logout(): session.clear() # 清除session就可 return redirect(url_for("user.login"))
用戶在未登錄時不能進行發布問答的功能,這裡可以利用一個裝飾器進行實現
"""裝飾器""" from flask import g, redirect, url_for from functools import wraps """如果沒有登錄就不能訪問,跳轉到登錄頁面 如果登錄了就正常邏輯處理""" def login_required(func): @wraps(func) def wrapper(*args, **kwargs): if hasattr(g, "user"): return func(*args, **kwargs) else: print("未登錄不能進行訪問") return redirect(url_for("user.login")) return wrapper
在需要進行訪問限制的方法上面加上
@bp.route('/public_question', methods=['GET', 'POST']) @login_required def public_question():
就可以實現訪問權限的設置啦!!!
問答功能:實際上就是生成一個QuestionModel,將符合要求的Question存儲到數據庫中
(1)QuestionModel:
class QuestionModel(db.Model): __tablename__ = "question" id = db.Column(db.Integer, primary_key=True, autoincrement=True) title = db.Column(db.String(200), nullable=False) content = db.Column(db.Text,nullable=False) create_time = db.Column(db.DateTime) author_id = db.Column(db.Integer,db.ForeignKey("user.id")) author = db.relationship("UserModel",backref="questions")
(2)表單驗證:
class QuestionForm(wtforms.Form): title = wtforms.StringField(validators=[length(min=3, max=200)]) content = wtforms.StringField(validators=[length(min=5)])
(3)問答功能
@bp.route('/public_question', methods=['GET', 'POST']) @login_required def public_question(): """發布問答""" if request.method == 'GET': return render_template("public_question.html") else: form = QuestionForm(request.form) if form.validate(): title = form.title.data content = form.content.data create_time = datetime.datetime.now() question = QuestionModel(title=title, content=content, author=g.user, create_time=create_time) db.session.add(question) db.session.commit() print("發布問答成功") return redirect(url_for("qa.index")) else: flash("格式不正確") return redirect(url_for("qa.public_question"))
(4)問題細節查看
@bp.route("/question/<int:question_id>") def question_detail(question_id): question = QuestionModel.query.get(question_id) return render_template("detail.html", question=question)
(5)問題搜索
在標題和內容中都可以進行查詢搜索,只有符合其一就輸出
@bp.route('/search') def search(): q = request.args.get("q") questions = QuestionModel.query.filter( or_(QuestionModel.title.contains(q), QuestionModel.content.contains(q))).order_by(db.text("create_time")) print("搜索結果", questions == None) if questions: return render_template("index.html", questions=questions) else: print("搜索結果為空") return "搜索結果為空"
在發布問題後可以進行評論,與問題一致的流程
(1)生成AnswerModel:
class AnswerModel(db.Model): __tablename__ = "answer" id = db.Column(db.Integer, primary_key=True, autoincrement=True) content = db.Column(db.Text,nullable=False) create_time = db.Column(db.DateTime) question_id = db.Column(db.Integer,db.ForeignKey("question.id")) author_id = db.Column(db.Integer, db.ForeignKey("user.id")) question = db.relationship("QuestionModel",backref=db.backref("answers",order_by=create_time.desc())) author = db.relationship("UserModel",backref="answers")
(2)生成表單驗證:
class AnswerForm(wtforms.Form): content = wtforms.StringField(validators=[length(min=1)])
(3)進行評論
@bp.route("/answer/<int:question_id>", methods=['POST']) @login_required def answer(question_id): form = AnswerForm(request.form) if form.validate(): content = form.content.data create_time = datetime.datetime.now() answer_model = AnswerModel(content=content, author=g.user, question_id=question_id, create_time=create_time) db.session.add(answer_model) db.session.commit() return redirect(url_for("qa.question_detail", question_id=question_id)) else: flash("表單驗證失敗!") return redirect(url_for("qa.question_detail", question_id=question_id))
就是簡單的一個小項目練練手,新手的可以看這個直接入門,但是項目也需要進行完善啦,比如注冊也可以設置頭像之類的,這樣在首頁就可以顯示自己的頭像之類的。