TransactionManagementError “You can't execute queries until the end of the 'atomic' block” while using signals, but only during Unit Testing

后端 未结 11 2092
有刺的猬
有刺的猬 2020-12-02 06:03

I am getting TransactionManagementError when trying to save a Django User model instance and in its post_save signal, I\'m saving some models that have the user as the forei

11条回答
  •  心在旅途
    2020-12-02 06:36

    I ran into this same problem myself. This is caused by a quirk in how transactions are handled in the newer versions of Django coupled with a unittest that intentionally triggers an exception.

    I had a unittest that checked to make sure a unique column constraint was enforced by purposefully triggering an IntegrityError exception:

    def test_constraint(self):
        try:
            # Duplicates should be prevented.
            models.Question.objects.create(domain=self.domain, slug='barks')
            self.fail('Duplicate question allowed.')
        except IntegrityError:
            pass
    
        do_more_model_stuff()
    

    In Django 1.4, this works fine. However, in Django 1.5/1.6, each test is wrapped in a transaction, so if an exception occurs, it breaks the transaction until you explicitly roll it back. Therefore, any further ORM operations in that transaction, such as my do_more_model_stuff(), will fail with that django.db.transaction.TransactionManagementError exception.

    Like caio mentioned in the comments, the solution is to capture your exception with transaction.atomic like:

    from django.db import transaction
    def test_constraint(self):
        try:
            # Duplicates should be prevented.
            with transaction.atomic():
                models.Question.objects.create(domain=self.domain, slug='barks')
            self.fail('Duplicate question allowed.')
        except IntegrityError:
            pass
    

    That will prevent the purposefully-thrown exception from breaking the entire unittest's transaction.

提交回复
热议问题