人工智能学习(一):Flask框架的重点与细节--快速入门(长期更新)

    技术2026-10-03  5

    一、虚拟环境

    二、Flask视图基础和URL

    Content-type:可以指定服务器与客户端之间传输数据类型和数据的编码类型

    Mime-type:不能指定传输的数据编码类型(Content-type>Mine-type)

    pycharm2017.1开启debug模式的方法:

    app.run(debug=True)app.debug=Trueapp.config.update(DEBUG=True)创建文件config.py ==> 写入DEBUG=True ==>import config ==> app.config.from_object(config)创建文件config.py ==> 写入DEBUG=True ==>import config ==> app.config.from_pyfile(config.py)pycharm2018以上在configuration中修改FLASK_DEBUG打勾,Additional options: --host=0.0.0.0 --port=8000

    第一种传递参数方式(路径):

    @app.route('/article/<id>/') def article(id): print(id) return "你传入的id为:%s"%id

    参数类型:string int float path uuid

    @app.route('/<any(user,blog):module>/<int:id>/') def list1(module, id): if module=='blog': return "博客详情:%s"%id else: return "用户详情:%s"%id

    第二中参数传递方式

    get请求:request.args.get(‘uname’)post请求:request.form.get(‘uname’) from flask import request @app.route('/list7/',methods=['GET','POST']) def list2(): if request.method == 'GET': uname = request.args.get('uname') pwd = request.args.get('pwd') return render_template('login.html') elif request.method == 'POST': uname = request.form.get('uname') pwd = request.form['pwd'] return "接收到的参数为:%s %s" %(uname,pwd)

    使用url_for() 函授构建url, 接收两个以上的参数,第一个参数为”[函数名]“,接受对应url规则的命名函数,(//)如果出现其他参数,会添加到url的后面作为查询参数(?key=value)

    自定义URL转化器

    regex= 用来按对应规则捕获参数to_python(self,value) 用来操作URL中参数返回value(URL到函数中参数时生效)to_url(self,value)在调用url_for()传入参数时生效(url_for()到URL时生效) from werkzeug.routing import BaseConverter class TelephoneConveter(BaseConverter): regex = r"1[345789]\d{9}" app.url_map.converters['tel'] = TelephoneConveter

    return redirect()重定向

    三、Flask之Jinja2模板

    修改tempate的默认路径

    app = Flask(__name__,template_folder='d:/demo')

    render_remplate()传递多个参数, content是一个字典

    return render_template('/news/news_list.html',**content) #使用传参技巧 return render_template('/news/news_list.html',c = content) #不使用传参技巧

    过滤器基本语法 {{variable|过滤器名字}}

    <p>位置的绝对值为:{{position|abs}}</p>

    常见过滤器:

    abs(value):返回一个数值的绝对值。 例如:-1|abs。

    default(value,default_value,boolean=false):如果当前变量没有值,则会使用参数中的值来代替。name|default(‘xiaotuo’)——如果name不存在,则会使用xiaotuo来替代。boolean=False默认是在只有这个变量为undefined的时候才会使用default中的值,如果想使用python的形式判断是否为false,则可以传递boolean=true。也可以使用or来替换。

    escape(value)或e:转义字符,会将<、>等符号转义成HTML中的符号。例如:content|escape或content|e。

    first(value):返回一个序列的第一个元素。names|first。

    format(value,*arags,**kwargs):格式化字符串。例如以下代码:

    {{ “%s” - “%s”|format(‘Hello?’,“Foo!”) }}将输出:Helloo? - Foo!

    last(value):返回一个序列的最后一个元素。示例:names|last。

    length(value):返回一个序列或者字典的长度。示例:names|length。

    join(value,d=’+’):将一个序列用d这个参数的值拼接成字符串。

    safe(value):如果开启了全局转义,那么safe过滤器会将变量关掉转义。示例:content_html|safe。

    int(value):将值转换为int类型。

    float(value):将值转换为float类型。

    lower(value):将字符串转换为小写。

    upper(value):将字符串转换为小写。

    replace(value,old,new): 替换将old替换为new的字符串。

    truncate(value,length=255,killwords=False):截取length长度的字符串。

    striptags(value):删除字符串中所有的HTML标签,如果出现多个空格,将替换成一个空格。

    trim:截取字符串前面和后面的空白字符。

    string(value):将变量转换成字符串。

    wordcount(s):计算一个长字符串中单词的个数。

    reverse() 反向遍历

    ‘autoescape’ jinja标签可以对里面的代码块开启或关闭自动转义

    {% autoescape off %} ...代码块 {% endautoescape %}

    自定义过滤器:

    @app.template_filter('cut') def cut(value): value = value.replace("abc","***") return value

    将模板设置为自动加载模式(可以加载templates中的html文件):

    app.config['TEMPLATES_AUTO_RELOAD']=True

    条件判断语句if:

    {% if age>=18 %} <p>已经成年</p> {% elif age>10 %} <p>未成年</p> {% else %} <p>你太小了</p> {% endif %}

    循环语句for:(没有continue 和 break 语句)

    {% for user in users %} <li>{{ user }}</li> {% else %} <li>没有任何用户</li> #user为空执行此处 {% endfor %}

    Jinja中的for循环还包含以下变量,可以用来获取当前的遍历状态:

    | loop.index | 当前迭代的索引(从1开始) |

    | loop.index0 | 当前迭代的索引(从0开始) |

    | loop.first | 是否是第一次迭代,返回True或False |

    | loop.last | 是否是最后一次迭代,返回True或False |

    | loop.length | 序列的长度 |

    定义宏

    {% macro inp(uname,type="text",value="") %} <input type="{{ type }}" name="{{ uname }}" value="{{ value }}"> {% endmacro %}

    宏的导入两种方式:

    {% from "macros/macros.html" import input as inp %} #以templates为根路径 #[with context]在定义宏的页面,使用引用宏的页面的参数 {% import "macros/macros.html" as macros with context %}

    导入include:复制粘贴的形式到页面

    {% include "commons/top.html" %}

    set定义的变量在后面都可以使用(全局),with只能在语句块中使用(局部)

    加载静态文件:

    <link type="text/css" rel="stylesheet" href="{{url_for("static",filename='css/index.css')}}" >

    模板继承语法

    base.html: ...<title> {% block block_title %} xyz {% endblock %} </title>... index.html: {% extends "parents/base.html" %} ...<title> {% block block_title %} {{super()}} #xyz 要覆盖的内容 {{self.block_title()}} # 引用其他模块 {% endblock %} </title>...

    四、Flask视图高级

    add_url_rule(rule,endpoint=None,view_func=None)用来添加url与视图函数的映射,如果写了endpoint值,则在使用url_for()时要使用endpoint值(app.route()底层就是借助此方法实现)

    app.add_url_rule("/list/",endpoint='li',view_func=my_list)

    创建注册类视图:

    from flask import views class ListView(views.View): def dispatch_request(self): return "这是List列表" app.add_url_rule('/list/',view_func=ListView.as_view('my_list')) with app.test_request_context(): print(url_for('my_list'))

    重写get()、post()方法(不推荐get、post方法相互调用)

    在视图函数中使用自定义装饰器,必须放在app.route()下面

    在类视图中使用装饰器,需要重写类视图的一个类属性decorators,这个类属性是一个列表或者元组都可以,里面装的就是所有的装饰器。

    def login_requierd(func): @wraps(func) def wrapper(*args,**kwargs): username = request.args.get("username") if username and username =='momo': return func(*args,**kwargs) else: return '请先登录' return wrapper class ProfileView(views.View): decorators = [login_requierd] def dispatch_request(self): return '这是个人中心界面'

    蓝图:

    blueprints/users.py: from flask import Blueprint users_bp = Blueprint("users",__name__,url_prefix='/users', template_folder='users_page')#仍然优先从templates中查找 @users_bp.route("/aaa/") def aaa(): return "我是aaa啊" app.py: from blueprints.users import users_bp app = Flask(__name__) app.register_blueprint(users_bp)

    如果在加载静态文件的时候,指定的蓝图的名字,比如news.static,那么就会到这个蓝图指定的static_folder下查找静态文件。

    news_bp = Blueprint('news',__name__,url_prefix='/news', template_folder='news_page',static_folder='news_page_static') html: <link rel="stylesheet" href="{{ url_for('news.static',filename='news_list.css') }}">

    url_for()反转蓝图

    url_for("users.aaa")

    子域名:

    cms_bp= Blueprint('cms',__name__,subdomain='cms') app.config['SERVER_NAME']='momo.com:5000'

    五、Flask高级

    Response.set_cookie()创建cookie,Response.delete_cookie()删除cookie

    设置session

    import os from datetime import timedelta app = Flask(__name__) app.config['SECRET_KEY'] = os.urandom(24) app.config['PERMANENT_SESSION_LIFETIME'] = timedelta(hours=2) @app.route('/setsession') def setsession(): session['uname'] = 'momo' session.permanent = True return 'hello'

    手动推入应用上下文:

    #方式一 app_context_obj = app.app_context() app_context_obj.push() print(current_app.name) #方式二 with app.app_context(): print(current_app.name)

    手动推入请求上下文:

    with app.test_request_context(): #手动推入一个请求上下文到请求上下文栈中 #如果当前app应用上下文栈中没有app应用上下文,那么会首先推入一个app应用上下文到栈中 print(url_for('my_list'))

    钩子函数

    @app.before_first_request 项目处理第一次请求前执行此函数

    @app.before_request 每次请求前执行

    @app.context_processor 上下文处理器(在render_template()前执行)。必须返回一个字典,字典中的值所有模板都可以使用

    @app.errorhandler(500) 接收状态码,自定义返回这种状态码的响应的处理方法。

    ​ abort(400) : 跳转到400错误

    @app.errorhandler(500) def server_error(error): return render_template('500.html'),500

    自定义信号

    from blinker import Namespace aSpace = Namespace() #创建信号 mysignal = aSpace.signal("信号名字") def func(sender): print(sender) mysignal.connect(func) #监听信号 mysignal.send() #发送信号

    request.remote_addr 获取对方ip地址

    内置信号

    template_rendered:模版渲染完成后的信号。

    def funcc(sender,template, context): print(sender) #发送者 print(template) #跳转到模板的名称 print(context) #跳转到模板页面带过去的参数 template_rendered.connect(funcc)

    before_render_template:模版渲染之前的信号。

    request_started:请求开始之前,在到达视图函数之前发送信号。

    request_finished:请求结束时,在响应发送给客户端之前发送信号。

    request_tearing_down:请求对象被销毁时发送的信号,即使在请求过程中发生异常也会发送信号。

    got_request_exception:在请求过程中抛出异常时发送信号,异常本身会通过exception传递到订阅(监听)的函数中。一般可以监听这个信号,来记录网站异常信息。

    appcontext_tearing_down:应用上下文被销毁时发送的信号。

    appcontext_pushed:应用上下文被推入到栈上时发送的信号。

    appcontext_popped:应用上下文被推出栈时发送的信号。

    message_flashed:调用了Flask的flash方法时发送的信号。

    WTForms:pip install flask-wtf

    WTForms表单验证基本使用:

    from wtforms import Form,StringField from wtforms.validators import Length,EqualTo class RegisterForm(Form): uname = StringField(validators=[Length(min=2,max=15,message="用户名必须在2-15长度之间")]) pwd = StringField(validators=[Length(min=6,max=12)]) pwd2 = StringField(validators=[Length(min=6,max=12),EqualTo('pwd')]) @app.route('/register/',methods=['GET','POST']) def register(): if request.method == 'GET': return render_template('login.html') else: form = RegisterForm(request.form) if form.validate(): return "验证通过" else: print(form.errors) return "验证不通过"

    常用验证器:

    Length:字符串长度限制,有min和max两个值进行限制。

    EqualTo:验证数据是否和另外一个字段相等,常用的就是密码和确认密码两个字段是否相等。

    Email:验证上传的数据是否为邮箱数据格式 如:223333@qq.com。

    InputRequired:验证该项数据为必填项,即要求该项非空。

    NumberRange:数值的区间,有min和max两个值限制,如果处在这两个数字之间则满足。IntegerField

    Regexp:定义正则表达式进行验证,如验证手机号码。

    URL:必须是URL的形式 如http://www.bjsxt.com。

    UUID:验证数据是UUID类型。

    自定义验证器(方法命名规则:‘validate_’+验证属性名

    code = StringField(validators=[Length(min=4,max=4)]) def validate_code(self,field): if field.data != str(session.get('code')): raise ValidationError(message="验证码不通过")

    上传文件并验证

    from werkzeug.utils import secure_filename from werkzeug.datastructures import CombinedMultiDict from flask_wtf.file import FileRequired,FileAllowed class UploadForm(Form): pichead = FileField(validators=[FileRequired(),FileAllowed(['jpg','png','gif'])]) desc = StringField(validators=[InputRequired()]) UPLOAD_PATH = os.path.join(os.path.dirname(__file__),'images') @app.route('/upload/',methods=['GET','POST']) def upload(): if request.method == 'GET': return render_template('upload.html') else: form = UploadForm(CombinedMultiDict([request.form,request.files])) if form.validate(): # desc = request.form.get("desc") # pichead = request.files.get("pichead") desc = form.desc.data pichead = form.pichead.data filename = secure_filename(pichead.filename) pichead.save(os.path.join(UPLOAD_PATH,filename)) return '文件上传成功' else: print(form.errors) return "文件上传失败"

    访问服务器已经上传成功的文件

    from flask import send_from_directory @app.route('/images/<filename>/') def get_image(filename): return send_from_directory(UPLOAD_PATH,filename)

    Flask_RESTfull基本使用

    from flask_restful import Resource,Api api = Api(app) class LoginView(Resource): def get(self): return {"flag":"no"} def post(self): return {"flag":'yes'} api.add_resource(LoginView,"/login/",'/login2/',endpoint=...) with app.test_request_context(): print(url_for('loginview')) #若没有指定endpoint将使用视图的小写作为endpoint。

    参数验证:

    default:默认值,如果这个参数没有值,那么将使用这个参数指定的默认值。required:是否必须。默认为False,如果设置为True,那么这个参数就必须提交上来。type:这个参数的数据类型,如果指定,那么将使用指定的数据类型来强制转换提交上来的值。choices:固定选项。提交上来的值只有满足这个选项中的值才符合验证通过,否则验证不通过。help:错误信息。如果验证失败后,将会使用这个参数指定的值作为错误信息。trim:是否要去掉前后的空格。 其中的type, 可以使用python自带的一些数据类型(如str或者int), 也可以使用flask_restful.inputs下的一些特定的数据类型来强制转换。 比如一些常用的: url:会判断这个参数的值是否是一个url,如果不是,那么就会抛出异常。 regex:正则表达式。 date:将这个字符串转换为datetime.date数据类型。如果转换不成功,则会抛出一个异常。 from flask_restful import Api,Resource,reqparse,inputs class RegisterView(Resource): def post(self): parser = reqparse.RequestParser() parser.add_argument('uname',type=str,help='用户名验证错误!', required=True,trim=True) args = parser.parse_args() print(args) return {"tips":"注册成功"}

    标准化参数返回:

    from flask_restful import Resource,Api,fields,marshal_with class New(): def __init__(self,title,content): self.title = news_title self.content = content self.signature=None class LoginView(Resource): resourse_field = { 'title':fields.String(attribute='news_title'), 'content':fields.String 'signature':fields.String(default='此人很懒,什么也没写') } @marshal_with(resourse_field) def get(self): new = New("titleee","contenttt") return new __str__和__repr__的区别,在列表中repr仍然打印,而str只打印地址值

    标准参数强化 复杂参数

    class NewsView2(Resource): resource_fields={ 'id':fields.Integer, 'title': fields.String, 'content': fields.String, #如在一个字段下面又是一个字典,那么可以使用fields.Nested({...}) 'author': fields.Nested({ 'id': fields.Integer, 'uname': fields.String, 'age':fields.Integer }), #如要在一个字段中放置一个列表,那么可以使用fields.List(fields.Nested({...})) 'tags': fields.List(fields.Nested({ 'id': fields.Integer, 'name': fields.String })) }

    渲染模板:

    class ListView(Resource): def get(self): return render_template('index.html') #不渲染返回字符串 @api.representation('text/html') def output_html(data,code,headers): if isinstance(data,str): # 在representation装饰的函数中,必须返回一个Response对象 # resp = make_response(data) resp =Response(data) return resp else: return Response(json.dumps(data),mimetype='application/json'

    六、Flask数据库

    创建 连接数据库:

    from sqlalchemy import create_engine #准备连接数据库基本信息 HOSTNAME = '127.0.0.1' PORT = '3306' DATABASE = 'test01' USERNAME = 'root' PASSWORD = 'root' DB_URI ="mysql+pymysql://{username}:{password}@{host}:{port}/{db}?charset=utf8" .format(username=USERNAME,password=PASSWORD,host=HOSTNAME,port=PORT,db=DATABASE) #创建数据库引擎 engine = create_engine(DB_URI) #创建连接 conn = engine.connect()

    基于ORM模型映射到数据库中

    from sqlalchemy import create_engine, Column, Integer, String from sqlalchemy.ext.declarative import declarative_base Base = declarative_base(engine) class Persion(Base): __tablename__='person' id = Column(Integer,primary_key=True,autoincrement=True) name = Column(String(50)) age = Column(Integer) country = Column(String(50)) Base.metadata.drop_all() Base.metadata.create_all()

    SQLAlchemy对数据的添加:

    from sqlalchemy.orm import sessionmaker session = sessionmaker(engine)() p1 = Persion(name='lulu',age=20,country='china') p2 = Persion(name='tantan',age=18,country='china') session.add_all([p1,p2]) session.commit()

    SQLAlchemy对数据的查找更新删除:

    ps = session.query(Person).all() ps = session.query(Person).filter_by(name='tantan').all() ps = session.query(Person).filter(Person.name=='lulu').all() ps = session.query(Person).get(1) ps = session.query(Person).first() ps.name = 'haha' ps.delete(ps) session.commit()

    SQLAlchemy的常用数据类型:

    Integer:整形,映射到数据库中是int类型。

    Float:浮点类型,映射到数据库中是float类型。他占据的32位。

    Double:双精度浮点类型,映射到数据库中是double类型,占据64位 (SQLALCHEMY中没有)。

    String:可变字符类型,映射到数据库中是varchar类型.

    Boolean:布尔类型,映射到数据库中的是tinyint类型。

    DECIMAL:定点类型。是专门为了解决浮点类型精度丢失的问题的。在存储钱相关的字段的时候建议大家都使用这个数据类型。并且这个类型使用的时候需要传递两个参数,第一个参数是用来标记这个字段总能能存储多少个数字,第二个参数表示小数点后有多少位。

    Enum:枚举类型。指定某个字段只能是枚举中指定的几个值,不能为其他值。在ORM模型中,使用Enum来作为枚举,示例代码如下:

    class News(Base): __tablename__ = 'news' tag = Column(Enum("python",'flask','django')) 在Python3中,已经内置了enum这个枚举的模块,我们也可以使用这个模块去定义相关的字段。示例代码如下: class TagEnum(enum.Enum): python = "python" flask = "flask" django = "django" class News(Base): __tablename__ = 'news' id = Column(Integer,primary_key=True,autoincrement=True) tag = Column(Enum(TagEnum)) news = News(tag=TagEnum.flask)

    Date:存储时间,只能存储年月日。映射到数据库中是date类型。在Python代码中,可以使用datetime.date来指定。

    DateTime:存储时间,可以存储年月日时分秒毫秒等。映射到数据库中也是datetime类型。在Python代码中,可以使用datetime.datetime来指定。

    Time:存储时间,可以存储时分秒。映射到数据库中也是time类型。在Python代码中,可以使用datetime.time来至此那个。示例代码如下:

    class News(Base): __tablename__ = 'news' create_time = Column(Time) news = News(create_time=time(hour=11,minute=11,second=11))

    Text:存储长字符串。一般可以存储6W多个字符。如果超出了这个范围,可以使用LONGTEXT类型。映射到数据库中就是text类型。

    LONGTEXT:长文本类型,映射到数据库中是longtext类型。

    from sqlalchemy import create_engine,Column,Integer,String,Float,Enum,Boolean,DECIMAL,Text,Date,DateTime,Time from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.dialects.mysql import LONGTEXT from sqlalchemy.orm import sessionmaker import enum from datetime import date from datetime import datetime from datetime import time #准备数据库的一堆信息 ip port user pwd 数据库的名称 按要求组织格式 HOSTNAME = '127.0.0.1' PORT = '3306' DATABASE = 'first_sqlalchemy' USERNAME = 'root' PASSWORD = 'root' #dialect+driver://username:password@host:port/database?charset=utf8 #按照上述的格式来 组织数据库信息 DB_URI ="mysql+pymysql://{username}:{password}@{host}:{port}/{db}?charset=utf8".\ format(username=USERNAME,password=PASSWORD,host=HOSTNAME,port=PORT,db=DATABASE) #创建数据库引擎 engine = create_engine(DB_URI) #创建会话对象 session = sessionmaker(engine)() #定义一个枚举类 class TagEnum(enum.Enum): python="PYHTON2" flask="FLASK2" django ="DJANGO" #创建一个ORM模型 说明基于sqlalchemy 映射到mysql数据库的常用字段类型有哪些? Base = declarative_base(engine) class News(Base): __tablename__='news' id = Column(Integer,primary_key=True,autoincrement=True) price1 = Column(Float) #存储数据时存在精度丢失问题 price2 = Column(DECIMAL(10,4)) title = Column(String(50)) is_delete =Column(Boolean) tag1 =Column(Enum('PYTHON','FLASK','DJANGO')) #枚举常规写法 tag2 =Column(Enum(TagEnum)) #枚举另一种写法 create_time1=Column(Date) create_time2=Column(DateTime) create_time3=Column(Time) content1 =Column(Text) content2 =Column(LONGTEXT) # Base.metadata.drop_all() # Base.metadata.create_all() #新增数据到表news中 # a1 = News(price1=1000.0078,price2=1000.0078,title='测试数据',is_delete=True, tag1="PYTHON",tag2=TagEnum.flask,create_time1=date(2018,12,12), create_time2=datetime(2019,2,20,12,12,30),create_time3=time(hour=11,minute=12,second=13),content1="hello",content2 ="hello hi nihao") a1 = News(price1=1000.0078,price2=1000.0078,title='测试数据', is_delete=False,tag1="PYTHON",tag2=TagEnum.python, create_time1=date(2018,12,12),create_time2=datetime(2019,2,20,12,12,30), create_time3=time(hour=11,minute=12,second=13),content1="hello",content2 ="hello hi nihao") session.add(a1) session.commit()

    SQLAlchemy的常用参数:

    primary_key:True设置某个字段为主键。autoincrement:True设置这个字段为自动增长的。default:设置某个字段的默认值。在发表时间这些字段上面经常用。nullable:指定某个字段是否为空。默认值是True,就是可以为空。unique:指定某个字段的值是否唯一。默认是False。onupdate:在数据更新的时候会调用这个参数指定的值或者函数。在第一次插入这条数据的时候,不会用onupdate的值,只会使用default的值。常用于是update_time字段(每次更新数据的时候都要更新该字段值)。name:指定ORM模型中某个属性映射到表中的字段名。如果不指定,那么会使用这个属性的名字来作为字段名。如果指定了,就会使用指定的这个值作为表字段名。这个参数也可以当作位置参数,在第1个参数来指定。 title = Column(String(50),name=‘title’,nullable=False) title = Column(‘my_title’,String(50),nullable=False

    query函数可传递的参数:

    模型名。指定查找这个模型中所有的属性(对应查询表为全表查询)。模型中的属性。可以指定只查找某个模型的其中几个属性。聚合函数。 func.count:统计行的数量。func.avg:求平均值。func.max:求最大值。func.min:求最小值。func.sum:求和。 func上,其实没有任何聚合函数。但是因为他底层做了一些魔术,只要mysql中有的聚合函数,都可以通过func调用。

    创建外键:

    uid = Colum(integer,ForeignKey('user.id',ondelete='RESTRICT'))

    外键查询:

    class User(Base): __tablename__ = 'user' id = Column(Integer,primary_key=True,autoincrement=True) uname = Column(String(50),nullable=False) # newss=relationship("News") #这种写法不是最优的,通常会把它通过反向声明的方式写在“多”的那一方 def __repr__(self): return "<User(uname:%s)>" % self.uname class News(Base): __tablename__ = 'news' id = Column(Integer,primary_key=True,autoincrement=True) title = Column(String(50),nullable=False) content = Column(Text,nullable=False) #外键 uid = Column(Integer,ForeignKey("user.id")) #正向author = relationship("User") #正向 和 反向在一起 表明两个模型之间的关系 author = relationship("User",backref="newss") def __repr__(self): return "<News(title:%s,content=%s)>" % (self.title,self.content) # Base.metadata.drop_all() # Base.metadata.create_all() #需求1:查询 第一篇新闻的 作者是谁 # news= session.query(News).first() # print(news) # print(news.uid) #1 # user = session.query(User).get(news.uid) # print(user.uname) #上述的需求 能够被实现 但是太麻烦,引入relationship进行查询优化 # news = session.query(News).first() # print(news.author) # print(news.author.uname) #需求2:查询xx作者的所有文章 user = session.query(User).first() print(user.newss)
    Processed: 0.012, SQL: 9