Django 2.1中CreateView的get_success_url因后续创建对象失效问题
解决Django CreateView中重定向URL依赖后续创建对象的问题
你的问题根源在于调用super().form_valid(form)的时机不对:这个方法会自动保存InvestmentCampaign实例到数据库,但此时你还没创建并关联CampaignBase对象,导致get_success_url尝试访问self.object.campaign.pk时,campaign属性还是None,触发AttributeError。
解决方案:调整逻辑顺序,先创建关联对象再保存表单
正确的流程应该是:先创建CampaignBase,把它关联到表单实例,再调用父类的form_valid完成保存,这样self.object在get_success_url中就已经包含完整的关联对象了。
修改后的代码如下:
class ProjectCreateInvestmentCampaignView(LoginRequiredMixin, SuccessMessageMixin, generic.CreateView): template_name = 'webplatform/project_edit_investment_campaign.html' model = InvestmentCampaign form_class = CreateInvestmentCampaignForm success_message = 'Investment campaign created!' def get_success_url(self): # 现在self.object.campaign已经存在,可以正常获取pk return reverse_lazy( 'project-update-investment-campaign', args=(self.kwargs['pk'], self.object.campaign.pk, self.object.pk) ) def form_valid(self, form): project = Project.objects.get(pk=self.kwargs['pk']) # 1. 先创建CampaignBase对象,直接设置必要字段 campaign = CampaignBase.objects.create( project=project, creation_date=timezone.now() ) # 2. 给表单实例赋值所有必要字段,包括关联刚创建的campaign form.instance.investment_type = "A" form.instance.contract_type = "CI" form.instance.history_change_reason = 'Investment campaign created' form.instance.campaign = campaign # 关键:提前关联campaign # 3. 最后调用父类方法,完成表单保存和后续重定向 return super().form_valid(form)
为什么原来的代码会报错?
原来的代码执行顺序是:
- 调用
super().form_valid(form):这一步会触发form.save(),把还没有campaign属性的InvestmentCampaign保存到数据库,同时self.object被设置为这个未完整的实例。 - 然后才创建
CampaignBase并关联到form.instance,再调用form.save()更新数据库。
但get_success_url是在super().form_valid(form)的执行过程中被调用的,此时self.object还没有campaign属性,自然会出现'NoneType' object has no attribute 'pk'的错误。
额外优化点
- 你原来代码中
form.instance.campaign.project = project是多余的,因为创建CampaignBase时已经指定了project=project。 - 如果
Project对象可能不存在,建议用get_object_or_404(Project, pk=self.kwargs['pk'])替代get(),避免抛出DoesNotExist异常。
内容的提问来源于stack exchange,提问作者Pol Frances
相关产品推荐
相关产品推荐

