当前位置: 首页 > news >正文

Flask学习笔记_异步论坛(四)

Flask学习笔记_异步论坛(四)

  • 1.配置和数据库链接
    • 1.exts.py里面实例化sqlalchemy数据库
    • 2.config.py配置app和数据库信息
    • 3.app.py导入exts和config并初始化到app上
  • 2.创建用户模型并映射到数据库
    • 1.models/auth.py创建用户模型
    • 2.app.py导入模型并用flask-migrate管理数据库
    • 3.命令行migrate三部曲将模型映射到数据库
  • 3.登录与注册页面的get请求
    • 1.首先写登录和注册的前端页面
    • 2.写它们的view蓝图并导入到__init__中
    • 3.蓝图注册到app
  • 4.邮箱验证功能
    • 1.邮箱验证
    • 2.使用celery异步发送邮箱验证网络请求
    • 3.使用flask-caching缓存验证码并验证
    • 4.重构restful API
  • 5.注册页面的post请求
    • 5.1注册页面邮箱验证码的ajax请求
    • 5.2注册页面的图形验证码功能
    • 5.3注册页面的post提交
  • 6.登录页面的post请求
  • 7.首页
    • 7.1首页状态切换功能
    • 7.2首页设置功能
    • 7.3首页头像功能
    • 7.4个性签名功能
  • 8.帖子相关设置
    • 8.1帖子板块
    • 8.2发布帖子
    • 8.3帖子详情页
    • 8.4首页帖子列表

flask 系列的代码笔记都放在了 仓库。

1.配置和数据库链接

1.exts.py里面实例化sqlalchemy数据库

from flask_sqlalchemy import SQLAlchemy
db=SQLAlchemy()

2.config.py配置app和数据库信息

#1.app配置
DEBUG=True
#2.数据库配置
DB_USERNAME="root"
DB_PASSWORD="1xxxx"
DB_HOST="127.0.0.1"
DB_PORT="3306"
DB_NAME="aforum"
DB_URI="mysql+pymysql://%s:%s@%s:%s/%s?charset=utf8mb4" % (DB_USERNAME,DB_PASSWORD,DB_HOST,DB_PORT,DB_NAME)
SQLALCHEMY_DATABASE_URI=DB_URI
SQLALCHEMY_TRACK_MODIFIER=False

3.app.py导入exts和config并初始化到app上

from flask import Flask
import config
from exts import db
app=Flask(__name__)#1.实例化app
app.config.from_object(config)#2.config配置文件绑定到app
db.init_app(app)#3.数据库绑定到app
@app.route('/')
def index():return "hello"
if __name__=="__main__":app.run()

2.创建用户模型并映射到数据库

1.models/auth.py创建用户模型

from exts import db
import shortuuid
from datetime import datetime
from werkzeug.security import generate_password_hash,check_password_hashclass UserModel(db.Model):__tablename__ = "user"id = db.Column(db.String(100), primary_key=True, default=shortuuid.uuid)email = db.Column(db.String(50), unique=True, nullable=False)username = db.Column(db.String(50), nullable=False)_password = db.Column(db.String(200), nullable=False)avatar = db.Column(db.String(100))signature = db.Column(db.String(100))join_time = db.Column(db.DateTime, default=datetime.now)is_staff = db.Column(db.Boolean, default=False)is_active = db.Column(db.Boolean, default=True)def __init__(self, *args, **kwargs):if "password" in kwargs:self.password = kwargs.get('password')kwargs.pop("password")super(UserModel, self).__init__(*args, **kwargs)@propertydef password(self):return self._password@password.setterdef password(self, newpwd):self._password = generate_password_hash(newpwd)def check_password(self,rawpwd):return check_password_hash(self.password, rawpwd)

2.app.py导入模型并用flask-migrate管理数据库

from flask_migrate import Migrate
from models import auth
migrate=Migrate(app,db)

3.命令行migrate三部曲将模型映射到数据库

在app.py文件的目录下

flask db init
flask db migrate
flask db upgrade

3.登录与注册页面的get请求

1.首先写登录和注册的前端页面

#1。首先抽出base.html文件
<html>
<head><meta charset="utf-8"><script src="http://cdn.bootcss.com/jquery/3.1.1/jquery.min.js"></script><link href="http://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet"><script src="http://cdn.bootcss.com/bootstrap/3.3.7/js/bootstrap.min.js"></script><script src="{{ url_for('static', filename='front/js/zlajax.js') }}"></script><script src="{{ url_for('static', filename='front/js/zlparam.js') }}"></script><link rel="stylesheet" href="{{ url_for('static', filename='front/css/front_base.css') }}"><meta name="viewport" content="width=device-width, initial-scale=1"><title>{% block title %}{% endblock %}</title>{% block head %}{% endblock %}
</head><body><nav class="navbar navbar-default"><div class="container"><div class="navbar-header"><button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false"><span class="sr-only">Toggle navigation</span><span class="icon-bar"></span><span class="icon-bar"></span><span class="icon-bar"></span></button><a class="navbar-brand" href="/">论坛</a></div><!-- Collect the nav links, forms, and other content for toggling --><div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1"><ul class="nav navbar-nav"><li class="active"><a href="/">首页<span class="sr-only">(current)</span></a></li></ul><form class="navbar-form navbar-left"><div class="form-group"><input type="text" class="form-control" placeholder="请输入关键字"></div><button type="submit" class="btn btn-default">搜索</button></form><ul class="nav navbar-nav navbar-right">{% if user %}<li class="dropdown"><a href="#" class="dropdown-toggle" type="button" id="dropdownMenu1" data-toggle="dropdown" aria-haspopup="true" aria-expanded="true">{{ user.username }}<span class="caret"></span></a><ul class="dropdown-menu" aria-labelledby="dropdownMenu1"><li><a href="{{ url_for('front.cms') }}">后台管理</a></li><li><a href="{{ url_for('front.setting') }}">设置</a></li><li><a href="{{ url_for('front.logout') }}">注销</a></li></ul></li>{% else %}<li><a href="{{ url_for('front.login') }}">登录</a></li><li><a href="{{ url_for('front.register') }}">注册</a></li>{% endif %}</ul></div><!-- /.navbar-collapse --></div><!-- /.container-fluid --></nav><div class="main-container">{% block body %}{% endblock %}</div>
</body></html>
#2.login.html文件
{% extends "front/base.html" %}{% block title %}登录
{% endblock %}{% block head %}<link rel="stylesheet" href="{{ url_for('static', filename='front/css/signbase.css') }}"><script src="{{ url_for('static', filename='front/js/login.js') }}"></script>
{% endblock %}
{% block body %}<div class="outer-box"><div class="logo-box"><a href="/"><img src="{{ url_for('static', filename='front/images/logo.png') }}" alt=""></a></div><h2 class="page-title">登录</h2><div class="sign-box"><div class="form-group"><input type="text" class="form-control" name="email" placeholder="邮箱"></div><div class="form-group"><input type="password" class="form-control" name="password" placeholder="密码"></div><div class="checkbox"><label><input type="checkbox" name="remember" value="1">记住我</label></div><div class="form-group"><button class="btn btn-warning btn-block" id="submit-btn">立即登录</button></div><div class="form-group"><a href="#" class="signup-link">没有账号?立即注册</a><a href="#" class="resetpwd-link" style="float:right;">找回密码</a></div></div></div>
{% endblock %}
#3.register.html文件
{% extends "front/base.html" %}{% block title %}注册
{% endblock %}{% block head %}<link rel="stylesheet" href="{{ url_for('static', filename='front/css/signbase.css') }}"><script src="{{ url_for('static', filename='front/js/register.js') }}"></script>
{% endblock %}{% block body %}<div class="outer-box"><div class="logo-box"><a href="/"><img src="{{ url_for('static', filename='front/images/logo.png') }}" alt=""></a></div><h2 class="page-title">注册</h2><div class="sign-box"><div class="form-group"><div class="input-group"><input type="email" class="form-control" name="email" placeholder="邮箱"><span class="input-group-btn"><button id="email-captcha-btn" class="btn btn-default">发送验证码</button></span></div></div><div class="form-group"><input type="text" class="form-control" name="email-captcha" placeholder="邮箱验证码"></div><div class="form-group"><input type="text" class="form-control" name="username" placeholder="用户名"></div><div class="form-group"><input type="password" class="form-control" name="password" placeholder="密码"></div><div class="form-group"><input type="password" class="form-control" name="repeat-password" placeholder="确认密码"></div><div class="form-group"><div class="input-group"><input type="text" class="form-control" name="graph-captcha" placeholder="图形验证码"><span class="input-group-addon captcha-addon"><img id="captcha-img" class="captcha-img" src="#" alt=""></span></div></div><div class="form-group"><button class="btn btn-warning btn-block" id="submit-btn">立即注册</button></div></div></div>
{% endblock %}

2.写它们的view蓝图并导入到__init__中

#1.在apps/front/views.py里面写蓝图的视图函数
from flask import Blueprint,request,render_template
bp=Blueprint("front",__name__,url_prefix="/")@bp.route('/login/', methods=['GET', 'POST'])
def login():if request.method == 'GET':return render_template('front/login.html')@bp.route('/register/', methods=['GET', 'POST'])
def register():if request.method == 'GET':return render_template('front/register.html')
#2.在apps/front/__init__.py里面导入蓝图
from .views import bp as front_bp                             

3.蓝图注册到app

#在app.py里面导入蓝图并注册到app上
from apps.front import front_bp
app.register_blueprint(front_bp)

4.邮箱验证功能

1.邮箱验证

#1.在config里面配置邮箱第三方服务商来发送邮件
MAIL_SERVER="smtp.qq.com"#发送验证码的邮箱服务器,这里是自己公司的邮箱服务器
MAIL_PORT='587'#587是tls协议,465是ssl协议
MAIL_USE_TLS=True
#MAIL_USE_SSL
MAIL_USERNAME="1xxxc9@qq.com"
MAIL_PASSWORD="wxxbe"
MAIL_DEFAULT_SENDER="11xx@qq.com"
#2.exts里面导入mail
from flask_mail import Mail
mail=Mail()
#3.在app里面把exts里面的mail导入进来并绑定到app
from exts import db,mail
mail.init_app(app)
#4.开始在views里面写发送邮箱验证码的视图函数
from exts import mail
from flask_mail import Message
from flask importjsonify
import string,random
@bp.get("/email/captcha/")
def email_captcha():email=request.args.get('email')if not email:return jsonify({"code":400,"message":"请先传入邮箱"})source=list(string.digits)captcha="".join(random.sample(source,6))message=Message(subject="注册验证码",recipients=[email],body="您的注册验证码是:%s" % captcha)try:mail.send(message)except Exception as e:print("邮件发送失败")print(e)return jsonify({"code":500,"message":"邮件发送失败"})return jsonify({"code":200,"message":"邮件发送成功"})

2.使用celery异步发送邮箱验证网络请求

celery(分布式任务队列/任务调度器)和redis(内存数据库)的教程和安装步骤可以参考学习。Broker和Backend都用redis存储。

pip install gevent
pip install redis
pip install hiredis
启动celery

redis-cli

在这里插入图片描述

#1.在config中设置reids的相关信息
CELERY_BROKER_URL="redis://127.0.0.1:6379/0"#broker
CELERY_RESULT_BACKEND="redis://127.0.0.1:6379/0"#backend
#2.mycelery.py里面定义并添加任务
from flask_mail import Message
from exts import mail
from celery import Celery# 定义任务函数
def send_mail(recipient,subject,body):message = Message(subject=subject,recipients=[recipient],body=body)try:mail.send(message)return {"status": "SUCCESS"}except Exception :return {"status": "FAILURE"}# 创建celery对象
def make_celery(app):celery = Celery(app.import_name, backend=app.config['CELERY_RESULT_BACKEND'],broker=app.config['CELERY_BROKER_URL'])TaskBase = celery.Taskclass ContextTask(TaskBase):abstract = Truedef __call__(self, *args, **kwargs):with app.app_context():return TaskBase.__call__(self, *args, **kwargs)celery.Task = ContextTaskapp.celery = celery# 添加任务celery.task(name="send_mail")(send_mail)return celery
#3.在app.py里面将celery绑定到app
from mycelery import make_celery
mycelery=make_celery(app)
#4.在views.py里面利用current_app调用celery里面的task任务
from flask import current_app
@bp.get("/email/captcha/")
def email_captcha():email=request.args.get('email')if not email:return jsonify({"code":400,"message":"请先传入邮箱"})source=list(string.digits)captcha="".join(random.sample(source,6))subject="注册验证码"body="您的注册验证码是:%s"%captchacurrent_app.celery.send_task("send_mail",(email,subject,body))return jsonify({"code":200,"message":"邮件发送成功"})
#5.在工程目录下运行这个celery
celery -A app.mycelery worker --loglevel=info -P gevent
#6.访问这个视图函数就可以成功利用celery进行异步任务调取

3.使用flask-caching缓存验证码并验证

flask-caching的相关教程可以查看博文。

#1.安装:pip install flask-caching
#2.在config里面写flask-caching相关的配置
CACHE_TYPE="RedisCache"
CACHE_DEFAULT_TIMEOUT=300
CACHE_REDIS_HOST="127.0.0.1"
CACHE_REDIS_PORT=6379
#3.在exts里面引入caching
from flask_caching import Cache
cache=Cache()
#4.在app.py里面init
from exts import cache
cache.init_app(app)
#5.在view的视图函数里面缓存验证码
from exts import cache
cache.set(email,captcha)#cache缓存是键值对的形式

4.重构restful API

#1.在utils/restful.py里面
# Restful API
from flask import jsonifyclass HttpCode(object):# 响应正常ok = 200# 没有登陆错误unloginerror = 401# 没有权限错误permissionerror = 403# 客户端参数错误paramserror = 400# 服务器错误servererror = 500def _restful_result(code, message, data):return jsonify({ "code": code,"message": message or "", "data": data or {}})def ok(message=None, data=None):return _restful_result(code=HttpCode.ok, message=message, data=data)def unlogin_error(message="没有登录!"):return _restful_result(code=HttpCode.unloginerror, message=message, data=None)def permission_error(message="没有权限访问!"):return _restful_result(code=HttpCode.paramserror, message=message, data=None)def params_error(message="参数错误!"):return _restful_result(code=HttpCode.paramserror, message=message, data=None)def server_error(message="服务器开小差啦!"):return _restful_result(code=HttpCode.servererror, message=message or '服务器内部错误', data=None)
#2.在view视图函数里
from utils import restful
return restful.params_error(message="请先传入邮箱")
return restful.ok(message="邮件发送成功")

5.注册页面的post请求

5.1注册页面邮箱验证码的ajax请求

#1.在register.html里面引入js文件
<script src="{{ url_for('static', filename='front/js/register.js') }}"></script>
#2.在register.js里面监听(4步),这里引用zlajax是因为它自动给了csrf-token
var RegisterHandler = function (){\\1.定义了一个JavaScript对象}RegisterHandler.prototype.listenSendCaptchaEvent = function (){\\2.包含一个方法var callback = function (event){// 原生的JS对象:this => jQuery对象var $this = $(this);// 阻止默认的点击事件event.preventDefault();var email = $("input[name='email']").val();var reg = /^\w+((.\w+)|(-\w+))@[A-Za-z0-9]+((.|-)[A-Za-z0-9]+).[A-Za-z0-9]+$/;if(!email || !reg.test(email)){alert("请输入正确格式的邮箱!");return;}zlajax.get({url: "/email/captcha?email=" + email,success: function (result){if(result['code'] == 200){console.log("邮件发送成功!");// 取消按钮的点击事件$this.off("click");// 添加禁用状态$this.attr("disabled", "disabled");// 开始倒计时var countdown = 60;var interval = setInterval(function (){if(countdown > 0){$this.text(countdown);}else{$this.text("发送验证码");$this.attr("disabled", false);$this.on("click", callback);// 清理定时器clearInterval(interval);}countdown--;}, 1000);}else{var message = result['message'];alert(message);}}})}$("#email-captcha-btn").on("click", callback);
}
RegisterHandler.prototype.run = function (){\\3.方法在run函数里调用this.listenSendCaptchaEvent();
}// $(function(){})
$(function (){\\4.实例化并运行var handler = new RegisterHandler();handler.run();
})#3.post请求要用csrf-token,所以在base.html里面引入
<meta name="csrf-token" content="{{csrf_token()}}">
#4.csrf-token需要先安装:
pip install flask-wtf
#5.在config里面设置secretkey
SECRET_KEY="FASDFNMLKSDF"
#6.在exts里面引入
from flask_wtf import CSRFProtect
csrf=CSRFProtect()
#6.在app上绑定init
from exts import csrf
csrf.init_app(app)

5.2注册页面的图形验证码功能

#1.在config里面获取工程的base目录
import os
BASE_DIR=os.path.dirname(__file__)
#2.在utils目录下的captcha的init文件里生成图形验证码
import random
import string
# Image:一个画布
# ImageDraw:一个画笔
# ImageFont:画笔的字体
from PIL import Image,ImageDraw,ImageFontfrom flask import current_app
import os# pip install pillow# Captcha验证码class Captcha(object):# 生成几位数的验证码number = 4# 验证码图片的宽度和高度size = (100,30)# 验证码字体大小fontsize = 25# 加入干扰线的条数line_number = 2# 构建一个验证码源文本SOURCE = list(string.ascii_letters)for index in range(0, 10):SOURCE.append(str(index))#用来绘制干扰线@classmethoddef __gene_line(cls,draw,width,height):begin = (random.randint(0, width), random.randint(0, height))end = (random.randint(0, width), random.randint(0, height))draw.line([begin, end], fill = cls.__gene_random_color(),width=2)# 用来绘制干扰点@classmethoddef __gene_points(cls,draw,point_chance,width,height):chance = min(100, max(0, int(point_chance))) # 大小限制在[0, 100]for w in range(width):for h in range(height):tmp = random.randint(0, 100)if tmp > 100 - chance:draw.point((w, h), fill=cls.__gene_random_color())# 生成随机的颜色@classmethoddef __gene_random_color(cls,start=0,end=255):random.seed()return (random.randint(start,end),random.randint(start,end),random.randint(start,end))# 随机选择一个字体@classmethoddef __gene_random_font(cls):fonts = ['Courgette-Regular.ttf','LHANDW.TTF','Lobster-Regular.ttf','verdana.ttf']font = random.choice(fonts)fontpath = os.path.join(current_app.config['BASE_DIR'],'utils','captcha',font)# return 'utils/captcha/'+fontreturn fontpath# 用来随机生成一个字符串(包括英文和数字)@classmethoddef gene_text(cls, number):# number是生成验证码的位数return ''.join(random.sample(cls.SOURCE, number))#生成验证码@classmethoddef gene_graph_captcha(cls):# 验证码图片的宽和高width,height = cls.size# 创建图片# R:Red(红色)0-255# G:G(绿色)0-255# B:B(蓝色)0-255# A:Alpha(透明度)image = Image.new('RGBA',(width,height),cls.__gene_random_color(0,100))# 验证码的字体font = ImageFont.truetype(cls.__gene_random_font(),cls.fontsize)# 创建画笔draw = ImageDraw.Draw(image)# 生成字符串text = cls.gene_text(cls.number)# 获取字体的尺寸font_width, font_height = font.getsize(text)# 填充字符串draw.text(((width - font_width) / 2, (height - font_height) / 2),text,font= font,fill=cls.__gene_random_color(150,255))# 绘制干扰线for x in range(0, cls.line_number):cls.__gene_line(draw, width, height)# 绘制噪点cls.__gene_points(draw, 10, width, height)return (text,image)
#3.在views里面写视图函数
from utils.captcha import Captcha
import time
from hashlib import md5
from io import BytesIO
from flask import make_response
@bp.route("/graph/captcha/")
def graph_captcha():captcha,image=Captcha.gene_graph_captcha()key=md5((captcha+str(time.time())).encode('utf-8')).hexdigest()cache.set(key,captcha)#cache里面缓存这个captchabuffer=BytesIO()image.save(buffer,"png")buffer.seek(0)#buffer文件指针指向最开始的位置resp=make_response(buffer.read())resp.content_type="image/png"resp.set_cookie("_graph_captcha_key",key,max_age=3600)#将key值保存到cookie1个小时return resp
#4.在register.html里面写图片验证码的src
<img id="captcha-img" class="captcha-img" src="{{url_for('front.graph_captcha')}}" alt="">
#5.实现点击图片重新生成,所以在regist.js里面监听
RegisterHandler.prototype.listenGraphCaptchaEvent = function (){$("#captcha-img").on("click", function (){console.log("点击了图形验证码");var $this = $(this);var src = $this.attr("src");// /graph/captcha// /graph/captcha?sign=Math.random()// 防止一些老的浏览器,在两次url相同的情况下,不会重新发送请求,导致图形验证码不会更新let new_src = zlparam.setParam(src, "sign", Math.random())$this.attr("src",new_src);});
}
RegisterHandler.prototype.run = function (){this.listenSendCaptchaEvent();this.listenGraphCaptchaEvent();
}

5.3注册页面的post提交

#1.在front/forms.py里面进行表单验证
from wtforms import Form,ValidationError
from wtforms.fields import StringField
from wtforms.validators import Email,Length,EqualTo
from models.auth import UserModel#对表单进行二次验证
from exts import cache
from flask import request
class BaseForm(Form):@propertydef messages(self):message_list = []if self.errors:for error in self.errors.values():message_list.extend(error)return message_list
class RegisterForm(BaseForm):email=StringField(validators=[Email(message="请输入正确的邮箱")])email_captcha=StringField(validators=[Length(6,6,message="请输入6位验证码")])username=StringField(validators=[Length(3,20,message="请输入3-20位的用户名")])password=StringField(validators=[Length(6,20,message="请输入6-20位的密码")])    repeat_password=StringField(validators=[EqualTo("password",message="两次密码不一致")])graph_captcha=StringField(validators=[Length(4,4,message="请输入4位图形验证码")])def validate_email(self,field):email=field.datauser=UserModel.query.filter_by(email=email).first()if user:raise ValidationError(message="邮箱已经被注册")def validate_email_captcha(self,field):email_captcha=field.dataemail=self.email.datacache_captcha=cache.get(email)if not cache_captcha or cache_captcha!=email_captcha:raise ValidationError(message="邮箱验证码错误")def validate_graph_captcha(self,field):graph_captcha=field.datakey=request.cookies.get("_graph_captcha_key")cache_captcha=cache.get(key)if not cache_captcha or cache_captcha.lower()!=graph_captcha.lower():raise ValidationError(message="图形验证码错误")
#2.在front/views.py里面写post视图函数
from .forms import RegisterForm
from models.auth import UserModel
from exts import db
@bp.route('/register/', methods=['GET', 'POST'])
def register():if request.method == 'GET':return render_template('front/register.html')else:form=RegisterForm(request.form)if form.validate():email=form.email.datausername=form.username.datapassword=form.password.datauser=UserModel(email=email,username=username,password=password)db.session.add(user)db.session.commit()return restful.ok()else:message=form.messages[0]return restful.params_error(message=message)
#3.在js里面绑定点击事件,跳到上面的视图函数
RegisterHandler.prototype.listenSubmitEvent = function (){$("#submit-btn").on("click", function (event){event.preventDefault();var email = $("input[name='email']").val();var email_captcha = $("input[name='email-captcha']").val();var username = $("input[name='username']").val();var password = $("input[name='password']").val();var repeat_password = $("input[name='repeat-password']").val();var graph_captcha = $("input[name='graph-captcha']").val();// 如果是商业项目,一定要先验证这些数据是否正确zlajax.post({url: "/register",data: {"email": email,"email_captcha": email_captcha,"username": username,password, // "password": passwordrepeat_password,graph_captcha},success: function (result){if(result['code'] == 200){window.location = "/login";}else{alert(result['message']);}}})});
}RegisterHandler.prototype.run = function (){this.listenSendCaptchaEvent();this.listenGraphCaptchaEvent();this.listenSubmitEvent();
}

6.登录页面的post请求

#1.首先表单验证
from wtforms.fields import IntegerField
class LoginForm(BaseForm):email=StringField(validators=[Email(message="请输入正确的邮箱")])password=StringField(validators=[Length(6,20,message="请输入6-20位的密码")]) remember=IntegerField()
#2.视图函数
from flask import session
from .forms import LoginForm
@bp.route('/login/', methods=['GET', 'POST'])
def login():if request.method == 'GET':return render_template('front/login.html')else:form=LoginForm(request.form)if form.validate():email=form.email.datapassword=form.password.dataremember=form.remember.datauser=UserModel.query.filter_by(email=email).first()if not user:return restful.params_error("此邮箱没有注册")if not user.check_password(password):return restful.params_error("邮箱或密码错误")session['user_id']=user.idif remember ==1:session.permanent=Truereturn restful.ok()else:return restful.params_error(message=form.messages[0])
#3.在config里面设置permanent时间
from datetime import timedelta
PERMANENT_SESSION_LIFETIME=timedelta(days=7)
#4.登录的post提交的前端监听,在login.js中,并加载到html中
var LoginHandler = function (){}LoginHandler.prototype.listenSubmitEvent = function (){$("#submit-btn").on("click", function (event){event.preventDefault();var email = $("input[name='email']").val();var password = $("input[name='password']").val();var remember = $("input[name='remember']").prop("checked");zlajax.post({url: "/login",data: {email,password,remember: remember?1:0},success: function (result){if(result['code'] == 200){var token = result['data']['token'];var user = result['data']['user'];localStorage.setItem("JWT_TOKEN_KEY", token);localStorage.setItem("USER_KEY", JSON.stringify(user));window.location = "/"}else{alert(result['message']);}}})});
}LoginHandler.prototype.run = function (){this.listenSubmitEvent();
}$(function (){var handler = new LoginHandler();handler.run();
});

7.首页

7.1首页状态切换功能

1.get请求:写index视图函数和html
2.状态切换功能

#1.写退出登录的视图函数
@bp.route("/logout/")
def logout():session.clear()return redirect("/")
#2.利用钩子函数(用户发送请求前的操作)和上下文处理器函数(视图函数返回给用户数据前的操作)将user绑定到g上
bp=Blueprint("front",__name__,url_prefix="/")
@bp.before_request#钩子函数,在用户访问视图函数前在session里拿到用户绑到g上
def front_before_request():if 'user_id' in session:user_id=session.get("user_id")user=UserModel.query.get(user_id)setattr(g,"user",user)
@bp.context_processor#上下文处理器函数,在视图函数里,服务器返回给用户数据前将这里的参数返回给模板进行渲染
def front_after_request():if hasattr(g,"user"):return {"user":g.user}else:return {}
#3.修改html里面的变量
{% if user %}<li class="dropdown"><a href="#" class="dropdown-toggle" type="button" id="dropdownMenu1" data-toggle="dropdown" aria-haspopup="true" aria-expanded="true">{{ user.username }}<span class="caret"></span></a><ul class="dropdown-menu" aria-labelledby="dropdownMenu1"><li><a href="#">后台管理</a></li><li><a href="{{url_for('front.setting')}}">设置</a></li><li><a href="{{ url_for('front.logout') }}">注销</a></li></ul></li>{% else %}<li><a href="{{ url_for('front.login') }}">登录</a></li><li><a href="{{ url_for('front.register') }}">注册</a></li>{% endif %}

7.2首页设置功能

#1.在front/decorates.py里面写登录装饰器
from flask import g,redirect,url_for
from functools import wraps
def login_required(func):@wraps(func)def inner(*args, **kwargs):if hasattr(g,"user"):return func(*args, **kwargs)else:return redirect(url_for('front.login'))return inner
#2.写视图函数和html,以及设置按钮的跳转链接,并要有登录装饰器
@bp.route("/setting/")
@login_required
def setting():email_hash=md5(g.user.email.encode("utf-8")).hexdigest()return render_template("front/setting.html",email_hash=email_hash)

7.3首页头像功能

#1.pip install flask-avatars
#2.在exts里面导入,在app里面初始化
from flask_avatars import Avatars
avatars=Avatars()
from exts import avatars
avatars.init_app(app)
#3.使用Gravatar头像
<img src="{{ avatars.gravatar(email_hash) }}" alt="..." class="img-circle" id="avatar-img">
#4.使用标识生成头像,并把L尺寸的头像地址保存到数据库
AVATARS_SAVE_PATH=os.path.join(BASE_DIR,"media","avatars")#在config里面设置图像的保存地址
#在register视图函数里面添加avatar的保存数据
from flask_avatars import Identicon
import osidenticon=Identicon()filenames=identicon.generate(text=md5(email.encode("utf-8")).hexdigest())avatar=filenames[2]user=UserModel(email=email,username=username,password=password,avatar=avatar)#在media/view里面写访问头像的视图函数,并绑定到app上,然后修改html的链接from flask import Blueprint,send_from_directory,current_app
bp=Blueprint("media",__name__,url_prefix="/media")
@bp.route('/avatar/<filename>')
def get_avatar(filename):return send_from_directory(current_app.config['AVATARS_SAVE_PATH'],filename)
from apps.media import media_bp 
app.register_blueprint(media_bp)
<img src="{{ url_for('media.get_avatar',filename=user.avatar) }}" alt="..." class="img-circle" id="avatar-img">
#4.用户自定义图像:用户头像上传就是表单提交的方式,所以要进行表单验证,然后写视图函数,
class UploadAvatarForm(BaseForm):image=FileField(validators=[FileAllowed(['png', 'jpg', 'jpeg',],message='图片格式不符合要求'),FileSize(max_size=1024*1024*5,message='图片大小不超过5MB')])
@bp.post("/avatar/upload/")
@login_required
def upload_avatar():form=UploadAvatarForm(request.files)if form.validate():image=form.image.datafilename=image.filename_,ext=os.path.splitext(filename)filename=md5((g.user.email+str(time.time())).encode('utf-8')).hexdigest()+extimage_path=os.path.join(current_app.config['AVATARS_SAVE_PATH'],filename)image.save(image_path)g.user.avatar=filenamedb.session.commit()return restful.ok(data={'avatar':filename})else:message=form.messages[0]return restful.params_error(message=message)
#写图像上传的js,并将js导入到html,

7.4个性签名功能

#1.写form表单验证,view视图进行post请求,写js,导入到html

8.帖子相关设置

8.1帖子板块

1.命令行实现板块初始化

#1.在models/post.py里面创建帖子板块模型,并导入到app文件,然后migrate到数据库
from exts import db
from datetime import datetime
class BoardModel(db.Model):__tablename__ = 'board'id=db.Column(db.Integer, primary_key=True,autoincrement=True)name=db.Column(db.String(20),unique=True)priority=db.Column(db.Integer, default=1)create_time=db.Column(db.DateTime,default=datetime.now)
from models import post#app.py里面导入一下
flask db migrate
flask db upgrade
#2.在commands.py里面写初始化板块的命令函数(给板块数据到db数据库)
from models.post import BoardModel
from exts import db
def init_boards():board_names=['flask','fast','ai','爬虫']for index,board_name in enumerate(board_names):board=BoardModel(name=board_name,priority=len(board_names)-index)db.session.add(board)db.session.commit()print("板块初始化成功")
#3.在app.py里面导入并注册命令
import commands
app.cli.command("inbo")(commands.init_boards)
#4.在cmd里面调用命令
flask inbo
#5.板块的后端已经实现,现在要将这个信息传给前端并显示,所以在首页(/)视图函数下传参并在html中循环显示
from models.post import BoardModel
boards=BoardModel.query.order_by(BoardModel.priority.desc()).all()
return render_template("front/index.html",boards=boards)
{% for board in boards %}<a href="#" class="list-group-item">{{board.name}}</a>
{% endfor %}

2.创建帖子相关的模型,包括PostModel,BannerModel,CommentModel,并migrate到数据库

8.2发布帖子

1.get

#1.首先修改首页的发布帖子的跳转链接,然后写视图函数和html文件
<a href="{{url_for('front.public_post')}}" class="btn btn-warning btn-block">发布帖子</a>
@bp.route("post/public/",methods=["POST", "GET"])
def public_post():if request.method == "GET":boards=BoardModel.query.all()return render_template("front/public_post.html",boards=boards)

2.富文本编辑器wangEditor,这里首先需要导入它的js文件,可以按官网的示例线上引用也可以下载到本地再导入。

#1.在public_post.html里面导入富文本编辑器的js
<script type="text/javascript" src="{{url_for('static',filename='lib/wangEditor/wangEditor.min.js')}}"></script>
#2.写自己的初始化编辑器和文本内容提交的js并引入到html中
<script type="text/javascript" src="{{url_for('static',filename='front/js/public_post.js')}}"></script>
#3.上传图片到本地,首先在wangeditor里面初始化图片上传的路径等信息,然后写图片上传的视图函数@bp.post("/post/image/upload/")
@login_required
def upload_post_image():form=UploadAvatarForm(request.files)if form.validate():image=form.image.datafilename=image.filename_,ext=os.path.splitext(filename)filename=md5((g.user.email+str(time.time())).encode('utf-8')).hexdigest()+extimage_path=os.path.join(current_app.config['POST_IMAGE_SAVE_PATH'],filename)image.save(image_path)return jsonify({"errno": 0, # 注意:值是数字,不能是字符串"data": [{"url": url_for('media.get_post_image',filename=filename), # 图片 src ,必须"alt": "filename", # 图片描述文字,非必须"href": "" # 图片的链接,非必须}]
})else:message=form.messages[0]return jsonify({"errno": 1, #只要不等于 0 就行"message": message
})

3.发布帖子的提交

#1.首先表单验证,然后写帖子内容提交的视图函数,js提交前端
@bp.route("post/public/",methods=["POST", "GET"])
@login_required
def public_post():if request.method == "GET":boards=BoardModel.query.all()return render_template("front/public_post.html",boards=boards)else:form=PublicPostForm(request.form)if form.validate():title=form.title.datacontent=form.content.databoard_id=form.board_id.datatry:board=BoardModel.query.get(board_id)except Exception as e:return restful.error(message='板块不存在')post_model=PostModel(title=title,content=content,board=board,author=g.user)db.session.add(post_model)db.session.commit()return restful.ok(data={"id":post_model.id})else:return restful.params_error(message=form.messages[0])
var PublicPostHandler = function (){var csrf_token = $("meta[name='csrf-token']").attr("content");var editor = new window.wangEditor("#editor");editor.config.uploadImgServer = "/post/image/upload";editor.config.uploadFileName = "image";// 1. 放到请求体中// 2. 放到请求头中X-CSRFToken// 再和cookie中的csrf_token进行对比editor.config.uploadImgHeaders = {"X-CSRFToken": csrf_token}editor.config.uploadImgMaxSize = 1024*1024*5;editor.create();this.editor = editor;}PublicPostHandler.prototype.listenSubmitEvent = function (){var that = this;$("#submit-btn").on("click", function (event){event.preventDefault();var title = $("input[name='title']").val();var board_id = $("select[name='board_id']").val();var content = that.editor.txt.html();zlajax.post({url: "/post/public/",data: {title,board_id,content},success: function (result){if(result['code'] == 200){let data = result['data'];let post_id = data['id'];window.location = "/post/detail/" + post_id;}else{alert(result['message']);}}});});}PublicPostHandler.prototype.run = function(){this.listenSubmitEvent();}$(function(){var handler = new PublicPostHandler();handler.run();});

8.3帖子详情页

#1.帖子详情页的get请求视图函数,html
#2.帖子详情的代码高亮功能,使用highlight.js<link rel="stylesheet" href="{{ url_for('static', filename='lib/highlight/styles/github-dark.min.css') }}"><script src="{{ url_for('static', filename='lib/highlight/highlight.min.js') }}"></script><script src="{{ url_for('static', filename='front/js/post_detail.js') }}"></script>hljs.highlightAll();#post_detail.js里面初始化
#3.帖子详情的评论功能:表单验证,post视图函数,在html中获取填写的信息通过js提交,html中导入js

8.4首页帖子列表

1.在首页的视图函数中拿到数据库的帖子数据,通过参数传递给前端html,前端通过for循环展示帖子相关信息
2.使用flask-paginate实现帖子分页

pip install flask-paginate
#1.首先在config中配置每页展示帖子的数量
PER_PAGE_COUNT=7
#2.在首页的视图函数中查询并进行分页,然后返回参数给html渲染
from flask_paginate import get_page_parameter,Pagination
@bp.route("/")
def index():boards=BoardModel.query.order_by(BoardModel.priority.desc()).all()post_query =PostModel.query.order_by(PostModel.create_time.desc())total=post_query.count()page= request.args.get(get_page_parameter(), type=int, default=1)per_page_count = current_app.config['PER_PAGE_COUNT']start = (page - 1) * per_page_countposts = post_query.offset(start).limit(per_page_count).all()pagination = Pagination(bs_version=3,page=page,total=total,per_page=per_page_count,  force_parameter=True)context={"boards":boards, "posts":posts,"pagination":pagination}return render_template("front/index.html",**context)
#3.在index的html的帖子列表末尾添加翻页按钮
<div style="text-align:center;">
{{pagination.links}}  
</div> 

3.帖子按评论顺序和时间排列

#1.首先通过html传递st参数,即按什么方式排序
{% if st==1 %}<li class="active">
{% else %} 
<li>
{% endif %}<a href="{{url_for('front.index',st=1)}}">最新</a></li>
{% if st==2 %}<li class="active">
{% else %} <li>
{% endif %}<a href="{{url_for('front.index',st=2)}}">评论最多</a></li>
#2.在view里面按照st参数进行排序,
from sqlalchemy.sql import func
@bp.route("/")
def index():sort=request.args.get('st',type=int,default=1)post_query=Noneif sort==1:post_query=PostModel.query.order_by(PostModel.create_time.desc())else:post_query=db.session.query(PostModel).outerjoin(CommentModel).group_by(PostModel.id).order_by(func.count(CommentModel.id).desc(),PostModel.create_time.desc())...context={"boards":boards, "posts":posts,"pagination":pagination,"st":sort}return render_template("front/index.html",**context)

4.帖子按板块过滤

#1.html传递板块bd参数
{% if not bd %}<a href="/" class="list-group-item active">所有板块</a>{% else %}<a href="/" class="list-group-item">所有板块</a>{% endif %}{% for board in boards %}{% if board.id==bd %}<a href="{{url_for('front.index',bd=board.id,page=1)}}" class="list-group-item active">{{board.name}}</a>{% else %}<a href="{{url_for('front.index',bd=board.id,page=1)}}" class="list-group-item">{{board.name}}</a>{% endif %}{% endfor %}
#2.view里面拿到bd参数,对post进行过滤
@bp.route("/")
def index():sort=request.args.get('st',type=int,default=1)board_id=request.args.get('bd',type=int,default=None)boards=BoardModel.query.order_by(BoardModel.priority.desc()).all()post_query=Noneif sort==1:post_query=PostModel.query.order_by(PostModel.create_time.desc())else:post_query = db.session.query(PostModel).outerjoin(CommentModel).group_by(PostModel.id).order_by(func.count(CommentModel.id).desc(), PostModel.create_time.desc())page= request.args.get(get_page_parameter(), type=int, default=1)per_page_count = current_app.config['PER_PAGE_COUNT']start = (page - 1) * per_page_countend=start+current_app.config['PER_PAGE_COUNT']if board_id:post_query=post_query.filter(PostModel.board_id==board_id)total = post_query.count()posts=post_query.slice(start,end)pagination = Pagination(bs_version=3,page=page,total=total,per_page=per_page_count,  force_parameter=True)context={"boards":boards, "posts":posts,"pagination":pagination,"st":sort,'bd':board_id}return render_template("front/index.html",**context)

相关文章:

Flask学习笔记_异步论坛(四)

Flask学习笔记_异步论坛&#xff08;四&#xff09; 1.配置和数据库链接1.exts.py里面实例化sqlalchemy数据库2.config.py配置app和数据库信息3.app.py导入exts和config并初始化到app上 2.创建用户模型并映射到数据库1.models/auth.py创建用户模型2.app.py导入模型并用flask-mi…...

K8S系列文章之 kubeasz部署K8S环境

自动化安装方式&#xff08;kubeasz&#xff09;* 生产环境推荐&#xff08;首次安装下载相关配置和安装包&#xff09;是基于Ansible实现的部署工具 简单介绍 每一具体k8s集群的详细配置参数文件 Ansible 任务配置文件 镜像安装包 安装部署步骤 前提 &#xff1a; 保证Ansib…...

nodejs和vue的关系--vue3教程

文章目录 总结性nodejs和vue的关系nodejs和vue产生关系的周边nodejs和vue的区别 总结性 vue是一套用于构建用户界面的前端框架&#xff0c;如果web项目中有前后端分离&#xff0c;前端项目想单独运行在服务器端&#xff0c;那么就要依赖nodeJs。 Vue的配套周边会和Node.js产生…...

前端大屏尺寸实现自适应屏幕大小

说在前面 目前很多业主在使用系统的时候都会有大屏的需求&#xff0c;很多屏幕并不会像我们开发的屏幕一样标准&#xff0c;比如1920*1080&#xff0c;这样我们就需要根据业主的屏幕尺寸进行适配&#xff0c;避免一些图表或文字在大屏中出现偏移&#xff0c;影响视觉观感。 方…...

leetcode 416. 分割等和子集

给你一个 只包含正整数 的 非空 数组 nums 。请你判断是否可以将这个数组分割成两个子集&#xff0c;使得两个子集的元素和相等。 示例 1&#xff1a; 输入&#xff1a;nums [1,5,11,5] 输出&#xff1a;true 解释&#xff1a;数组可以分割成 [1, 5, 5] 和 [11] 。 示例 2&a…...

cesium加载三维模型3dtiles

1.将数据和代码放到一个目录下 目的&#xff1a;为避免跨域 输入cmd命令 python3 -m http.server 5500 2.三维服务地址 http://127.0.0.1:5500/data/mars3d-max-shihua-3dtiles-master/tileset.json 3.模型网页地址 http://127.0.0.1:5500/cesium/cesium%E5%8A%A0%E8%BD%…...

el-select控制单选还是多选

<el-form :inline"true" :model"form" class"demo-form-inline"><el-form-item><el-select v-model"form.properties_id" placeholder"请选择样品性质" clearable :multiple"multiple_properties"…...

nginx使用

1 安装 yum -y install gcc pcre-devel zlib-devel openssl openssl-devel yum install -y wget wget https://nginx.org/download/nginx-1.16.1.tar.gz tar -zxvf nginx-1.16.1.tar.gz cd nginx-1.16.1 ./configure --prefix/usr/local/nginx make make install2 目录 目录说…...

基于Jenkins+Python+Ubuntu+Docker的接口/UI自动化测试环境部署详细过程

基于JenkinsPythonUbuntuDocker的接口/UI自动化测试环境部署详细过程 1 Jenkins是什么&#xff1f;2 Jenkins目标是什么&#xff1f;3 什么是CI/CD?3.1 CI持续集成3.2 CD持续部署3.3 CD持续交付 4 Ubuntu环境4.1 环境需求4.2 实现思路 5 Ubuntu下安装Docker6 安装Jenkins6.1 拉…...

Linux|ubuntu下运行python

参考&#xff1a;ubuntu系统下切换python版本的方法 文章目录 python版本问题查看ubuntu下的所有python版本通过apt-get install可以安装不同版本python查看python版本号更新update-alternatives替代列表查看update-alternatives下的python版本切换python版本删除python版本 p…...

使用FreeMarker导出word文档(支持导出图片)

今天跟大家分享一下工作中比较实用的导出word 带图片的功能。 对于在idea开发中我们需要引入以下依赖&#xff1a; 2.对于eclipse 开发我们需要进入对应的jar包 这个必须放在lib下&#xff0c;同样也需要在当前项目的环境是加入该依赖 需要在MEAT-INF加入 首先制定word 导出…...

C/C++中变量按位操作

一、按位写入1 uint32_t writeBit (1 << 5) // 第5位的掩码 uint32_t value 0x12341234; // 设置第5位为1 value | writeBit;原理就是原值与掩码… 00010000进行按位相与&#xff0c;与0相交的位还是等于原来的值&#xff0c;与1相交的位则变为1。 二、按位写入0…...

uni、css——制作表格样式的模型

案例展示 这里以5列做展示&#xff08;可随意调节&#xff09; 案例代码 <view class"list"><view class"item" v-for"(item,index) in list" :key"index">1</view> <!-- 有内容 --><view clas…...

mac前端代码编辑 Sublime Text 4 Dev 中文v4.0(4151)

Sublime Text 4 for Mac是一款功能强大的代码编辑器&#xff0c;适合所有需要高效编写代码和进行代码管理的程序员使用。 快速响应&#xff1a;Sublime Text 4在加载文件和执行命令时非常快速&#xff0c;能够让用户在高效的开发过程中体验到无缝的交互。 多种语言支持&#…...

面试之HashMap

1.什么是集合框架 Java的集合主要有两个根接口Collection和Map派生出来的&#xff0c;Collection派生出来了三个子接口&#xff1a;List,Queue,Set。因此Java集合大致可分为List,Queue,Set,Map四种体系结构。 2.HashMap与TreeMap HashMap是直接实现Map接口&#xff0c;而Tree…...

promethues mysql-rules

groups: - name: mysql.rules rules: - alert: MysqlDown expr: mysql_up 0 for: 1s labels: severity: critical annotations: title: MySQL down description: "Mysql实例: 【{{ $labels.instance }}】, MySQL instance is down…...

Maven项目中Lifecycle和Plugins下的install的区别

在Maven中&#xff0c;如果你的web和service在不同的模块下&#xff0c;如果直接用用tomcat插件运行web层&#xff0c;那么运行时会报错 Failed to execute goal org.apache.maven.plugins:maven-install-plugin:2.5.2:install (default-cli) on project springboot: The pack…...

02-状态模式

1 意图 允许一个对象在其内部状态改变时改变它的行为&#xff0c;对象看起来似乎修改了它的类。&#xff08;这里的对象指的就是模型中的Context&#xff0c;行为指的就是State的子类&#xff09; 2 动机 考虑一个问题&#xff1a;实现一个表示网络连接的类TCPConnection&am…...

Python异常处理中异常的种类有哪些?你知道几个?

前言 嗨喽~大家好呀&#xff0c;这里是魔王呐 ❤ ~! 在python中不同的异常可以用不同的类型&#xff08;python中统一了类与类别&#xff0c;类型即类&#xff09;取标识&#xff0c;一个异常标识一种错误。 1.常见语法错误 AttributeError 试图访问一个对象没有的属性&#x…...

COBOL语言介绍及使用场景

COBOL&#xff08;Common Business-Oriented Language&#xff09;是一种面向业务的通用计算机编程语言&#xff0c;最初于1959年由美国国家标准学会&#xff08;ANSI&#xff09;开发。COBOL的设计目标是为了处理商业应用程序&#xff0c;尤其是大型企业级应用。本文将介绍COB…...

java_网络服务相关_gateway_nacos_feign区别联系

1. spring-cloud-starter-gateway 作用&#xff1a;作为微服务架构的网关&#xff0c;统一入口&#xff0c;处理所有外部请求。 核心能力&#xff1a; 路由转发&#xff08;基于路径、服务名等&#xff09;过滤器&#xff08;鉴权、限流、日志、Header 处理&#xff09;支持负…...

五年级数学知识边界总结思考-下册

目录 一、背景二、过程1.观察物体小学五年级下册“观察物体”知识点详解&#xff1a;由来、作用与意义**一、知识点核心内容****二、知识点的由来&#xff1a;从生活实践到数学抽象****三、知识的作用&#xff1a;解决实际问题的工具****四、学习的意义&#xff1a;培养核心素养…...

第一篇:Agent2Agent (A2A) 协议——协作式人工智能的黎明

AI 领域的快速发展正在催生一个新时代&#xff0c;智能代理&#xff08;agents&#xff09;不再是孤立的个体&#xff0c;而是能够像一个数字团队一样协作。然而&#xff0c;当前 AI 生态系统的碎片化阻碍了这一愿景的实现&#xff0c;导致了“AI 巴别塔问题”——不同代理之间…...

论文浅尝 | 基于判别指令微调生成式大语言模型的知识图谱补全方法(ISWC2024)

笔记整理&#xff1a;刘治强&#xff0c;浙江大学硕士生&#xff0c;研究方向为知识图谱表示学习&#xff0c;大语言模型 论文链接&#xff1a;http://arxiv.org/abs/2407.16127 发表会议&#xff1a;ISWC 2024 1. 动机 传统的知识图谱补全&#xff08;KGC&#xff09;模型通过…...

WordPress插件:AI多语言写作与智能配图、免费AI模型、SEO文章生成

厌倦手动写WordPress文章&#xff1f;AI自动生成&#xff0c;效率提升10倍&#xff01; 支持多语言、自动配图、定时发布&#xff0c;让内容创作更轻松&#xff01; AI内容生成 → 不想每天写文章&#xff1f;AI一键生成高质量内容&#xff01;多语言支持 → 跨境电商必备&am…...

ardupilot 开发环境eclipse 中import 缺少C++

目录 文章目录 目录摘要1.修复过程摘要 本节主要解决ardupilot 开发环境eclipse 中import 缺少C++,无法导入ardupilot代码,会引起查看不方便的问题。如下图所示 1.修复过程 0.安装ubuntu 软件中自带的eclipse 1.打开eclipse—Help—install new software 2.在 Work with中…...

Maven 概述、安装、配置、仓库、私服详解

目录 1、Maven 概述 1.1 Maven 的定义 1.2 Maven 解决的问题 1.3 Maven 的核心特性与优势 2、Maven 安装 2.1 下载 Maven 2.2 安装配置 Maven 2.3 测试安装 2.4 修改 Maven 本地仓库的默认路径 3、Maven 配置 3.1 配置本地仓库 3.2 配置 JDK 3.3 IDEA 配置本地 Ma…...

Linux 中如何提取压缩文件 ?

Linux 是一种流行的开源操作系统&#xff0c;它提供了许多工具来管理、压缩和解压缩文件。压缩文件有助于节省存储空间&#xff0c;使数据传输更快。本指南将向您展示如何在 Linux 中提取不同类型的压缩文件。 1. Unpacking ZIP Files ZIP 文件是非常常见的&#xff0c;要在 …...

腾讯云V3签名

想要接入腾讯云的Api&#xff0c;必然先按其文档计算出所要求的签名。 之前也调用过腾讯云的接口&#xff0c;但总是卡在签名这一步&#xff0c;最后放弃选择SDK&#xff0c;这次终于自己代码实现。 可能腾讯云翻新了接口文档&#xff0c;现在阅读起来&#xff0c;清晰了很多&…...

Python 训练营打卡 Day 47

注意力热力图可视化 在day 46代码的基础上&#xff0c;对比不同卷积层热力图可视化的结果 import torch import torch.nn as nn import torch.optim as optim from torchvision import datasets, transforms from torch.utils.data import DataLoader import matplotlib.pypl…...