This is mainly the rear end , The front-end code is not shown , If you need code, you can comment or send a private letter
User registration 、 Login related :
Register with mailbox , And send the verification code to the mailbox to judge , A mailbox can only register one account
Home page related :
Users can post questions and answer after logging in , It also provides search function , Show all the questions on the front page
Search for :
Comment on :
blueprints:
The project blueprint includes the logical implementation of Q & A and the logical time of users
migrations:
Project database migration
static、templates:
Front-end correlation ,css、html Documents, etc.
The main thing is to design a user table user surface , A mailbox corresponds to a verification code EmailCaptcha Table of , A problem question surface , A comment Answer surface
utilize flask Provided in SQLAlchemy We don't have to write it ourselves SQL Code , Just use object-oriented thinking
(1) newly build db object
Because it is needed in many files db object , So use a special file etx.py Store , And in app.py Initialize in :
etx.py:
from flask_mail import Mail from flask_sqlalchemy import SQLAlchemy db = SQLAlchemy() mail = Mail()
app.py:
app = Flask(__name__) # Configuration item app.config.from_object(config) db.init_app(app) mail.init_app(app) migrate = Migrate(app, db) # Assembly blueprint take book、course、user Modules are assembled in main.py in app.register_blueprint(qa_bp) app.register_blueprint(user_bp)
(2) Configuration database :
# Configuration variables of the database 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 # Close database modification tracking operation [ Improve performance ], It can be set to True, This allows you to track operations : SQLALCHEMY_TRACK_MODIFICATIONS=False # Turn on the output of the underlying execution sql sentence SQLALCHEMY_ECHO = True
(3) Design 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) Generate database
utilize flask The database migration function provided in can be migrated directly after updating the database
Enter... On the command line
step1:
flask db init
After this statement is executed, the... In the above file will be generated migrate Folder
step2:
flask db migrate
step3:
flask db upgrade
Update complete !
Click send verification code during registration and send verification code information to the registered mailbox through a fixed mailbox , At the same time, it is stored in the database to verify whether the input verification code is consistent with the received verification code .
Mainly to make use of flask_mail Send by email .
(1) Mailbox settings
# Mailbox configuration # What is used in the project QQ mailbox 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) Get the verification code
You can test in the browser
@bp.route("/captcha", methods=['POST']) def get_captcha(): email = request.form.get("email") # from letters Random extraction from the set 4 Generated verification code letters A set is a collection of English and numbers letters = string.ascii_letters + string.digits captcha = "".join(random.sample(letters, 4)) if email: message = Message( subject=" Mailbox test ", recipients=[email], body=f" Your registration verification code is :{captcha}" ) mail.send(message) # Store in database : Through the first email The query , If the email Just update captcha Just go , If it doesn't exist, add a record 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 It's empty "})
(1) Determine whether the registration and login forms meet the requirements
Can be used directly flask Medium wtforms Format restrictions :
Create a new one 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=" The length is in 3 and 20 Between ")]) email = wtforms.StringField(validators=[email()]) captcha = wtforms.StringField(validators=[length(min=4, max=4)]) password = wtforms.StringField(validators=[length(min=6,max=20,message=" The length is in 6 and 20 Between ")]) 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(" Verification code error ") raise wtforms.ValidationError(" Mailbox verification code error !") def validate_email(self,field): email = field.data user_model = UserModel.query.filter_by(email=email).first() if user_model: print(" The mailbox already exists ") raise wtforms.ValidationError(" The mailbox already exists !") 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) register
Registration is mainly to judge whether the format is correct , And determine whether the verification code is correct , If correct , A new generation user Object inserted into user In the table . At the same time, the password is encrypted for storage
@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(" Verify success ") username = form.username.data email = form.email.data password = form.password.data # Password encryption hash_password = generate_password_hash(password=password) captcha = form.captcha.data create_time = datetime.datetime.now() # 1. adopt email Inquire about user surface If it exists, notify the user that it already exists New if it doesn't exist user_model = UserModel.query.filter_by(email=email).first() if user_model: print(" The mailbox has been registered , Please re-enter ") 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(" Registration verification failed ") return redirect(url_for("user.register"))
(3) Sign in
First, check whether this user exists , If yes, perform password verification
@bp.route('/login', methods=['GET', 'POST']) def login(): """ Sign in :guest1:123456 0. Pass the verification 1. Find out by email user_model 2. If it exists, compare whether the password is correct correct : Login successful Incorrect : Wrong password 3. Does not exist directly prompts the user that does not exist and returns to the registration page """ 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(" Login successful ") session['user_id'] = user_model.id return redirect("/") else: print(" Wrong password ") flash(" Wrong password ") return redirect(url_for("user.login")) else: print(" The user does not exist , Please register ") flash(" The user does not exist , Please register ") return redirect(url_for("user.register")) else: print(" Please enter the account or password in the correct format ") flash(" Please enter the account or password in the correct format ") return redirect(url_for("user.login"))
(4) Log out
Delete session that will do
@bp.route('/logout') def logout(): session.clear() # eliminate session Can be return redirect(url_for("user.login"))
Users cannot publish Q & a when they are not logged in , This can be achieved by using a decorator
""" Decorator """ from flask import g, redirect, url_for from functools import wraps """ If you are not logged in, you cannot access , Go to the login page If you are logged in, the normal logic processing """ def login_required(func): @wraps(func) def wrapper(*args, **kwargs): if hasattr(g, "user"): return func(*args, **kwargs) else: print(" Cannot access without logging in ") return redirect(url_for("user.login")) return wrapper
Add... To the method that requires access restriction
@bp.route('/public_question', methods=['GET', 'POST']) @login_required def public_question():
You can set the access permissions !!!
Q & a function : In fact, it is to generate a QuestionModel, Will meet the requirements of Question Store in database
(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) Form validation :
class QuestionForm(wtforms.Form): title = wtforms.StringField(validators=[length(min=3, max=200)]) content = wtforms.StringField(validators=[length(min=5)])
(3) Q & a function
@bp.route('/public_question', methods=['GET', 'POST']) @login_required def public_question(): """ Post Q & A """ 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(" Successful Q & a release ") return redirect(url_for("qa.index")) else: flash(" Incorrect format ") return redirect(url_for("qa.public_question"))
(4) Problem details view
@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) Problem search
You can query and search in both title and content , Only if one of them is met, it will be output
@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(" The search results ", questions == None) if questions: return render_template("index.html", questions=questions) else: print(" Search results are empty ") return " Search results are empty "
Comments can be made after posting questions , A process consistent with the problem
(1) Generate 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) Generate form validation :
class AnswerForm(wtforms.Form): content = wtforms.StringField(validators=[length(min=1)])
(3) Comment
@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(" Form validation failed !") return redirect(url_for("qa.question_detail", question_id=question_id))
It is a simple small project to practice hands , Novices can see this to get started directly , But the project also needs to be improved , For example, you can also set up avatars for registration , In this way, you can display your avatar on the home page .