Django insert performance with multiple nested models


Thanh Nguyen

I have the following model definition:

class Workflow(models.Model):
    name = models.CharField(max_length=255)

class Step(models.Model):
    workflow = models.ForeignKey(Section, on_delete=models.CASCADE, related_name='steps')
    title = models.CharField(max_length=255)

class Section(models.Model):
    body = models.CharField(max_length=255)
    step = models.ForeignKey(Section, on_delete=models.CASCADE, related_name='sections')

class Question(models.Model):
    description = models.CharField(max_length=255)
    section = models.ForeignKey(Section, on_delete=models.CASCADE, related_name='questions')

class Option(models.Model):
    set_fail = models.BooleanField()
    question = models.ForeignKey(Question, on_delete=models.CASCADE, related_name='options')

class Action(models.Model):
    yes_no = models.BooleanField()
    option = models.ForeignKey(Option, on_delete=models.CASCADE, related_name='actions')

# Workflow -> Step -> Section -> Question -> Option -> Action

And I am sending the following body for inserting data from the client side (the request body is huge and I can't paste it here)

https://jsoneditoronline.org/?id=e970abc01b2a489c9933464867d11eaf

You'll see that the data is huge, with multiple records at each level, and then doing the insert does take time.

I am currently using this insert method:

class WorkflowUpdateSerializer(serializers.Serializer):

    def update(self, workflow, data):
        self.update_steps(workflow, data) # data is request JSON body

    def update_steps(self, workflow, steps):
        step_clones = [{key: value for key, value in step.items() if key != 'sections'} for step in steps]

        step_instances = Step.objects.bulk_create(
            [Step(workflow=workflow, **step) for step in step_clones])

        for index, step in enumerate(steps):
            self.update_sections(step_instances[index], step.pop('sections'))

    def update_sections(self, step, sections):
        section_clones = [{key: value for key, value in section.items() if
                           key != 'questions'} for section in sections]

        section_instances = Section.objects.bulk_create(
            [Section(step=step, **section) for section in section_clones])

        for index, section in enumerate(sections):
            self.update_questions(section=section_instances[index], questions=section.pop('questions'))

    def update_questions(self, section, questions):
    # code

    def update_options(self, question, options):
    # code

    def update_actions(self, option, actions):
    # code

Do you have any ideas how to improve it?

thanks.

Nour Wolf

Here is my solution. It bulk creates all instances of each model in a single database call, resulting in only 5 bulk inserts.

class WorkflowUpdateSerializer(serializers.Serializer):
    steps = serializers.JSONField()

    def update(self, workflow, validated_data):
        steps_dicts = [s['step'] for s in validated_data['steps']]
        sections_dicts = []
        questions_dicts = []
        options_dicts = []
        actions_dicts = []

        def _kws(d, exclude):
            return {k: v for k, v in d.items() if k != exclude}

        steps = []
        for step_dict in steps_dicts:
            sections_dicts.extend(step_dict['section'])
            steps.append(Step(workflow=workflow, **_kws(step_dict, 'section')))
        steps = Step.objects.bulk_create(steps)

        sections = []
        for step, step_dict in zip(steps, steps_dicts):
            for section_dict in step_dict['section']:
                questions_dicts.extend(section_dict['questions'])
                sections.append(Section(step=step, **_kws(section_dict, 'questions')))
        sections = Section.objects.bulk_create(sections)

        questions = []
        for section, section_dict in zip(sections, sections_dicts):
            for question_dict in section_dict['questions']:
                options_dicts.extend(question_dict['options'])
                questions.append(Question(section=section, **_kws(question_dict, 'options')))
        questions = Question.objects.bulk_create(questions)

        options = []
        for question, question_dict in zip(questions, questions_dicts):
            for option_dict in question_dict['options']:
                actions_dicts.extend(option_dict['actions'])
                options.append(Option(question=question, **_kws(option_dict, 'actions')))
        options = Option.objects.bulk_create(options)

        actions = []
        for option, option_dict in zip(options, options_dicts):
            for action_dict in option_dict['actions']:
                actions.append(Action(option=option, **action_dict))
        actions = Action.objects.bulk_create(actions)

        return workflow

Note that bulk_createto return the instance ID, the model must have something other than AutoField as its primary key. I have to create a summary BaseModellike this

# models.py
class BaseModel(models.Model):
    id = models.UUIDField(default=uuid.uuid4, primary_key=True)

    class Meta:
        abstract = True

class Step(BaseModel):
    workflow = models.ForeignKey(Workflow, on_delete=models.CASCADE, related_name='steps')
    title = models.CharField(max_length=255)

...

This is how I test the serializer

# tests.py   
with open('./data.json') as f:
    data = json.load(f)


class TestSerializer(TestCase):

    def test_serializer(self):
        workflow = Workflow.objects.create(name='test')
        serializer = WorkflowUpdateSerializer(instance=workflow, data={'steps': data})
        serializer.is_valid(raise_exception=True)
        serializer.save()
        self.assertEqual(Step.objects.count(), 3)
        self.assertEqual(Section.objects.count(), 9)
        self.assertEqual(Question.objects.count(), 18)
        self.assertEqual(Option.objects.count(), 54)
        self.assertEqual(Action.objects.count(), 162)

Related


django rest framework nested fields with multiple models

Momokjaaaaa This is django and django rest framework. I have 2 models: User and Phone. first question: I want to be able to update user data (email) as well as phone data (phone number) in 1 api update response. Phone numbers can be 0 or many. Well, it's actua

Django insert performance with multiple nested models

Thanh Nguyen I have the following model definition: class Workflow(models.Model): name = models.CharField(max_length=255) class Step(models.Model): workflow = models.ForeignKey(Section, on_delete=models.CASCADE, related_name='steps') title = model

Django rest framework. Returns nested multiple models

tomoc4 I'm trying to create a combined viewset that displays data in a nested format of three nested models. I got an error when trying to return to the viewset. Got AttributeError when attempting to get a value for field `runner` on serializer `CombinedSerial

Django - Handling multiple nested models at different levels

CH Andre Meloa I have some questions about Django nested models. As far as I know, to handle nested data structures, I have to deserialize each data and then create objects that will be concatenated with the ForeignKeyField. I can handle this by overriding the

django rest framework nested fields with multiple models

Momokjaaaaa This is django and django rest framework. I have 2 models: User and Phone. first question: I want to be able to update user data (email) as well as phone data (phone number) in 1 api update response. Phone numbers can be 0 or many. Well, it's actua

django rest framework nested fields with multiple models

Momokjaaaaa This is django and django rest framework. I have 2 models: User and Phone. first question: I want to be able to update user data (email) as well as phone data (phone number) in 1 api update response. Phone numbers can be 0 or many. Well, it's actua

Nested Json for Django multiple foreign key models

Mehmet Ince I have 4 models with relationships via FK. class Journal(models.Model): name = models.CharField(max_length=255) class Volume(models.Model): journal = models.ForeignKey(Journal, related_name='volumes') number = models.IntegerField() cl

Django rest framework. Returns nested multiple models

tomoc4 I'm trying to create a combined viewset that displays data in a nested format of three nested models. I got an error when trying to return to the viewset. Got AttributeError when attempting to get a value for field `runner` on serializer `CombinedSerial

Django - Handling multiple nested models at different levels

CH Andre Meloa I have some questions about Django nested models. As far as I know, to handle nested data structures, I have to deserialize each data and then create objects that will be concatenated with the ForeignKeyField. I can handle this by overriding the

KnockoutJS and multiple nested models

John Du I'm trying to find some tutorials on how to create nested view models with more than two levels like: shop order PO line order PO line PO line shop order PO line All orders for the store are listed and when I click on an order I should see an order lin

KnockoutJS and multiple nested models

John Doe I'm trying to find some tutorials on how to create a nested viewmodel with more than two levels, for example: Store Order Order row Order Order row Order row Store Order Order row All the orders are listed for the stores and when I click on an order I

KnockoutJS and multiple nested models

John Du I'm trying to find some tutorials on how to create nested view models with more than two levels like: shop Order PO line Order PO line PO line shop Order PO line All orders for the store are listed and when I click on an order I should see an order lin

Django rest framework. Returns nested multiple models

tomoc4 I'm trying to create a combined viewset that displays data in a nested format of three nested models. I got an error when trying to return to the viewset. Got AttributeError when attempting to get a value for field `runner` on serializer `CombinedSerial

Django - Handling multiple nested models at different levels

CH Andre Meloa I have some questions about Django nested models. As far as I know, to handle nested data structures, I have to deserialize each data and then create objects that will be concatenated with the ForeignKeyField. I can handle this by overriding the

django rest framework nested fields with multiple models

Momokjaaaaa This is django and django rest framework. I have 2 models: User and Phone. first question: I want to be able to update user data (email) as well as phone data (phone number) in 1 api update response. Phone numbers can be 0 or many. Well, it's actua

Django insert performance with multiple nested models

Thanh Nguyen I have the following model definition: class Workflow(models.Model): name = models.CharField(max_length=255) class Step(models.Model): workflow = models.ForeignKey(Section, on_delete=models.CASCADE, related_name='steps') title = model

Nested Json for Django multiple foreign key models

Mehmet Ince I have 4 models with relationships via FK. class Journal(models.Model): name = models.CharField(max_length=255) class Volume(models.Model): journal = models.ForeignKey(Journal, related_name='volumes') number = models.IntegerField() cl

Django rest framework. Returns nested multiple models

tomoc4 I'm trying to create a combined viewset that displays data in a nested format of three nested models. I got an error when trying to return to the viewset. Got AttributeError when attempting to get a value for field `runner` on serializer `CombinedSerial

Django rest framework. Returns nested multiple models

tomoc4 I'm trying to create a combined viewset that displays data in a nested format of three nested models. I got an error when trying to return to the viewset. Got AttributeError when attempting to get a value for field `runner` on serializer `CombinedSerial

Django - Handling multiple nested models at different levels

CH Andre Meloa I have some questions about Django nested models. As far as I know, to handle nested data structures, I have to deserialize each data and then create objects that will be concatenated with the ForeignKeyField. I can handle this by overriding the

KnockoutJS and multiple nested models

John Du I'm trying to find some tutorials on how to create nested view models with more than two levels like: shop Order PO line Order PO line PO line shop Order PO line All orders for the store are listed and when I click on an order I should see an order lin

django rest framework nested fields with multiple models

Momokjaaaaa This is django and django rest framework. I have 2 models: User and Phone. first question: I want to be able to update user data (email) as well as phone data (phone number) in 1 api update response. Phone numbers can be 0 or many. Well, it's actua