shining
shining

Reputation: 1069

How to save related model instances before the model instance in django?

How to save the related model instances before the instance model.

This is necessary because I want to preprocess the related model's instance field under model instance save method.

I am working on Django project, and I am in a situation, that I need to run some function, after all the related models of instance get saved in the database.

Let say I have a model

models.py

from . import signals
class Video(models.Model):
    """Video model"""

    title = models.CharField(
        max_length=255,
        )

    keywords = models.ManyToManyField(
        KeyWord,
        verbose_name=_("Keywords")
    )

When the new instance of video model is created.

I need to 1. All the related models get saved first. a. If the related models are empty return empty or None 2. and then Save this video instance.

I tried to do it using post_save signals, but couldn't succeed as there is no guarantee that related models get saved first that the model.

from django.db.models.signals import post_save, pre_delete, m2m_changed
from django.dispatch import receiver

from .models import Video


@receiver(m2m_changed, sender=Video)
@receiver(post_save, sender=Video)
def index_or_update_video(sender, instance, **kwargs):
    """Update or create an instance to search server."""
    # TODO: use logging system
    # Grab the id
    print("Id is", instance.id)
    # Keywords is empty as keyword instance is saved later than this instace.
    keywords = [keyword.keyword for keyword in instance.keywords.all()]
    print(keywords) # [] empty no keywords
    instance.index()


@receiver(pre_delete, sender=Video)
def delete_video(sender, instance, **kwargs):
    print("Delete index object")
    instance.delete()

Update:

Can be implemented by grabbing the post_save signals and wait unitls its related models get saved in db, when the related_models get saved start serialization process and create flat json file along with the models fields and its related instance so, the flat json file can index into elastic search server.

And the question aries, how much time should we wait in signal handler method? and how to know all instance related fields got saved in db.

class Video(models.Model):
    def save(self, *args, **kwargs): 
        # 1. Make sure all of its related items are saved in db 
        # 2. Now save this instance in db. 
        # 3. If the model has been saved. Serialize its value, 
        # 4. Serailize its related models fields 
        # 5. Save all the serialized data into index server 

        # The advantage of using this is the data are indexed in real 
        # time to index server. 

        # I tired to to implement this logic using signals, in case of 
        # signals, when the instance get saved, its related models are 
        # not instantly available in the databse. 

       # Other solution could be, grab the `post_save` signals, wait(delay
       # the serialization process) and start the serialization of 
       # instance model and it's related to convert the data to flat json 
       # file so, that it could index in the searching server(ES) in real 
       # time. 


       # until the instance related models get saved and start to 
       # serialize the data when its

Upvotes: 1

Views: 7099

Answers (2)

raratiru
raratiru

Reputation: 9636

By the way I am using django-admin and I am not defining the logics in a view, adding the related model instance is handled by django admin

In this case, you can flip the order with which ModelAdmin calls save_model() and save_related() so from Model.save() you will be able to reach the updated values of the related fields, as stated in this post.

class Video(models.Model):

    def save(self, *args, **kwargs): 
        if not self.id:
            super().save(*args, **kwargs)
        all_updated_keywards = self.keywards.all()
        ...
        super().save(*args, **kwargs)


class VideoAdmin(admin.ModelAdmin):

    def save_model(self, request, obj, form, change):
        if not obj.pk: 
            super().save_model(request, obj, form, change)
        else:
            pass 

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

Upvotes: 3

Yahor Tsyplakou
Yahor Tsyplakou

Reputation: 88

You can override model's save() method and save related models (objects) before saving instance.

Upvotes: 0

Related Questions