You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Django中动态设置表单提交的URL?

Django动态设置表单Action URL解决重定向错误

我用下面的模板语法设置更新商品/自定义订单数量的表单action URL:

"{% if custom_orders %}{% url 'custom_order_update' item.custom_order.id %}{% else %}{% url 'update_bag' item.product_id %}{% endif %}"

但Django抛出重定向错误,求其他动态设置URL的方法。


相关代码

bag应用 urls.py

from django.urls import path
from . import views

urlpatterns = [
    path('', views.view_bag, name='view_bag'),
    path('add/<product_id>/', views.add_to_bag, name='add_to_bag'),
    path('update/<product_id>/', views.update_bag, name='update_bag'),
    path('remove/<product_id>/', views.remove_from_bag, name='remove_from_bag'),
]

bag应用 views.py

from django.shortcuts import render, redirect, reverse

def view_bag(request):
    """
    A view to return bag page
    """
    bag = request.session.get('bag', {})
    custom_orders = bag.get('custom_orders', [])

    context = {
        'custom_orders': custom_orders
    }
    return render(request, 'bag/bag.html', context)


def add_to_bag(request, product_id):
    """ Add a quantity of the specified product to the shopping bag """
    quantity = int(request.POST.get('quantity'))
    redirect_url = request.POST.get('redirect_url')
    
    bag = request.session.get('bag', {})

    if product_id in list(bag.keys()):
        bag[product_id] += quantity
    else:
        bag[product_id] = quantity

    request.session['bag'] = bag
    return redirect(redirect_url)


def update_bag(request, product_id):
    bag = request.session.get('bag', {})
    quantity = int(request.POST.get('quantity'))
    print(quantity)
            
    if quantity > 0:
        bag[product_id] = quantity
    else:
        bag.pop(product_id, None)
    request.session['bag'] = bag

    return redirect(reverse('view_bag'))

def remove_from_bag(request, product_id):
    bag = request.session.get('bag', {})
    bag.pop(product_id)
    request.session['bag'] = bag
    return redirect(reverse('view_bag'))

custom_order应用 urls.py

from django.urls import path
from . import views

urlpatterns = [
    path('', views.custom_order, name='custom_order'),
    path('price/<int:custom_order_id>/', views.custom_order_details, name='price'),
    path('add_custom_order/<int:custom_order_id>/', views.add_custom_order_to_bag, name='add_custom_order'),
    path('update_custom_order/<int:custom_order_id>/', views.custom_order_update, name='custom_order_update'),
    path('delete_custom_order/<int:custom_order_id>/', views.custom_order_delete, name='custom_order_delete'),
]

custom_order应用 views.py

from django.shortcuts import render, redirect, reverse, get_object_or_404
from .models import CustomOrder
from profiles.models import UserProfile
from .forms import CustomOrderForm
from django.contrib import messages


def custom_order(request):
    """ A view to display custom_order form """
    if request.method == 'POST':
        custom_form = CustomOrderForm(request.POST)
        if custom_form.is_valid():
            custom = custom_form.save(commit=False)
            user_profile = UserProfile.objects.get(user=request.user)
            custom.user_profile = user_profile
            custom.save()
            custom_order_id = custom.pk
            messages.success(request, f'Your custom order has been added to your bag. Price: Eur{custom.price}')
            return redirect(reverse('price', args=[custom_order_id]))
    else:
        custom_form = CustomOrderForm()

    context = {
        'custom_form': custom_form,
    }

    return render(request, 'custom_order/custom_order.html', context)

 
def custom_order_details(request, custom_order_id):
    user_profile = UserProfile.objects.get(user=request.user)
    custom_order = CustomOrder.objects.get(pk=custom_order_id, user_profile=user_profile)
    price = custom_order.price
    context = {
        'price': price,
        'custom_order': custom_order,
        'custom_order.id': custom_order_id
    }

    return render(request, 'custom_order/custom_order_price.html', context)

def add_custom_order_to_bag(request, custom_order_id):
    """Add custom order to the shopping bag """
    custom_order = CustomOrder.objects.get(pk=custom_order_id)
    bag = request.session.get('bag', {})

    if 'custom_orders' not in bag:
        bag['custom_orders'] = []

    bag['custom_orders'].append(custom_order_id)
   
    request.session['bag'] = bag
    return redirect(reverse('view_bag'))


def custom_order_update(request, custom_order_id):
    """ Updates the quantity of custom orders added to the bag"""
    bag = request.session.get('bag', {})
    quantity = int(request.POST.get('quantity'))
    custom_orders = bag.get('custom_orders', [])
    found = False
    
    for quantity_dict in custom_orders:
        if isinstance(quantity_dict, dict) and str(custom_order_id) in quantity_dict:
            quantity_dict[str(custom_order_id)] = quantity
            found = True
            break
    
    if not found:
        custom_orders.append({str(custom_order_id): quantity})
        custom_orders.remove(custom_order_id)


    bag['custom_orders'] = custom_orders

    request.session['bag'] = bag

    return redirect(reverse('view_bag'))

def custom_order_delete(request, custom_order_id):
    bag = request.session.get('bag', {})
    custom_orders = bag.get('custom_orders', [])
    for quantity_dict in custom_orders:
        if isinstance(quantity_dict, dict) and str(custom_order_id) in quantity_dict:
            custom_orders.remove(quantity_dict)
        else:
            custom_orders.remove(custom_order_id)
    request.session['bag'] = bag
    return redirect(reverse('view_bag'))

解决方案

问题根源

原模板判断逻辑用了全局的custom_orders列表,只要购物车里有自定义订单,所有表单都会指向custom_order_update,不符合单个item的类型判断逻辑,导致URL参数错误(比如商品item没有custom_order.id),触发重定向错误。

方法一:视图层预生成每个item的action URL

修改view_bag视图,为购物车内的每个商品和自定义订单生成对应更新URL,传递到模板:

def view_bag(request):
    bag = request.session.get('bag', {})
    custom_orders = bag.get('custom_orders', [])
    
    bag_items = []
    # 处理普通商品
    for product_id, quantity in bag.items():
        if product_id != 'custom_orders':
            from products.models import Product
            product = get_object_or_404(Product, id=product_id)
            update_url = reverse('update_bag', args=[product_id])
            bag_items.append({
                'type': 'product',
                'item': product,
                'quantity': quantity,
                'update_url': update_url
            })
    
    # 处理自定义订单
    for order_entry in custom_orders:
        if isinstance(order_entry, dict):
            custom_order_id = next(iter(order_entry.keys()))
            quantity = order_entry[custom_order_id]
        else:
            custom_order_id = order_entry
            quantity = 1
        custom_order = get_object_or_404(CustomOrder, id=custom_order_id)
        update_url = reverse('custom_order_update', args=[custom_order_id])
        bag_items.append({
            'type': 'custom',
            'item': custom_order,
            'quantity': quantity,
            'update_url': update_url
        })
    
    context = {
        'bag_items': bag_items
    }
    return render(request, 'bag/bag.html', context)

模板直接使用预生成的URL:

<form method="POST" action="{{ item.update_url }}">
    {% csrf_token %}
    <input type="number" name="quantity" value="{{ item.quantity }}" min="1">
    <button type="submit">更新数量</button>
</form>

方法二:模板内针对单个item类型判断

确保模板能区分当前item是普通商品还是自定义订单(需在视图处理时为item添加类型标识):

<form method="POST" action="{% if item.type == 'custom' %}{% url 'custom_order_update' item.item.id %}{% else %}{% url 'update_bag' item.item.id %}{% endif %}">
    {% csrf_token %}
    <input type="number" name="quantity" value="{{ item.quantity }}" min="1">
    <button type="submit">更新数量</button>
</form>

方法三:统一更新视图(推荐)

创建通用更新视图,统一处理两种类型的更新,减少重复代码:

在bag应用urls.py添加:

path('update/', views.update_bag_or_custom, name='update_bag_or_custom'),

在bag应用views.py添加:

def update_bag_or_custom(request):
    bag = request.session.get('bag', {})
    quantity = int(request.POST.get('quantity'))
    item_type = request.POST.get('item_type')
    item_id = request.POST.get('item_id')
    
    if item_type == 'product':
        if quantity > 0:
            bag[item_id] = quantity
        else:
            bag.pop(item_id, None)
    elif item_type == 'custom':
        custom_orders = bag.get('custom_orders', [])
        found = False
        for entry in custom_orders:
            if isinstance(entry, dict) and str(item_id) in entry:
                if quantity > 0:
                    entry[str(item_id)] = quantity
                else:
                    custom_orders.remove(entry)
                found = True
                break
        if not found and quantity > 0:
            custom_orders.append({str(item_id): quantity})
        bag['custom_orders'] = custom_orders
    
    request.session['bag'] = bag
    return redirect(reverse('view_bag'))

模板表单修改为:

<!-- 普通商品表单 -->
<form method="POST" action="{% url 'update_bag_or_custom' %}">
    {% csrf_token %}
    <input type="hidden" name="item_type" value="product">
    <input type="hidden" name="item_id" value="{{ item.item.id }}">
    <input type="number" name="quantity" value="{{ item.quantity }}" min="1">
    <button type="submit">更新数量</button>
</form>

<!-- 自定义订单表单 -->
<form method="POST" action="{% url 'update_bag_or_custom' %}">
    {% csrf_token %}
    <input type="hidden" name="item_type" value="custom">
    <input type="hidden" name="item_id" value="{{ item.item.id }}">
    <input type="number" name="quantity" value="{{ item.quantity }}" min="1">
    <button type="submit">更新数量</button>
</form>

这种方式简化了模板逻辑,统一了更新流程,更便于维护。


内容的提问来源于stack exchange,提问作者Leonie

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.29 02:17:03