Dynamic table creation on a Django Model save

I would like to create additional tables when I call model.save() (INSERT). But I keep getting this error:

django.db.transaction.TransactionManagementError: Executing DDL
statements while in a transaction on databases that can’t perform a
rollback is prohibited.

I tried to create additional tables inside model.save() and using a pre_save() signal, I get the same error.

This is a pre_save solution attempt:

from django.db import connection
from django.db.models.signals import pre_save
from django.dispatch import receiver


@receiver(pre_save, sender=MyModel, dispatch_uid="create_tags")
def create_tags(sender, instance, **kwargs):
    print("debug, signal pre_save works") 
    try:
        # if obj exists in MyModel table, skip tag table creation
        existing_obj = MyModel.objects.get(name=instance.name)
        print("debug, obj exists")
    except MyModel.DoesNotExist:
        with connection.schema_editor() as schema_editor:
            schema_editor.create_model(MyModel2)

Stack: Django, MySQL.

What I want to implement is to create additional tables for the instance that is being inserted.

To clarify how the miscellaneous per-instance tables are generated, this is the code to derive a per-instance table. For example, for every inserted car instance, there are to be generated miscellaneous Tag tables with sensor measurements:

def get_tag(car_url, car_type):
    class Tag(models.Model):
        time = models.PositiveIntegerField(primary_key=True)  # unix time in seconds  # noqa
        value = models.FloatField()

        class Meta:
            db_table = car_url + "_" + car_type

        def __str__(self) -> str:
            return str(self.time) + "," + str(self.value)

    return Tag

I think that the probable solution is to use “nonatomic” somewhere in the code.

Answers:

Thank you for visiting the Q&A section on Magenaut. Please note that all the answers may not help you solve the issue immediately. So please treat them as advisements. If you found the post helpful (or not), leave a comment & I’ll get back to you as soon as possible.

Method 1

Temporarily set schema_editor.connection.in_atomic_block = False.

with connection.schema_editor() as schema_editor:
    in_atomic_block = schema_editor.connection.in_atomic_block
    schema_editor.connection.in_atomic_block = False
    try:
        schema_editor.create_model(MyModel2)
    finally:
        schema_editor.connection.in_atomic_block = in_atomic_block

Using a context manager:

with connection.schema_editor() as schema_editor:
    with non_atomic(schema_editor):
        schema_editor.create_model(MyModel2)
from contextlib import contextmanager


@contextmanager
def non_atomic(schema_editor):
    in_atomic_block = schema_editor.connection.in_atomic_block
    schema_editor.connection.in_atomic_block = False
    try:
        yield
    finally:
        schema_editor.connection.in_atomic_block = in_atomic_block


All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

0 0 votes
Article Rating
Subscribe
Notify of
guest

0 Comments
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x