Модель Django генерирует сотни файлов в цикле сохранения

#python-3.x #django #django-models #reportlab #temporary-files

Вопрос:

Я использую Django 3.8.2 в Ubuntu 18.04, я создаю PDF-файл при сохранении модели django, но файлы генерируются в бесконечном цикле.

У меня есть эта модель django:

 class Fattura(models.Model):
   ordine = models.ForeignKey(Ordine, on_delete=models.CASCADE, related_name="fatture", null=False)
   pdf = models.FileField(upload_to='archivio/fatture/%Y/%m')
 

pdf поле создается при сохранении экземпляра на основе информации, содержащейся в соответствующем поле «порядок», которое является внешним ключом для этой другой модели:

 class Ordine(models.Model):

   utente = models.ForeignKey(User, on_delete=models.CASCADE, related_name="ordini", null=False)
   data = models.DateField(auto_now_add=True)
   abbonamento = models.ForeignKey(Abbonamento, on_delete=models.PROTECT, null=False)
   importo = models.DecimalField(max_digits = 5, decimal_places = 2, null=False)
 

Я объявил все инструкции по созданию PDF-файла внутри save() метода моей модели Fattura. Я использую библиотеку, рекомендованную документацией Django: reportlab. Вот пользовательский метод сохранения:

     def save(self, *args, **kwargs):

       self.count_save  = 1 # I defined this attribute which I increment to understand what is actually looping
       print("COUNT SAVE: "   str(self.count_save)) # it always grows, that's the save method being re-called

       if self.pdf._file == None:

          try:
             buffer = io.BytesIO()

             p = canvas.Canvas(buffer)

             p.drawString(100,100, str(self.ordine))
             p.showPage()
             p.save()

             buffer.seek(0)

             utente = self.ordine.utente

             num_questa_fattura = utente.ordini.count()

             nome_file = "{}_{}-{}.pdf".format(
                self.ordine.utente.first_name.lower(),
                self.ordine.utente.last_name.lower(),
                num_questa_fattura)
            
             percorso = '{}/upload/archivio/fatture/{}/{}/{}'.format(
                BASE_DIR, # from settings.py
                self.ordine.data.year,
                self.ordine.data.month,
                nome_file)

             file_temporaneo = NamedTemporaryFile(delete=True)


             file_temporaneo.write(buffer.getbuffer())

             file_temporaneo.flush()

             temp_file = File(file_temporaneo, name = nome_file)

             print(nome_file)
             print(file_temporaneo)
             print("--------------------- SAVE")

             self.pdf.save(nome_file, file_temporaneo, save=True) # saving the field (EDIT: this was the problem, setting save to True resaves the whole model object, and it was creating an infinite loop)
             file_temporaneo.close()


           except:

             raise ValidationError("Invoice could not be saved")

       super().save(*args, **kwargs) # saving the object to the database
 

Когда я сохраняю объект Fattura с помощью панели администратора Django, в папке, которую я объявил, создаются сотни PDF-файлов в цикле, и на самом деле сохранение базы данных не завершается. После этого никакой объект модели Django недоступен, поэтому ничего окончательно не сохраняется в базе данных, и каждый раз генерируется ровно 499 файлов.

Я не уверен, что вызывает этот цикл, может быть, метод сохранения поля pdf в try инструкции? Или финал super().save(*args, **kwargs) ? Однако я не могу удалить его, иначе ничего не было бы сохранено, если файл PDF действительно связан с одним экземпляром, а я обновляю другое поле ordine

Вот выдержка из моих журналов

 [Tue Nov 02 15:25:06.974768 2021] [wsgi:error] [pid 10050] [remote 95.x.x.x:36048] COUNT SAVE: 262
[Tue Nov 02 15:25:07.010584 2021] [wsgi:error] [pid 10050] [remote 95.x.x.x:36048] maria_rosselli-1.pdf
[Tue Nov 02 15:25:07.015778 2021] [wsgi:error] [pid 10050] [remote 95.x.x.x:36048] <tempfile._TemporaryFileWrapper object at 0x7f7b07ac0390>
[Tue Nov 02 15:25:07.020944 2021] [wsgi:error] [pid 10050] [remote 95.x.x.x:36048] --------------------- SAVE
[Tue Nov 02 15:25:07.124451 2021] [wsgi:error] [pid 10050] [remote 95.x.x.x:36048] COUNT SAVE: 263
[Tue Nov 02 15:25:07.164934 2021] [wsgi:error] [pid 10050] [remote 95.x.x.x:36048] maria_rosselli-1.pdf
[Tue Nov 02 15:25:07.170144 2021] [wsgi:error] [pid 10050] [remote 95.x.x.x:36048] <tempfile._TemporaryFileWrapper object at 0x7f7b07ac0e48>
[Tue Nov 02 15:25:07.175250 2021] [wsgi:error] [pid 10050] [remote 95.x.x.x:36048] --------------------- SAVE
[Tue Nov 02 15:25:07.253426 2021] [wsgi:error] [pid 10050] [remote 95.x.x.x:36048] COUNT SAVE: 264
[Tue Nov 02 15:25:07.286589 2021] [wsgi:error] [pid 10050] [remote 95.x.x.x:36048] maria_rosselli-1.pdf
[Tue Nov 02 15:25:07.291734 2021] [wsgi:error] [pid 10050] [remote 95.x.x.x:36048] <tempfile._TemporaryFileWrapper object at 0x7f7b07ac0e10>
[Tue Nov 02 15:25:07.296894 2021] [wsgi:error] [pid 10050] [remote 95.x.x.x:36048] --------------------- SAVE
 

Ответ №1:

вам нужно установить значение save в False, чтобы снова вызвать идентификатор сохранения экземпляра модели при сохранении pdf:

 self.pdf.save(nome_file, file_temporaneo, save=False)
 

Комментарии:

1. Именно в этом и заключалась проблема, мне до сих пор трудно понять, почему она зацикливалась, но сейчас она работает. Спасибо

2. предполагаемое поведение метода pdf.save — «сохранить также связанный экземпляр модели, если поле filefield было изменено, если параметр save=True» … таким образом, ваш метод сохранения Fatturas будет вызван при сохранении поля файла, которое снова сохранит поле файла и так далее … см. Документацию django по Файловому полю и файловому файлу

3. docs.djangoproject.com/en/3.2/ref/models/fields/…