Christian Frei
Christian Frei

Reputation: 481

Django save previous object from models

In a Django admin site, I have this class. I want to save a previous version of a object (Servers) which is a manytomany field to find changes on the object.

With normal CharField this work, but for manytomany fields I got this error:

"<SourceDestinationGroup: asdas>" needs to have a value for field "id" before this many-to-many relationship can be used.

here is my objectclass

class SourceDestinationGroup(models.Model):
    STATE_CHOICES = (
        ('C', 'in Change'),
        ('F', 'Finished')
        )
    ServerGroupName = models.CharField(max_length=256)
    Description = models.CharField(max_length=256,blank=True)
    Servers = models.ManyToManyField(Server)
    Status = models.CharField(max_length=1, choices=STATE_CHOICES, default='C')



    def __init__(self, *args, **kw):
        super(SourceDestinationGroup, self).__init__(*args, **kw)
        self._old_Servers = self.Servers

    def save(self, **kw):
       if self.Servers != self._old_Servers:
            self.Status = 'C'
            self._old_Servers = self.Servers

        super(SourceDestinationGroup, self).save(**kw)



   def __str__(self):
       return self.ServerGroupName

Upvotes: 11

Views: 769

Answers (4)

Christian Frei
Christian Frei

Reputation: 481

M2M relationships aren't saved as part of the save() method. In the admin, the main object is saved, and then the m2m relation is saved; so, by serializing the list of tags in the save method, you're printing the value of the tags before the new values have been saved. If you want to install "post m2m save" behavior, you'd need to override the update view on the admin itself.

Upvotes: 5

albar
albar

Reputation: 3100

I encountered this problem a few month ago. The m2m relationships are not updated inside the model save method, even if super() is called before using those relationships.

The only workaround I've found is to write a separate function that deals with m2m relationships, and call it from the save method of the form:

def save(self, *args, **kwargs):
    instance = super().save(*args, **kwargs)
    instance.update_m2m()
    return instance

In your case, this function is the nearly the same as your model save method:

def update_m2m(self):
    if self.Servers != self._old_Servers:
       self.Status = 'C'
       self._old_Servers = self.Servers
       self.save()

Upvotes: 3

ameunier
ameunier

Reputation: 156

You are trying to initialize the ManyToMany attribute (.Servers) when creating the object..

Wherever you try to create the SourceDestinationGroup, you should probably create it THEN do:

group.Servers = servers
group.save()

Or, remove the Servers attribute in the init method and reset it afterwards:

def __init__(self, *args, **kw):
    super(SourceDestinationGroup, self).__init__(*args, **kw)
    self._temp_Servers = self.Servers

def save(self, **kw):
    if self.id is None:
        self._temp_Servers = self.Servers
        self.Servers = None # Your 'Servers' attribute was still being set, hence raising an Exception in the super.save just after
    super(SourceDestinationGroup, self).save(**kw)
    if self._old_Servers is not None:
        self.Servers = self._temp_Servers 
        super(SourceDestinationGroup, self).save(**kw)

Upvotes: 4

GwynBleidD
GwynBleidD

Reputation: 20559

Your SourceDestinationGroup instance must be saved to database before you can add any servers to it. You probably can do an easy fix to it in your save method:

def save(self, **kw):
    if self.id is not None and self.Servers != self._old_Servers:
        self.Status = 'C'
        self._old_Servers = self.Servers

    super(SourceDestinationGroup, self).save(**kw)

As I can see, your change of server only makes sense when there are any old values.

Upvotes: 1

Related Questions