Skip to main content
 首页 » 编程设计

python之Django之同一表单上的多个自定义模型

2024年09月03日37TianFang

我正在使用 Django 2.1 和 PostgreSQL。 我的问题是我正在尝试创建一个表单来同时编辑两个不同的模型。该模型与 FK 相关,我看到的每个示例都与用户和配置文件模型有关,但我无法复制我真正需要的东西。

我的模型经过简化以显示有关它们的相关信息是:

# base model for Campaigns. 
class CampaignBase(models.Model): 
    .... 
    project = models.ForeignKey(Project, on_delete=models.CASCADE) 
    creation_date = models.DateTimeField(auto_now_add=True) 
    start_date = models.DateTimeField(null=True, blank=True) 
    end_date = models.DateTimeField(null=True, blank=True) 
    .... 
 
# define investment campaign made on a project. 
class InvestmentCampaign(models.Model): 
    .... 
    campaign = models.ForeignKey(CampaignBase, on_delete=models.CASCADE, null=True, blank=True) 
 
    description = models.CharField( 
        blank=True, 
        max_length=25000, 
    ) 
    .... 

我要创建的表单包含 FK CampaignBase 的 end_date 和 的 Description >投资事件。

现在我有这个 UpdateView 来编辑 InvestmentCampaign,我需要适应我的实际需要,这也更新了 CampaignBase 模型:

class ProjectEditInvestmentCampaignView(LoginRequiredMixin, SuccessMessageMixin, generic.UpdateView): 
    template_name = 'webplatform/project_edit_investment_campaign.html' 
    model = InvestmentCampaign 
    form_class = CreateInvestmentCampaignForm 
    success_message = 'Investment campaign updated!' 
 
    def get_success_url(self): 
        return reverse_lazy('project-update-investment-campaign', args=(self.kwargs['project'], self.kwargs['pk'])) 
 
    # Make the view only available for the users with current fields 
    def dispatch(self, request, *args, **kwargs): 
        self.object = self.get_object() 
        # here you can make your custom validation for any particular user 
        if request.user != self.object.campaign.project.user: 
            raise PermissionDenied() 
        return super().dispatch(request, *args, **kwargs) 
 
    # Set field as current user 
    def form_valid(self, form): 
        campaign = InvestmentCampaign.objects.get(pk=self.kwargs['campaign']) 
        form.instance.campaign = campaign 
        form.instance.history_change_reason = 'Investment campaign updated' 
        return super(ProjectEditInvestmentCampaignView, self).form_valid(form) 
 
    def get_context_data(self, **kwargs): 
        project = Project.objects.get(pk=self.kwargs['project']) 
        context = super(ProjectEditInvestmentCampaignView, self).get_context_data(**kwargs) 
        context['project'] = project 
        return context 

我的表格是:

class CreateCampaignBaseForm(forms.ModelForm): 
    class Meta: 
        model = CampaignBase 
        fields = ('end_date',) 
        widgets = { 
            'end_date': DateTimePickerInput(), 
        } 
 
    def __init__(self, *args, **kwargs): 
        # first call parent's constructor 
        super(CreateCampaignBaseForm, self).__init__(*args, **kwargs) 
        # evade all labels and help text to appear when using "as_crispy_tag" 
        self.helper = FormHelper(self) 
        self.helper.form_show_labels = False 
        self.helper._help_text_inline = True 
 
 
class CreateInvestmentCampaignForm(forms.ModelForm): 
    class Meta: 
        model = InvestmentCampaign 
        fields = ('description') 
        widgets = { 
            'description': SummernoteWidget(attrs={'summernote': { 
                'placeholder': 'Add some details of the Investment Campaign here...'}}), 
        } 
 
    def __init__(self, *args, **kwargs): 
        # first call parent's constructor 
        super(CreateInvestmentCampaignForm, self).__init__(*args, **kwargs) 
        # evade all labels and help text to appear when using "as_crispy_tag" 
        self.helper = FormHelper(self) 
        self.helper.form_show_labels = False 
        self.helper._help_text_inline = True 

我到处都读到,执行此操作的最佳方法是使用基于函数的 View ,并调用我拥有的每个表单,然后进行验证。问题是我不知道如何用两种形式的正确对象填充字段,而且,我不知道如何做与 get_context_data 等效的操作,也不知道如何获取 self执行与 get_success_url 等效的参数(因为基于函数的 View 我只有请求属性,所以我无法访问 kwargs)。

我见过一些人使用 django-betterforms ,但同样,唯一的例子是 auth 和 profile 模型,我看不到用我自己的模型复制它的方法。

非常感谢。

请您参考如下方法:

如果您只想更改 BaseCampaign 上的一个字段 end_date,那么您应该只使用一种形式。只需在 CreateInvestmentCampaignForm 和 form.valid() 中添加 end_date 作为附加字段(例如 forms.DateTimeField()) 方法,保存表单后,设置关联事件的值:

def form_valid(self, form): 
    inv_campaign = form.save(commit=False) 
    inv_campaign.campaign.end_date = form.cleaned_data['end_date'] 
    inv_campaign.campaign.save() 
    inv_campaign.history_change_reason = ... 
    return super().form_valid(form) 

以下是将 end_date 添加到您的表单并正确初始化它的方法:

class CreateInvestmentCampaignForm(ModelForm): 
    end_date = forms.DateTimeField(blank=True) 
 
    class Meta: 
        model = InvestmentCampaign 
        fields = ('description') 
 
    def __init__(self, *args, **kwargs): 
        super().__init__(*args, **kwargs) 
        if self.instance.campaign: 
            self.fields['end_date'].initial = self.instance.campaign.end_date