flask中的url_for

本文解析了Flask框架中url_for函数的工作原理,详细介绍了如何通过函数名称获取对应的URL,以及如何处理不定数量的具名参数。同时,探讨了在不同场景下如何生成外部URL和相对URL。

经常在flask代码中看到endpoint,翻译成中文是终点的意思?

不清楚,也不晓得什么意思,反正感觉是有点崩溃的。

直到看了url_for的源码才晓得。

def url_for(endpoint, **values):
    """Generates a URL to the given endpoint with the method provided.

    Variable arguments that are unknown to the target endpoint are appended
    to the generated URL as query arguments.  If the value of a query argument
    is ``None``, the whole pair is skipped.  In case blueprints are active
    you can shortcut references to the same blueprint by prefixing the
    local endpoint with a dot (``.``).

    This will reference the index function local to the current blueprint::

        url_for('.index')

    For more information, head over to the :ref:`Quickstart <url-building>`.

    To integrate applications, :class:`Flask` has a hook to intercept URL build
    errors through :attr:`Flask.url_build_error_handlers`.  The `url_for`
    function results in a :exc:`~werkzeug.routing.BuildError` when the current
    app does not have a URL for the given endpoint and values.  When it does, the
    :data:`~flask.current_app` calls its :attr:`~Flask.url_build_error_handlers` if
    it is not ``None``, which can return a string to use as the result of
    `url_for` (instead of `url_for`'s default to raise the
    :exc:`~werkzeug.routing.BuildError` exception) or re-raise the exception.
    An example::

        def external_url_handler(error, endpoint, values):
            "Looks up an external URL when `url_for` cannot build a URL."
            # This is an example of hooking the build_error_handler.
            # Here, lookup_url is some utility function you've built
            # which looks up the endpoint in some external URL registry.
            url = lookup_url(endpoint, **values)
            if url is None:
                # External lookup did not have a URL.
                # Re-raise the BuildError, in context of original traceback.
                exc_type, exc_value, tb = sys.exc_info()
                if exc_value is error:
                    raise exc_type, exc_value, tb
                else:
                    raise error
            # url_for will use this result, instead of raising BuildError.
            return url

        app.url_build_error_handlers.append(external_url_handler)

    Here, `error` is the instance of :exc:`~werkzeug.routing.BuildError`, and
    `endpoint` and `values` are the arguments passed into `url_for`.  Note
    that this is for building URLs outside the current application, and not for
    handling 404 NotFound errors.

    .. versionadded:: 0.10
       The `_scheme` parameter was added.

    .. versionadded:: 0.9
       The `_anchor` and `_method` parameters were added.

    .. versionadded:: 0.9
       Calls :meth:`Flask.handle_build_error` on
       :exc:`~werkzeug.routing.BuildError`.

    :param endpoint: the endpoint of the URL (name of the function)
    :param values: the variable arguments of the URL rule
    :param _external: if set to ``True``, an absolute URL is generated. Server
      address can be changed via ``SERVER_NAME`` configuration variable which
      defaults to `localhost`.
    :param _scheme: a string specifying the desired URL scheme. The `_external`
      parameter must be set to ``True`` or a :exc:`ValueError` is raised. The default
      behavior uses the same scheme as the current request, or
      ``PREFERRED_URL_SCHEME`` from the :ref:`app configuration <config>` if no
      request context is available. As of Werkzeug 0.10, this also can be set
      to an empty string to build protocol-relative URLs.
    :param _anchor: if provided this is added as anchor to the URL.
    :param _method: if provided this explicitly specifies an HTTP method.
    """
    appctx = _app_ctx_stack.top
    reqctx = _request_ctx_stack.top

    if appctx is None:
        raise RuntimeError(
            'Attempted to generate a URL without the application context being'
            ' pushed. This has to be executed when application context is'
            ' available.'
        )

    # If request specific information is available we have some extra
    # features that support "relative" URLs.
    if reqctx is not None:
        url_adapter = reqctx.url_adapter
        blueprint_name = request.blueprint

        if endpoint[:1] == '.':
            if blueprint_name is not None:
                endpoint = blueprint_name + endpoint
            else:
                endpoint = endpoint[1:]

        external = values.pop('_external', False)

    # Otherwise go with the url adapter from the appctx and make
    # the URLs external by default.
    else:
        url_adapter = appctx.url_adapter

        if url_adapter is None:
            raise RuntimeError(
                'Application was not able to create a URL adapter for request'
                ' independent URL generation. You might be able to fix this by'
                ' setting the SERVER_NAME config variable.'
            )

        external = values.pop('_external', True)

    anchor = values.pop('_anchor', None)
    method = values.pop('_method', None)
    scheme = values.pop('_scheme', None)
    appctx.app.inject_url_defaults(endpoint, values)

    # This is not the best way to deal with this but currently the
    # underlying Werkzeug router does not support overriding the scheme on
    # a per build call basis.
    old_scheme = None
    if scheme is not None:
        if not external:
            raise ValueError('When specifying _scheme, _external must be True')
        old_scheme = url_adapter.url_scheme
        url_adapter.url_scheme = scheme

    try:
        try:
            rv = url_adapter.build(endpoint, values, method=method,
                                   force_external=external)
        finally:
            if old_scheme is not None:
                url_adapter.url_scheme = old_scheme
    except BuildError as error:
        # We need to inject the values again so that the app callback can
        # deal with that sort of stuff.
        values['_external'] = external
        values['_anchor'] = anchor
        values['_method'] = method
        values['_scheme'] = scheme
        return appctx.app.handle_url_build_error(error, endpoint, values)

    if anchor is not None:
        rv += '#' + url_quote(anchor)
    return rv

虽说整体上处于看不明白的状态,但是至少懂了点。

根据其中对url_for的参数endpoint的解释

:param endpoint: the endpoint of the URL (name of the function)

我感觉意思就是函数的名字对应的url映射。

既然可以根据url找到相应的处理函数,那么根据处理函数,是不是也是可以同样找到对应的url呢?

答案当然就是肯定的。url_for就是用于满足这个功能的。

定义函数的时候使用**values表示的是,不定项的具名参数。

在函数内部,values就是一个字典了。

需要传入的参数呢包括endpoint需要的参数。

如果还有些不在endpoint只中呢,就是当成参数放在url的后面。

例如传入一个a=1,而a不属于endpoint中需要的参数

那么这个a=1就会在这个url_for产生的url后面添加如下部分

'?a=1'

from flask import Flask, render_template, request, redirect, url_for, flash from flask_sqlalchemy import SQLAlchemy from flask_login import LoginManager, UserMixin, login_user, login_required, logout_user, current_user from werkzeug.security import generate_password_hash, check_password_hash from datetime import datetime app = Flask(__name__) app.config['SECRET_KEY'] = 'your-secret-key' app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///shop.db' app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False db = SQLAlchemy(app) login_manager = LoginManager() login_manager.init_app(app) login_manager.login_view = 'login' # 用户模型 class User(UserMixin, db.Model): id = db.Column(db.Integer, primary_key=True) username = db.Column(db.String(80), unique=True, nullable=False) email = db.Column(db.String(120), unique=True, nullable=False) password_hash = db.Column(db.String(128)) orders = db.relationship('Order', backref='user', lazy=True) cart_items = db.relationship('CartItem', backref='user', lazy=True) def set_password(self, password): self.password_hash = generate_password_hash(password) def check_password(self, password): return check_password_hash(self.password_hash, password) # 商品模型 class Product(db.Model): id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(100), nullable=False) description = db.Column(db.Text) price = db.Column(db.Float, nullable=False) stock = db.Column(db.Integer, nullable=False) image_url = db.Column(db.String(200)) # 订单模型 class Order(db.Model): id = db.Column(db.Integer, primary_key=True) user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False) date_ordered = db.Column(db.DateTime, default=datetime.utcnow) status = db.Column(db.String(20), default='pending') # pending, completed, refunded items = db.relationship('OrderItem', backref='order', lazy=True) total_amount = db.Column(db.Float, nullable=False) def can_delete(self): """判断订单是否可以删除""" return self.status in ['pending', 'cancelled'] def can_deliver(self): """判断订单是否可以交付""" return self.status == 'pending' # 订单项模型 class OrderItem(db.Model): id = db.Column(db.Integer, primary_key=True) order_id = db.Column(db.Integer, db.ForeignKey('order.id'), nullable=False) product_id = db.Column(db.Integer, db.ForeignKey('product.id'), nullable=False) quantity = db.Column(db.Integer, nullable=False) price = db.Column(db.Float, nullable=False) product = db.relationship('Product') # 购物车项模型 class CartItem(db.Model): id = db.Column(db.Integer, primary_key=True) user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False) product_id = db.Column(db.Integer, db.ForeignKey('product.id'), nullable=False) quantity = db.Column(db.Integer, nullable=False) product = db.relationship('Product') @login_manager.user_loader def load_user(user_id): return User.query.get(int(user_id)) # 首页 - 商品展示 @app.route('/') def index(): products = Product.query.all() return render_template('index.html', products=products) # 用户个人信息 @app.route('/profile') @login_required def profile(): return render_template('profile.html') # 购物车 @app.route('/cart') @login_required def cart(): cart_items = CartItem.query.filter_by(user_id=current_user.id).all() total = sum(item.product.price * item.quantity for item in cart_items) return render_template('cart.html', cart_items=cart_items, total=total) # 订单历史 @app.route('/orders') @login_required def orders(): user_orders = Order.query.filter_by(user_id=current_user.id).all() return render_template('orders.html', orders=user_orders) # 添加到购物车 @app.route('/add_to_cart/<int:product_id>', methods=['POST']) @login_required def add_to_cart(product_id): quantity = int(request.form.get('quantity', 1)) cart_item = CartItem.query.filter_by(user_id=current_user.id, product_id=product_id).first() if cart_item: cart_item.quantity += quantity else: cart_item = CartItem(user_id=current_user.id, product_id=product_id, quantity=quantity) db.session.add(cart_item) db.session.commit() flash('商品已添加到购物车!') return redirect(url_for('cart')) # 创建订单 @app.route('/create_order', methods=['POST']) @login_required def create_order(): cart_items = CartItem.query.filter_by(user_id=current_user.id).all() if not cart_items: flash('购物车为空!') return redirect(url_for('cart')) total_amount = sum(item.product.price * item.quantity for item in cart_items) order = Order(user_id=current_user.id, total_amount=total_amount) db.session.add(order) for cart_item in cart_items: order_item = OrderItem( order=order, product_id=cart_item.product_id, quantity=cart_item.quantity, price=cart_item.product.price ) db.session.add(order_item) # 更新库存 product = cart_item.product product.stock -= cart_item.quantity db.session.delete(cart_item) db.session.commit() flash('订单创建成功!') return redirect(url_for('orders')) # 申请退款 @app.route('/refund/<int:order_id>', methods=['POST']) @login_required def refund_order(order_id): order = Order.query.get_or_404(order_id) if order.user_id != current_user.id: flash('无权限操作此订单!') return redirect(url_for('orders')) if order.status == 'completed': order.status = 'refunded' # 恢复库存 for item in order.items: product = item.product product.stock += item.quantity db.session.commit() flash('退款申请已提交!') else: flash('订单状态不允许退款!') return redirect(url_for('orders')) # 用户认证相关路由 @app.route('/login', methods=['GET', 'POST']) def login(): if current_user.is_authenticated: return redirect(url_for('index')) if request.method == 'POST': username = request.form.get('username') password = request.form.get('password') remember = 'remember' in request.form user = User.query.filter_by(username=username).first() if user and user.check_password(password): login_user(user, remember=remember) next_page = request.args.get('next') return redirect(next_page or url_for('index')) flash('用户名或密码错误') return render_template('login.html') @app.route('/register', methods=['GET', 'POST']) def register(): if current_user.is_authenticated: return redirect(url_for('index')) if request.method == 'POST': username = request.form.get('username') email = request.form.get('email') password = request.form.get('password') confirm_password = request.form.get('confirm_password') if password != confirm_password: flash('两次输入的密码不一致') return redirect(url_for('register')) if User.query.filter_by(username=username).first(): flash('用户名已存在') return redirect(url_for('register')) if User.query.filter_by(email=email).first(): flash('邮箱已被注册') return redirect(url_for('register')) user = User(username=username, email=email) user.set_password(password) db.session.add(user) db.session.commit() flash('注册成功,请登录') return redirect(url_for('login')) return render_template('register.html') @app.route('/logout') @login_required def logout(): logout_user() return redirect(url_for('index')) @app.route('/update_profile', methods=['POST']) @login_required def update_profile(): email = request.form.get('email') if email != current_user.email: if User.query.filter_by(email=email).first(): flash('邮箱已被使用') else: current_user.email = email db.session.commit() flash('个人信息更新成功') return redirect(url_for('profile')) @app.route('/change_password', methods=['POST']) @login_required def change_password(): current_password = request.form.get('current_password') new_password = request.form.get('new_password') confirm_password = request.form.get('confirm_password') if not current_user.check_password(current_password): flash('当前密码错误') elif new_password != confirm_password: flash('两次输入的新密码不一致') else: current_user.set_password(new_password) db.session.commit() flash('密码修改成功') return redirect(url_for('profile')) # 购物车相关路由 @app.route('/update_cart_item/<int:item_id>', methods=['POST']) @login_required def update_cart_item(item_id): cart_item = CartItem.query.get_or_404(item_id) if cart_item.user_id != current_user.id: flash('无权限操作此购物车项') return redirect(url_for('cart')) quantity = int(request.form.get('quantity', 1)) if quantity > cart_item.product.stock: flash('商品库存不足') else: cart_item.quantity = quantity db.session.commit() flash('购物车更新成功') return redirect(url_for('cart')) @app.route('/remove_from_cart/<int:item_id>', methods=['POST']) @login_required def remove_from_cart(item_id): cart_item = CartItem.query.get_or_404(item_id) if cart_item.user_id != current_user.id: flash('无权限操作此购物车项') return redirect(url_for('cart')) db.session.delete(cart_item) db.session.commit() flash('商品已从购物车中移除') return redirect(url_for('cart')) # 添加删除订单功能 @app.route('/cancel_order/<int:order_id>', methods=['POST']) @login_required def cancel_order(order_id): order = Order.query.get_or_404(order_id) # 验证订单属于当前用户 if order.user_id != current_user.id: flash('无权限操作此订单', 'danger') return redirect(url_for('orders')) # 检查订单状态是否可以取消 if not order.can_delete(): flash('订单当前状态不可取消', 'danger') return redirect(url_for('orders')) try: # 恢复库存 for item in order.items: product = item.product product.stock += item.quantity # 更新订单状态为已取消 order.status = 'cancelled' db.session.commit() flash('订单已成功取消', 'success') except Exception as e: db.session.rollback() flash('取消订单时出错: {}'.format(str(e)), 'danger') return redirect(url_for('orders')) # 添加管理员端订单交付功能 @app.route('/admin/deliver_order/<int:order_id>', methods=['POST']) @login_required def deliver_order(order_id): # 检查是否是管理员 if not current_user.is_authenticated or current_user.username != 'admin': flash('需要管理员权限', 'danger') return redirect(url_for('index')) order = Order.query.get_or_404(order_id) # 检查订单状态是否可以交付 if not order.can_deliver(): flash('订单当前状态不可交付', 'danger') return redirect(url_for('admin_orders')) try: # 更新订单状态为已完成 order.status = 'completed' db.session.commit() flash('订单已标记为已交付', 'success') except Exception as e: db.session.rollback() flash('交付订单时出错: {}'.format(str(e)), 'danger') return redirect(url_for('admin_orders')) # 添加管理员端订单删除功能 @app.route('/admin/delete_order/<int:order_id>', methods=['POST']) @login_required def admin_delete_order(order_id): # 检查是否是管理员 if not current_user.is_authenticated or current_user.username != 'admin': flash('需要管理员权限', 'danger') return redirect(url_for('index')) order = Order.query.get_or_404(order_id) # 检查订单状态是否可以删除 if not order.can_delete(): flash('订单当前状态不可删除', 'danger') return redirect(url_for('admin_orders')) try: # 如果是待处理订单,需要恢复库存 if order.status == 'pending': for item in order.items: product = item.product product.stock += item.quantity # 删除订单项 for item in order.items: db.session.delete(item) # 删除订单 db.session.delete(order) db.session.commit() flash('订单已成功删除', 'success') except Exception as e: db.session.rollback() flash('删除订单时出错: {}'.format(str(e)), 'danger') return redirect(url_for('admin_orders')) # 添加管理员端页面管理路由 @app.route('/admin/orders') @login_required def admin_orders(): # 检查是否是管理员 if not current_user.is_authenticated or current_user.username != 'admin': flash('需要管理员权限', 'danger') return redirect(url_for('index')) # 获取所有订单,按日期降序排列 orders = Order.query.order_by(Order.date_ordered.desc()).all() return render_template('admin_orders.html', orders=orders) # 添加订单详情页面路由 @app.route('/order/<int:order_id>') @login_required def order_details(order_id): order = Order.query.get_or_404(order_id) # 检查是否是订单所有者或管理员 if order.user_id != current_user.id and current_user.username != 'admin': flash('无权限查看此订单', 'danger') return redirect(url_for('index')) return render_template('order_details.html', order=order) if __name__ == '__main__': with app.app_context(): db.create_all() app.run(debug=True)
最新发布
05-29
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值