简体   繁体   English

Django在ModelForm中的“其他”模型上包含ManyToManyField

[英]Django Include ManyToManyField on "other" model in ModelForm

I would like to have a form with the preselected checkboxes of a ManyToManyField.我想要一个带有 ManyToManyField 的预选复选框的表单。

models.py模型.py

class Store(models.Model):
    ...

class Brand(models.Model):
   stores = models.ManyToManyField(Store, blank=True, related_name="brands")

forms.py表格.py

class StoreForm(ModelForm):

    class Meta:
        model = Store
        fields = ('brands',)

I get this exception:我得到这个例外:

django.core.exceptions.FieldError: Unknown field(s) (brands) specified for Store

I know that I can add the field manually to the class:我知道我可以手动将字段添加到类中:

brands = forms.ModelMultipleChoiceField(
         queryset=Brand.objects.all(),
         widget=forms.CheckboxSelectMultiple,
    )

If I do this the checkboxes are not preselected.如果我这样做,则不会预先选中复选框。

How is it possible to include the ManyToMany field from "the other side" of the model (from Store)?如何从模型的“另一侧”(来自 Store)包含 ManyToMany 字段?

One possibility is to define the field on the "other" model.一种可能性是在“其他”模型上定义字段。 So instead of writing this:所以,而不是写这个:

class Store(models.Model):
    ...

class Brand(models.Model):
   stores = models.ManyToManyField(Store, blank=True, related_name="brands")

You can write this:你可以这样写:

class Brand(models.Model):
...

class Store(models.Model):
    brands = models.ManyToManyField(Brand, blank=True, related_name="stores")

Or, if you have manually added the field to the form, you could populate its initial value in the form's __init__() method.或者,如果您手动将字段添加到表单中,您可以在表单的__init__()方法中填充其初始值。

@hedgie To change the field in the other model is not a good option for me because I use it already. @hedgie 更改另一个模型中的字段对我来说不是一个好选择,因为我已经在使用它了。

But the __init__() was a good hint.但是__init__()是一个很好的提示。 I come up with this solution and it seems to work.我想出了这个解决方案,它似乎有效。

class StoreForm(ModelForm):
    def __init__(self, *args, **kwargs):
        if kwargs.get('instance'):
            brand_ids = [t.pk for t in kwargs['instance'].brands.all()]

            kwargs['initial'] = {
                'brands': brand_ids,
            }
        super().__init__(*args, **kwargs)

    # https://stackoverflow.com/questions/49932426/save-many-to-many-field-django-forms
    def save(self, commit=True):
        # Get the unsaved Pizza instance
        instance = forms.ModelForm.save(self, False)

        # Prepare a 'save_m2m' method for the form,
        old_save_m2m = self.save_m2m

        def save_m2m():
            old_save_m2m()
            # This is where we actually link the pizza with toppings
            instance.brands.clear()
            for brand in self.cleaned_data['brands']:
                instance.brands.add(brand)

        self.save_m2m = save_m2m

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

        return instance

    brands = forms.ModelMultipleChoiceField(
         queryset=Brand.objects.all(),
         widget=forms.CheckboxSelectMultiple,
    )

Though it seems to be not very elegant.虽然看起来不是很优雅。 I wonder why django does not support a better way.我想知道为什么 django 不支持更好的方法。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM