M2M使用通过和形成多个复选框

时间:2014-12-01 18:34:51

标签: django forms many-to-many

我想创建一个表单,允许我从这些模型中为供应商分配服务。由于我使用其他程序使用的DB,因此没有定义M2M关系,因此似乎无法更改它。我也可能错了。

class Service(models.Model):
    name = models.CharField(max_length=30L, blank=True) 


class ServiceUser(models.Model):
    service = models.ForeignKey(Service, null=False, blank=False)
    contact = models.ForeignKey(Contact, null=False, blank=False) 


class SupplierPrice(models.Model):
    service_user = models.ForeignKey('ServiceUser') 
    price_type = models.IntegerField(choices=PRICE_TYPES) 
    price = models.DecimalField(max_digits=10, decimal_places=4)

我已创建此表单:

class SupplierServiceForm(ModelForm):
    class Meta:
        services = ModelMultipleChoiceField(queryset=Service.objects.all())
        model = ServiceUser

        widgets = {
            'service': CheckboxSelectMultiple(),
            'contact': HiddenInput(),
       }

以下是我开始研究的观点,但没有取得任何成功:

class SupplierServiceUpdateView(FormActionMixin, TemplateView):

    def get_context_data(self, **kwargs):
        supplier = Contact.objects.get(pk=self.kwargs.get('pk'))
        service_user = ServiceUser.objects.filter(contact=supplier)

        form = SupplierServiceForm(instance=service_user)

        return {'form': form}

我觉得我尝试这样做的方式出了问题。我显示了正确的表单,但没有使用联系人进行实例化,即使供应商已经在service_user中有一些条目,也不会检查复选框。

2 个答案:

答案 0 :(得分:0)

您正在Meta类中定义服务。在SupplierServiceForm开始之后立即将它放在外面。至少它应该出现在那里。

编辑:

我误解了你的目标。您似乎想要显示一个只能有1个值的字段的多重选择。您的服务字段将无法存储多个服务。 因此,根据定义,您的ServiceUser只能有一个服务。 如果由于其他应用程序使用它而不想修改数据库,则可以创建与Service有多对多关系的另一个字段。这可能会导致使用旧字段与应用程序的其他部分发生冲突,但如果不修改关系,我就看不到其他方式了。

答案 1 :(得分:0)

我的问题的解决方案确实是在oder中重新定义我的模型,以使用through参数整合缺少的m2m关系。然后我必须使用特殊的init方法调整表单以在复选框中显示所有选定的服务,并使用特殊的save()方法使用m2m关系保存表单。

class Supplier(Contact):
    services = models.ManyToManyField('Service', through='SupplierPrice')


class Service(models.Model):
    name = models.CharField(max_length=30L, blank=True) 


class ServiceUser(models.Model):
    service = models.ForeignKey(Service, null=False, blank=False)
    supplier = models.ForeignKey(Supplier, null=False, blank=False)
    price = models.Decimal(max_digits=10, decimal_places=2, default=0)

这个表格改编自关于浇头和披萨的非常着名的帖子。

class SupplierServiceForm(ModelForm):

    class Meta:
        model = Supplier
        fields = ('services',)

        widgets = {
            'services': CheckboxSelectMultiple(),
            'contact_ptr_id': HiddenInput(),
            }

        services = ModelMultipleChoiceField(queryset=Service.objects.all(), required=False)

    def __init__(self, *args, **kwargs):
        # Here kwargs should contain an instance of Supplier
        if 'instance' in kwargs:
            # We get the 'initial' keyword argument or initialize it
            # as a dict if it didn't exist.
            initial = kwargs.setdefault('initial', {})
            # The widget for a ModelMultipleChoiceField expects
            # a list of primary key for the selected data (checked boxes).
            initial['services'] = [s.pk for s in kwargs['instance'].services.all()]

        ModelForm.__init__(self, *args, **kwargs)


    def save(self, commit=True):
        supplier = ModelForm.save(self, False)
        # Prepare a 'save_m2m' method for the form,

        def save_m2m():
            new_services = self.cleaned_data['services']
            old_services = supplier.services.all()

           for service in old_services:
               if service not in new_services:
                   service.delete()

           for service in new_services:
                if service not in old_services:
                    SupplierPrice.objects.create(supplier=supplier, service=service)
        self.save_m2m = save_m2m

        # Do we need to save all changes now?
        if commit:
            self.save_m2m()

        return supplier

这改变了我的第一个模型,并且会在我的旧数据库中弄得一团糟,但至少它可以工作。