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

4πŸ‘

βœ…

@hedgie To change the field in the other model is not a good option for me because I use it already.

But the __init__() was a good hint. 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.

πŸ‘€moritz_h

1πŸ‘

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.

Leave a comment