xpanta
xpanta

Reputation: 8418

Django, can't save ManyToMany field from admin interface using save() method

I am trying to save/create a ManyToMany relation while saving my model. This is my code:

class Invoice(models.Model):
    name = models.CharField(max_length=64)
    product_ids = models.CharField(max_length=128, default='')
    products = models.ManyToManyField(Product, null=True, blank=True)

    def save(self, *args, **kwargs):
        super(Invoice, self).save(*args, **kwargs)
        if self.product_ids:
            ids = self.product_ids.split(',')
            for id in ids:
               product = Product.objects.get(id=id)
               self.products.add(product)
            super(AllAroundCompetition, self).save(*args, **kwargs)

It is self explanatory. Sometimes I would like to create the invoice manually and I don't want to browse the whole list while pressing "ctrl" in order to select many products. So I have added a dumb extra field to pass ids in a comma separated manner (eg 25,27,90,20). This is product_ids field. I overrode the save() method to create these M2M relations. But it doesn't work. Invoice is created, but the relations are not.

What am I doing wrong?

(using django 1.5)

Upvotes: 0

Views: 648

Answers (2)

phoenix
phoenix

Reputation: 235

M2M relation changes are saved in separate table in database. Therefore save method saves only model itself, relation is saved after changes to model has been made in proper table in database, e.g. when creating new model it has to be created in database (save method) to add M2M foreign keys.

TL;DR: Try overriding method ModelAdmin.save_related(self, request, form, formsets, change) (you may have to create model admin class for this model and registering it)

Upvotes: 1

sk1p
sk1p

Reputation: 6725

You are calling super(AllAroundCompetition, self).save(…) at the bottom of your method. Adding an object to an m2m relation shouldn't need a save call afterwards at all, and calling save of another class might mess things up.

I suggest an alternative solution at all: try using a filter_horizontal for your products field in the admin. It has a nicer user interface, including search. No more ctrl-holding neccessary:

class InvoiceAdmin(admin.ModelAdmin):
    filter_horizontal = ('products',)

Upvotes: 1

Related Questions