Calin Don
Calin Don

Reputation: 865

Save the related objects before the actual object being edited on django admin

Is it possible to save the related objects before the actual object being edited on a django admin form?

For example:

in models.py

class Parent(model.Model):
    pass

class Child(model.Model):
    parent = models.ForeignKey(Parent)

@receiver(post_save,sender = Parent)
def notify_parent_save(sender, instance=None, **kwargs):
    print "Parent save"

@receiver(post_save,sender = Child)
def notify_child_save(sender, instance=None, **kwargs):
    print "Child saved"

in admin.py

class ChildInline(admin.TabularInline):
    model = Child
    extra = 1

class ParentsAdmin(admin.ModelAdmin):
    inlines = [ChildInline]

admin.site.register(Parent,ParentsAdmin)

Now, in django admin if I save a parent object, it will output on the console.

Parent save
Child save

I need this to happen in revese order:

Child save
Parent save

Upvotes: 18

Views: 11860

Answers (4)

Nicholas Woodward
Nicholas Woodward

Reputation: 121

I was having issues with the answers in this post, so I figured out a more concise answer. I was having an issue because using django-fsm, the other answers here would try to save the model multiple times (once for every formset) rather than once at the end.

def save_model(self, request, obj, form, change):
    if not obj.pk: # call super method if object has no primary key 
        super(YourAdmin, self).save_model(request, obj, form, change)
    else:
        pass # don't actually save the parent instance

def save_related(self, request, form, formsets, change):
    form.save_m2m()
    for formset in formsets:
        self.save_formset(request, form, formset, change=change)
    super(YourAdmin, self).save_model(request, form.instance, form, change)

This essential just flips the order of save_model and save_related as called in Django ModelAdmin source

Upvotes: 11

ecp
ecp

Reputation: 2429

ccrisan's answer brought me on the right track, but I think there is a flaw regarding save behavior of instances that do not yet exist in the database. In this case it's not possible to save the related objects first, because there is no foreign key that they can point to. For me the following extension did the trick:

class ParentAdmin(admin.ModelAdmin):
    inlines = [ChildInline]

    def save_model(self, request, obj, form, change):
        if not obj.pk: # call super method if object has no primary key 
            super(ParentAdmin, self).save_model(request, obj, form, change)
        else:
            pass # don't actually save the parent instance

    def save_formset(self, request, form, formset, change):
        formset.save() # this will save the children
        form.instance.save() # form.instance is the parent

Upvotes: 5

ccrisan
ccrisan

Reputation: 399

The following will save the children first:

class ParentAdmin(admin.ModelAdmin):
    inlines = [ChildInline]

    def save_model(self, request, obj, form, change):
        pass # don't actually save the parent instance

    def save_formset(self, request, form, formset, change):
        formset.save() # this will save the children
        form.instance.save() # form.instance is the parent

Upvotes: 22

Ponytech
Ponytech

Reputation: 1684

Depending on what you exactly want to do in your signals, can you just change the post_save to pre_save for the Child model ?

Upvotes: 0

Related Questions