Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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
like image 674
Calin Don Avatar asked Feb 13 '13 16:02

Calin Don


People also ask

What is Save () in Django?

The . save() method is used to write a model instance to the database. It can be an existing record or even a new one. For an existing record, Django will run a SQL UPDATE statement on the database. For a new record, Django will run an INSERT.

What is Inlines in Django admin?

The admin interface is also customizable in many ways. This post is going to focus on one such customization, something called inlines. When two Django models share a foreign key relation, inlines can be used to expose the related model on the parent model page. This can be extremely useful for many applications.

Does Django save call clean?

save() calls the clean. This way the integrity is enforced both from forms and from other calling code, the command line, and tests. Without this, there is (AFAICT) no way to write a test that ensures that a model has a FK relation to a specifically chosen (not default) other model.

How do you remove save and add another Django admin?

The simplest option is to set save_as=True on the ModelAdmin . This will replace the "Save and add another" button with a "Save as new" button.


2 Answers

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
like image 176
ccrisan Avatar answered Oct 16 '22 01:10

ccrisan


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

like image 37
Nicholas Woodward Avatar answered Oct 16 '22 02:10

Nicholas Woodward