Как начать и завершить атомарную транзакцию в 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)
Таким образом, мы сначала создаем большой список элементов для вставки, а затем добавляем их все одним (или несколькими запросами) в базу данных. Это, вероятно, также значительно ускорит процесс.