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

Django中如何将Services模型数据复制到ServiceSelect并关联Proposal?

问题解决方案

核心错误原因

  1. ServiceSelect模型的save方法错误调用了Services类的父类方法,导致实例保存逻辑异常。
  2. 表单尝试将Services实例(或其ID)直接赋值给Proposal的services字段,但该字段要求关联ServiceSelect实例,类型不匹配。
  3. 表单的验证方法命名错误,未正确处理选中的服务数据。

1. 修复ServiceSelect模型的save方法

原代码中父类调用错误,需替换为对应ServiceSelect类的父类方法:

class ServiceSelect(models.Model):
    # 字段定义保持不变
    title = models.CharField(null=True, blank=True, max_length=100)
    discription = models.TextField(null=True, blank=True)
    category = models.CharField(choices=CATEGORY, blank=True, max_length=100)
    quantity = models.FloatField(null=True, blank=True)
    price = models.FloatField(null=True, blank=True)
    paymentTerms = models.CharField(choices=PAYMENT, blank=True, max_length=100)
    salesTax = models.CharField(choices=SALESTAX, blank=True, max_length=100)
    uniqueId = models.CharField(null=True, blank=True, max_length=100)
    slug = models.SlugField(max_length=500, unique=True, blank=True, null=True)
    date_created = models.DateTimeField(blank=True, null=True)
    last_updated = models.DateTimeField(blank=True, null=True)

    def __str__(self):
        return '{} {}'.format(self.title, self.uniqueId)
    
    def get_absolute_url(self):
        return reverse('product-detail', kwargs={'slug': self.slug})
    
    def save(self, *args, **kwargs):
        if self.date_created is None:
            self.date_created = timezone.localtime(timezone.now())
        if self.uniqueId is None:
            self.uniqueId = str(uuid4()).split('-')[4]
            self.slug = slugify('{} {}'.format(self.title, self.uniqueId))
    
        self.slug = slugify('{} {}'.format(self.title, self.uniqueId))
        self.last_updated = timezone.localtime(timezone.now())
    
        # 修复父类调用
        super(ServiceSelect, self).save(*args, **kwargs)

2. 重构ServiceSelectForm表单

调整表单逻辑,专注于选择服务模板并创建ServiceSelect实例:

class ServiceSelectForm(forms.Form):
    services = forms.ChoiceField(
        label='Choose services to add:',
        widget=forms.Select(attrs={'class': 'form-control mb-3'}),
    )

    def __init__(self, *args, **kwargs):
        self.request = kwargs.pop('request')
        super().__init__(*args, **kwargs)
        
        # 动态生成服务选项
        service_list = Services.objects.filter(team=self.request.team)
        service_choices = [('-----', '--Select Services--')] + [(s.uniqueId, s.title) for s in service_list]
        self.fields['services'].choices = service_choices

    def clean_services(self):
        selected_id = self.cleaned_data['services']
        if selected_id == '-----':
            raise forms.ValidationError('Please select a service template')
        try:
            return Services.objects.get(uniqueId=selected_id)
        except Services.DoesNotExist:
            raise forms.ValidationError('Invalid service selected')

    def save(self, proposal):
        # 从模板复制数据创建ServiceSelect实例
        service_template = self.cleaned_data['services']
        service_select = ServiceSelect.objects.create(
            title=service_template.title,
            discription=service_template.discription,
            category=service_template.category,
            quantity=service_template.quantity,
            price=service_template.price,
            paymentTerms=service_template.paymentTerms,
            salesTax=service_template.salesTax,
        )
        # 关联到Proposal
        proposal.services = service_select
        proposal.save()
        return service_select

3. 调整视图逻辑

修改视图中表单处理流程,调用重构后的表单方法:

# GET请求初始化表单
if request.method == 'GET':
    service_form = ServiceSelectForm(request=request)

# POST请求处理表单提交
elif request.method == 'POST':
    service_form = ServiceSelectForm(request.POST, request=request)
    if service_form.is_valid() and 'service' in request.POST:
        service_form.save(proposal)
        messages.success(request, "Service added to Proposal")
        return redirect('proposals:build_proposal', team_slug=team.slug, slug=slug)

补充说明

  • 确保Proposal模型的services字段为ForeignKey(ServiceSelect, on_delete=models.CASCADE, ...)类型,才能正确关联ServiceSelect实例。
  • 若需支持批量添加服务,可将Proposal的services字段改为ManyToManyField(ServiceSelect, ...),并调整表单和视图逻辑处理批量创建。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.15 00:50:21