我对Django测试有一个奇怪的问题。只有在运行测试命令python3 manage.py test
时,才会出现此问题
背景信息:我有一个带有自定义保存方法的订阅模型,它可以发送信息邮件、创建账单和课程等。使用super().save(using=using, *args, **kwargs)
命令将订阅保存到数据库后,调用方法self.create_lessons()
get,该方法在创建课程时导致IntegrityError,表示订阅在表中不存在
为了检查订阅表中是否真的不存在订阅,我添加了一个查询s = Subscription.objects.get(id=self.id)
,并将其打印到控制台print(s)
。查询和print命令工作正常,但我仍然得到完整性错误
至少对我来说,真正令人困惑的是,只有在运行python3 manage.py test
命令时才会发生错误。当我运行Web服务器并使用管理面板或shell创建订阅时,一切都正常运行
更奇怪的是,当我对代码进行更改时,错误会消失一段时间(2-3次成功执行测试命令),这其实并不重要。例如,注释或数据库查询
我在下面添加了回溯、subcription模型的自定义保存方法和create_-lessons方法。只要告诉我你是否需要任何进一步的代码或信息。我希望你们能帮我解决这个问题,任何提示或提示都非常感谢!我为这个问题挣扎了很长一段时间
编辑:
我已经找到了一个解决方法来避免这个问题。当我将keep database和debug mode选项添加到test命令并运行python3 manage.py test -k --failfast -v 3 --debug-mode
时,我的测试总是在工作
回溯:
Traceback (most recent call last):
sut_1 | File "/usr/local/lib/python3.8/dist-packages/django/db/backends/utils.py", line 84, in _execute
sut_1 | return self.cursor.execute(sql, params)
sut_1 | psycopg2.errors.ForeignKeyViolation: insert or update on table "main_lesson" violates foreign key constraint "main_lesson_subscription_id_41a5ca44_fk_main_subscription_id"
sut_1 | DETAIL: Key (subscription_id)=(5) is not present in table "main_subscription".
sut_1 |
sut_1 |
sut_1 | The above exception was the direct cause of the following exception:
sut_1 |
sut_1 | Traceback (most recent call last):
sut_1 | File "/code/main/tests_commands.py", line 105, in setUp
sut_1 | sub1 = Subscription.objects.create(first_lessons_date=date,
sut_1 | File "/usr/local/lib/python3.8/dist-packages/django/db/models/manager.py", line 82, in manager_method
sut_1 | return getattr(self.get_queryset(), name)(*args, **kwargs)
sut_1 | File "/usr/local/lib/python3.8/dist-packages/django/db/models/query.py", line 422, in create
sut_1 | obj.save(force_insert=True, using=self.db)
sut_1 | File "/code/main/models.py", line 431, in save
sut_1 | self.create_lessons()
sut_1 | File "/code/main/models.py", line 211, in create_lessons
sut_1 | l.save(using='primary')
sut_1 | File "/usr/local/lib/python3.8/dist-packages/django/db/models/base.py", line 740, in save
sut_1 | self.save_base(using=using, force_insert=force_insert,
sut_1 | File "/usr/local/lib/python3.8/dist-packages/django/db/models/base.py", line 777, in save_base
sut_1 | updated = self._save_table(
sut_1 | File "/usr/local/lib/python3.8/dist-packages/django/db/models/base.py", line 870, in _save_table
sut_1 | result = self._do_insert(cls._base_manager, using, fields, update_pk, raw)
sut_1 | File "/usr/local/lib/python3.8/dist-packages/django/db/models/base.py", line 907, in _do_insert
sut_1 | return manager._insert([self], fields=fields, return_id=update_pk,
sut_1 | File "/usr/local/lib/python3.8/dist-packages/django/db/models/manager.py", line 82, in manager_method
sut_1 | return getattr(self.get_queryset(), name)(*args, **kwargs)
sut_1 | File "/usr/local/lib/python3.8/dist-packages/django/db/models/query.py", line 1186, in _insert
sut_1 | return query.get_compiler(using=using).execute_sql(return_id)
sut_1 | File "/usr/local/lib/python3.8/dist-packages/django/db/models/sql/compiler.py", line 1375, in execute_sql
sut_1 | cursor.execute(sql, params)
sut_1 | File "/usr/local/lib/python3.8/dist-packages/django/db/backends/utils.py", line 99, in execute
sut_1 | return super().execute(sql, params)
sut_1 | File "/usr/local/lib/python3.8/dist-packages/django/db/backends/utils.py", line 67, in execute
sut_1 | return self._execute_with_wrappers(sql, params, many=False, executor=self._execute)
sut_1 | File "/usr/local/lib/python3.8/dist-packages/django/db/backends/utils.py", line 76, in _execute_with_wrappers
sut_1 | return executor(sql, params, many, context)
sut_1 | File "/usr/local/lib/python3.8/dist-packages/django/db/backends/utils.py", line 84, in _execute
sut_1 | return self.cursor.execute(sql, params)
sut_1 | File "/usr/local/lib/python3.8/dist-packages/django/db/utils.py", line 89, in __exit__
sut_1 | raise dj_exc_value.with_traceback(traceback) from exc_value
sut_1 | File "/usr/local/lib/python3.8/dist-packages/django/db/backends/utils.py", line 84, in _execute
sut_1 | return self.cursor.execute(sql, params)
sut_1 | django.db.utils.IntegrityError: insert or update on table "main_lesson" violates foreign key constraint "main_lesson_subscription_id_41a5ca44_fk_main_subscription_id"
sut_1 | DETAIL: Key (subscription_id)=(5) is not present in table "main_subscription".
订阅模型的自定义保存方法:
#Custom save function which includes creating the lessons of the
#subscription
def save(self, using='primary', *args, **kwargs):
#Marks the bill as paid if the bill already was paid in advance
if self.billing_mode == '2':
self.bill_paid = '1'
#If lessons_outstanding isn't defined and isn't zero, the value
#will be equal to the amount of lessons.
if not self.lessons_outstanding:
if self.lessons_outstanding != 0:
self.lessons_outstanding = self.contract.lessons_count
#Sends info email
if self.bexio_id == None and self.info_mail != '1':
#Sends email to student
subject = 'Neues Abo wurde erstellt'
#If there is a bill, the text with a bill will be sent.
#Elsewise there's only the text without the reference
#to the bill.
first_lessons_date = self.first_lessons_date + timedelta(minutes=60)
if self.billing_mode == '2':
body_text = """Hallo {} {}
{} {} hat soeben ein Abo mit Startdatum {} für Sie erstellt.
Wir wünschen viel Spass im Unterricht und stehen bei Fragen gerne zur Verfügung.
""".format(self.contract.student.first_name,
self.contract.student.last_name,
self.contract.teacher.first_name,
self.contract.teacher.last_name,
first_lessons_date.strftime("%d.%m.%Y"))
body_html = """<p>Hallo {} {}</p><p></p><p>{} {} hat soeben
ein Abo mit Startdatum {} für Sie erstellt.
<p></p>
<p>Wir wünschen viel Spass im Unterricht und stehen bei
Fragen gerne zur Verfügung.</p>
""".format(self.contract.student.first_name,
self.contract.student.last_name,
self.contract.teacher.first_name,
self.contract.teacher.last_name,
first_lessons_date.strftime("%d.%m.%Y"))
else:
body_text = """Hallo {} {}
{} {} hat soeben ein Abo mit Startdatum {} für Sie erstellt. Die Rechnung erhalten Sie in einem separaten Mail. Falls Sie die Zahlungsweise «Papierrechnung» bei uns hinterlegt haben, erhalten Sie die Rechnung per Post.
Wir wünschen viel Spass im Unterricht und stehen bei Fragen gerne zur Verfügung.
""".format(self.contract.student.first_name,
self.contract.student.last_name,
self.contract.teacher.first_name,
self.contract.teacher.last_name,
first_lessons_date.strftime("%d.%m.%Y"))
body_html = """<p>Hallo {} {}</p><p></p><p>{} {} hat soeben
ein Abo mit Startdatum {} für Sie erstellt. Die Rechnung
erhalten Sie in einem separaten Mail. Falls Sie die
Zahlungsweise «Papierrechnung» bei uns hinterlegt haben,
erhalten Sie die Rechnung per Post.</p>
<p></p>
<p>Wir wünschen viel Spass im Unterricht und stehen bei
Fragen gerne zur Verfügung.</p>
""".format(self.contract.student.first_name,
self.contract.student.last_name,
self.contract.teacher.first_name,
self.contract.teacher.last_name,
first_lessons_date.strftime("%d.%m.%Y"))
#If there is no student address an escalation will
#be created, because the info email can't be send
if not self.contract.student.address:
rel = ('Student (' + str(self.contract.student.pipedrive_id) +
'): ' + self.contract.student.first_name + ' ' +
self.contract.student.last_name)
create_escalation(type='E1', category='S', object=rel)
else:
email_to = [self.contract.student.address.email]
send_mail(subject, body_text, body_html, email_to)
#send email to teacher
subject = 'Abo erstellt für {} {}'.format(self.contract.student.last_name,
self.contract.student.first_name)
#If there is a bill, the text with a bill will be sent.
#Elsewise there's only the text without
if self.billing_mode == '2':
body_text = """Hallo {}
Es wurde folgendes Abo erstellt. Die Rechnung wurde bereits mit einem Gutschein beglichen:
Schüler: {} {}
Startdatum {}
Für weitere Informationen logge dich mit deinem Login unter backend.school78.ch ein. Dort kannst du auch die erteilten Lektionen eintragen.
Bei Fragen wende dich an dein School78 Team.
""".format(self.contract.teacher.first_name,
self.contract.student.last_name,
self.contract.student.first_name,
first_lessons_date.strftime("%d.%m.%Y"))
body_html = """<p>Hallo {}</p>
<p></p><p>Es wurde folgendes Abo erstellt. Die Rechnung wurde
bereits mit einem Gutschein beglichen:</p>
<p></p><p>Schüler: {} {}<br />Startdatum: {}</p>
<p></p><p>Für weitere Informationen logge dich mit deinem Login
unter <a href='https://backend.school78.ch'>backend.school78.ch</a>
ein. Dort kannst du auch die erteilten Lektionen eintragen.</p><p></p><p>Bei Fragen
stehen wir dir gerne zur Verfügung.</P>""".format(self.contract.teacher.first_name,
self.contract.student.last_name,
self.contract.student.first_name,
first_lessons_date.strftime("%d.%m.%Y"))
else:
body_text = """Hallo {}
Die Rechnung für das folgende Abo wurde per Email versendet – bei Papierrechnung erfolgt der Versand in den nächsten drei Arbeitstagen:
Schüler: {} {}
Startdatum {}
Für weitere Informationen logge dich mit deinem Login unter backend.school78.ch ein. Dort kannst du auch die erteilten Lektionen eintragen und sehen, ob das Abo bereits bezahlt ist oder nicht.
Bei Fragen wende dich an dein School78 Team.
""".format(self.contract.teacher.first_name,
self.contract.student.last_name,
self.contract.student.first_name,
first_lessons_date.strftime("%d.%m.%Y"))
body_html = """<p>Hallo {}</p>
<p></p><p>Die Rechnung für das folgende Abo wurde per Email
versendet – bei Papierrechnung erfolgt der Versand in den
nächsten drei Arbeitstagen:</p>
<p></p><p>Schüler: {} {}<br />Startdatum: {}</p>
<p></p><p>Für weitere Informationen logge dich mit deinem Login
unter <a href='https://backend.school78.ch'>backend.school78.ch</a>
ein. Dort kannst du auch die erteilten Lektionen eintragen und sehen,
ob das Abo bereits bezahlt ist oder nicht.</p><p></p><p>Bei Fragen
stehen wir dir gerne zur Verfügung.</P>""".format(self.contract.teacher.first_name,
self.contract.student.last_name,
self.contract.student.first_name,
first_lessons_date.strftime("%d.%m.%Y"))
#If there is no teacher address an escalation will
#be created, because the info email can't be send
if not self.contract.teacher.address:
rel = ('Teacher (' + str(self.contract.teacher.pipedrive_id) +
'): ' + self.contract.teacher.first_name + ' ' +
self.contract.teacher.last_name)
create_escalation(type='E1', category='T', object=rel)
else:
email_to = [self.contract.teacher.address.email]
send_mail(subject, body_text, body_html, email_to, teacher=True)
self.info_mail = '1'
#Saves the model
super().save(using=using, *args, **kwargs)
s = Subscription.objects.get(id=self.id)
print(s)
#Creates lessons if they don't exist
if self.lessons.count() == 0:
self.create_lessons()
#Creates bill if there is none
if self.bexio_id == None and self.bill_paid != '1':
#If bill type is paper, an escalation will be created and the
#office will send a bill manually
if self.contract.bill_type == 'P':
#Creates bill with customcommand
args = [None, self.pk]
opts = {}
call_command('create_bill', *args, **opts)
#If bill type is email, the bill will be created directly in
#Bexio and will be sent...
elif self.contract.bill_type == 'E':
#Creates bill with customcommand
args = [None, self.pk]
opts = {}
call_command('create_bill', *args, **opts)
创建_课程()-方法:
#Creates the lessons related to the new subscription
def create_lessons(self):
s = Subscription.objects.get(id=self.id)
print(s)
for i in range(0, self.contract.lessons_count):
if i == 0:
l = Lesson(lessons_date=self.first_lessons_date, status='T', subscription=self)
l.save(using='primary')
else:
l = Lesson(status='N', subscription=self)
l.save(using='primary')
目前没有回答
相关问题 更多 >
编程相关推荐