如何在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
相关产品推荐
相关产品推荐

