Django Oscar中重写ShippingMethodView的get()方法失效问题排查
Django Oscar重写ShippingMethodView的get()方法未生效问题
问题描述
已成功重写Django Oscar的ShippingMethodView类,但重写的get()方法未生效。需求背景:当仅存在1种配送方式时,系统默认跳转到支付页面,需强制展示配送方式页面,让客户查看配送费用后再进入支付环节。
相关代码如下:
apps.py
import oscar.apps.checkout.apps as apps from oscar.core.loading import get_class from django.urls import path class CheckoutConfig(apps.CheckoutConfig): name = 'apps.checkout' def ready(self): self.shipping_method_view = get_class('checkout.views', 'ShippingMethodViewOverwritten') super().ready() def get_urls(self): urls = super(CheckoutConfig, self).get_urls() urls += [ path('shipping-method/', self.shipping_method_view.as_view(), name='shipping-method'), ] return urls
views.py
from oscar.apps.checkout import views from oscar.core.loading import get_class ShippingAddressForm, ShippingMethodForm, GatewayForm \ = get_classes('checkout.forms', ['ShippingAddressForm', 'ShippingMethodForm', 'GatewayForm']) from django.contrib import messages Repository = get_class('shipping.repository', 'Repository') class ShippingMethodViewOverwritten(views.ShippingMethodView): template_name = 'oscar/checkout/shipping_methods.html' form_class = ShippingMethodForm pre_conditions = ['check_basket_is_not_empty', 'check_basket_is_valid', 'check_user_email_is_captured'] success_url = reverse_lazy('checkout:payment-method') def get(self, request, *args, **kwargs): if not request.basket.is_shipping_required(): self.checkout_session.use_shipping_method( NoShippingRequired().code) return self.get_success_response() if not self.checkout_session.is_shipping_address_set(): messages.error(request, _("Please choose a shipping address")) return redirect('checkout:shipping-address') self._methods = self.get_available_shipping_methods() if len(self._methods) == 0: # No shipping methods available for given address messages.warning(request, _( "Shipping is unavailable for your chosen address - please " "choose another")) return redirect('checkout:shipping-address') elif len(self._methods) == 1: # Only one shipping method - set this and redirect onto the next # step self.checkout_session.use_shipping_method(self._methods[0].code) return self.get_success_response() return super().get(request, *args, **kwargs) def get_available_shipping_methods(self): return Repository().get_shipping_methods(basket=self.request.basket, user=self.request.user,shipping_addr=self.get_shipping_address(self.request.basket), request=self.request)
问题原因及解决方案
1. apps.py配置错误
错误点:
- ready方法执行顺序颠倒:先获取自定义视图再调用父类
ready(),但Oscar的ready()会初始化核心加载逻辑,未执行父类方法就调用get_class可能导致视图加载异常。 - URL追加顺序错误:自定义路由被追加到原有URL列表末尾,而Oscar默认已经注册了
shipping-method路由,原有路由会优先匹配,导致自定义视图无法触发。
修正后的apps.py:
import oscar.apps.checkout.apps as apps from oscar.core.loading import get_class from django.urls import path class CheckoutConfig(apps.CheckoutConfig): name = 'apps.checkout' def ready(self): # 先执行父类ready方法,初始化核心加载逻辑 super().ready() self.shipping_method_view = get_class('checkout.views', 'ShippingMethodViewOverwritten') def get_urls(self): urls = super(CheckoutConfig, self).get_urls() # 将自定义路由插入到原有URL列表前面,确保优先匹配 custom_urls = [ path('shipping-method/', self.shipping_method_view.as_view(), name='shipping-method'), ] return custom_urls + urls
2. views.py逻辑错误及缺失导入
错误点:
- 核心逻辑矛盾:重写的
get()方法中,当只有1种配送方式时仍执行跳转支付页面的逻辑,和需求完全相反。 - 缺失必要导入:代码中使用了
NoShippingRequired但未导入,会导致运行时错误。
修正后的views.py:
from oscar.apps.checkout import views from oscar.core.loading import get_class, get_classes from django.contrib import messages from django.urls import reverse_lazy # 补充导入NoShippingRequired NoShippingRequired = get_class('shipping.methods', 'NoShippingRequired') ShippingAddressForm, ShippingMethodForm, GatewayForm \ = get_classes('checkout.forms', ['ShippingAddressForm', 'ShippingMethodForm', 'GatewayForm']) Repository = get_class('shipping.repository', 'Repository') class ShippingMethodViewOverwritten(views.ShippingMethodView): template_name = 'oscar/checkout/shipping_methods.html' form_class = ShippingMethodForm pre_conditions = ['check_basket_is_not_empty', 'check_basket_is_valid', 'check_user_email_is_captured'] success_url = reverse_lazy('checkout:payment-method') def get(self, request, *args, **kwargs): if not request.basket.is_shipping_required(): self.checkout_session.use_shipping_method( NoShippingRequired().code) return self.get_success_response() if not self.checkout_session.is_shipping_address_set(): messages.error(request, _("Please choose a shipping address")) return redirect('checkout:shipping-address') self._methods = self.get_available_shipping_methods() if len(self._methods) == 0: messages.warning(request, _( "Shipping is unavailable for your chosen address - please " "choose another")) return redirect('checkout:shipping-address') # 删除原有跳转逻辑,强制渲染配送方式页面 # elif len(self._methods) == 1: # self.checkout_session.use_shipping_method(self._methods[0].code) # return self.get_success_response() return super().get(request, *args, **kwargs) def get_available_shipping_methods(self): return Repository().get_shipping_methods( basket=self.request.basket, user=self.request.user, shipping_addr=self.get_shipping_address(self.request.basket), request=self.request )
内容的提问来源于stack exchange,提问作者César Augusto
相关产品推荐
相关产品推荐

