Как начать и завершить атомарную транзакцию в MySQL с помощью Django 5.0.3?

Я хочу сохранить CSV (с 50k строк) как таблицу MySQL в Django. Я написал модель и запустил атомарную транзакцию из post-запроса (выполняется ajax async submit из шаблона). Проблема в том, что транзакция прерывается StateReloader и не продолжается. Можно ли запустить транзакцию в фоновом режиме и дать ей завершиться?

Вот небольшая версия моего кода:

# file: models.py


from django.db import models, transaction

class MyManager(models.Manager):
    @transaction.atomic
    def create_models(self, file, callback=None):
        if callback is not None:
            transaction.on_commit(callback)
        with open(file, "r") as fp:
            lines = map(str.strip, fp.readlines())
            lines = map(lambda x: x.split(";"), lines)
            lines = list(lines)
        headers = lines[0]
        print("Writing table with {} lines...".format(len(lines) - 1)) # 50k lines
        for i, line in enumerate(lines[1:]):
            for j in range(len(headers)):
                print("Saving line #{}".format(i), end="\r").          # ends at line 800 before the StateReloader
                self.create(row=i + 1, header=headers[j], value=line[j])
        print()
        return True


class MyModel(models.Model):
    row = models.IntegerField(null=False, blank=False)
    header = models.CharField(max_length=255, null=False, blank=False)
    value = models.CharField(max_length=255, null=False, blank=False)
    objects = MyManager()
    
    def __str__(self) -> str:
        return f"{self.header} = {self.value} on row {self.row}"
# file: views.py


from django.http import JsonResponse
from django.views import generic
from django.contrib.auth.mixins import LoginRequiredMixin


from .models import MyModel


def end_transaction():
    print("Done!")


class MyView(LoginRequiredMixin, generic.TemplateView):
    template_name = "edit_csv.html"

    def get(self, request, *args, **kwargs):
        context = self.get_context_data(*args, **kwargs)
        return self.render_to_response(context)

    def post(self, request, *args, **kwargs):
        context = self.get_context_data(*args, **kwargs)
        filename = request.POST.get("filename", "").strip()
        if filename != "":
            filename = os.path.basename(filename)
            routine = MyModel.objects.create_models(filename, end_transaction)
        return JsonResponse(self.render_to_response(context), safe=False)

    def get_context_data(self, *args, **kwargs):
        context = super(MyView, self).get_context_data(*args, **kwargs)
        return context

Я не думаю, что проблема в транзакции, вы можете эффективно создавать записи в bulk с помощью:

import csv


class MyView(LoginRequiredMixin, generic.TemplateView):
    template_name = 'edit_csv.html'

    def post(self, request, *args, **kwargs):
        context = self.get_context_data(*args, **kwargs)
        filename = request.POST.get('filename', '').strip()
        if filename != '':
            with open(os.path.basename(filename), 'r') as f:
                csv = csv.DictReader(csvfile, delimiter=';')
                MyModel.objects.bulk_create(
                    [
                        MyModel(row=row, header=header, value=value)
                        for row, data in enumerate(csv, 1)
                        for header, value in data.items()
                    ]
                )
        return JsonResponse(self.render_to_response(context), safe=False)

Таким образом, мы сначала создаем большой список элементов для вставки, а затем добавляем их все одним (или несколькими запросами) в базу данных. Это, вероятно, также значительно ускорит процесс.

Вернуться на верх