b_pcakes
b_pcakes

Reputation: 2540

Django - Triggering creation of other instances when one instance is created

I have three models representing a message, plaintext, and ciphertext:

class Message(models.Model):
    key = models.ForeignKey(Key, on_delete=models.CASCADE, related_name='messages')
    created = models.DateTimeField(auto_now_add=True)
    title = models.CharField(max_length=50, blank=True, default='')
    owner = models.ForeignKey(User, related_name='messages')

class Plaintext(models.Model):
    message = models.OneToOneField(
        Message,
        on_delete=models.CASCADE,
        primary_key=True
    )
    text = models.TextField(blank=True, default='')


class Ciphertext(models.Model):
    message = models.OneToOneField(
        Message,
        on_delete=models.CASCADE,
        primary_key=True
    )
    text = models.TextField(blank=True, default='')

I would like a Plaintext and a Ciphertext instance to be automatically created whenever a Message is created. What is the best way to accomplish this?

Upvotes: 0

Views: 792

Answers (2)

Brandon Taylor
Brandon Taylor

Reputation: 34553

You actually have everything you need just by using a post_save signal:

from .models import Plaintext, Ciphertext


def create_plain_and_cipher_text(sender, instance, created, **kwargs):
    if created:
        Plaintext.objects.create(message=instance)
        Ciphertext.objects.create(message=instance)

You don't need or have to override the .save() method on Message at all.

Fortunately, post_save tells us whether or not a new instance was created or not, allowing you to create the related model instances only when a new object is created, and not every time a Message object is saved.

Upvotes: 2

Ian Price
Ian Price

Reputation: 7616

By dynamically getting model constructors, you won't have to worry about loading order. You can override the save method or use a post save signal to trigger the create actions.

from django.apps import apps

class Message(models.Model):
    ...
    def save(self,*args,**kwargs):
        PLAIN_TEXT_MODEL = apps.get_model(app_label='your_app_name_here', model_name='Plaintext')
        CIPHER_TEXT_MODEL = apps.get_model(app_label='your_app_name_here', model_name='Ciphertext')
        new_pt_model = PLAIN_TEXT_MODEL.objects.create(some_field=self.some_field_in_message_model,some_other_field = 1)
        CIPHER_TEXT_MODEL.objects.create(some_field="Something")
        super(Message,self).save(*args,**kwargs) # call default save method

To use a signal -

from django.db.models import signals

class Message(models.Model):
    ...

def your_callable_function(sender, instance, **kwargs):
    # do something, create other model instances, etc

signals.post_save.connect(your_callable_function, sender=Message)

Upvotes: 0

Related Questions