You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Django 2.0项目单元测试因IntegrityError失败求助(PostgreSQL环境)

Hey there! Let's figure out why your Django 2.0 unit tests are failing with PostgreSQL 10.1 (but working fine with SQLite) — especially since the tests pass individually but break during teardown. This is a common gotcha with database-specific behavior, so let's break down the most likely causes and fixes:

Possible Causes & Solutions

1. Transaction Behavior Differences Between Databases

PostgreSQL has way stricter transaction rules than SQLite. Django wraps each test in a transaction that gets rolled back after the test finishes, but if your change-tracking model uses signals (like post_save or post_delete) that run database operations outside the test's transaction, PostgreSQL will enforce integrity constraints during rollback that SQLite ignores.

  • Audit your signal handlers: If you're using signals to create/update your tracking entries, make sure they're tied to the same transaction as the model they're tracking. For example, if you have a signal that creates a ChangeLog entry when a Product is saved, wrap the database operation in transaction.on_commit() to ensure it only runs after the test's transaction commits (and thus won't cause issues during rollback):
    from django.db import transaction
    from django.db.models.signals import post_save
    from django.dispatch import receiver
    from .models import Product, ChangeLog
    
    @receiver(post_save, sender=Product)
    def log_product_change(sender, instance, created, **kwargs):
        def create_log_entry():
            ChangeLog.objects.create(
                product_id=instance.id,
                action="created" if created else "updated"
            )
        # Wait until the transaction commits to create the log
        transaction.on_commit(create_log_entry)
    

2. Teardown Order & Foreign Key Constraints

PostgreSQL enforces foreign key constraints rigorously, while SQLite doesn't (unless you explicitly enable foreign_keys in your settings). During teardown, Django might try to delete your main model instances before the tracking model instances, triggering an integrity error because the tracking entries still reference the main model.

  • Explicitly delete tracking entries first: Override your test class's tearDown() method to clean up the tracking model before the main models. This ensures you don't hit foreign key conflicts:
    from django.test import TestCase
    from .models import ChangeLog
    
    class ProductTests(TestCase):
        def tearDown(self):
            # Delete tracking entries first to avoid FK violations
            ChangeLog.objects.all().delete()
            # Then run the default teardown
            super().tearDown()
    
  • Double-check on_delete behavior: Make sure the foreign key in your tracking model uses on_delete=models.CASCADE (so deleting a main model instance deletes its tracking entries automatically). Even with this, PostgreSQL's transaction rollback might not handle deletion order the same way SQLite does, so explicit teardown cleanup is still a safe bet.

3. Test Isolation Gaps

When running tests in a suite, PostgreSQL's stricter isolation levels can expose shared state issues that SQLite ignores. If one test leaves behind data that conflicts with another during teardown, you'll get integrity errors.

  • Use the right test class: Ensure your tests inherit from TestCase (which uses transactions for isolation) instead of SimpleTestCase. If you're already using TestCase, try switching to TransactionTestCase — it's slower, but it resets the database between tests by truncating tables instead of rolling back transactions, which can avoid teardown conflicts.
  • Verify setup/cleanup logic: Make sure any test data you create in setUp() is properly contained within the test's transaction. Avoid using global fixtures or data that persists across tests unless you're explicitly cleaning it up.

4. PostgreSQL-Specific Constraint Enforcement

PostgreSQL enforces constraints like UNIQUE or NOT NULL more strictly than SQLite, even during transaction rollback. If your tracking model has unique constraints (e.g., a unique combination of model_id and change_timestamp), rollback might leave orphaned entries that conflict with the next test's setup.

  • Audit your tracking model's constraints: Check if any unique_together or UniqueConstraint clauses could be causing conflicts during teardown. If so, you might need to adjust the constraint or add cleanup logic to remove conflicting entries before each test runs.
  • Temporarily defer constraints (last resort): If you can't pinpoint the exact constraint, you can tell PostgreSQL to defer all constraint checks during teardown. This is a workaround, not a fix, but it can help you debug further:
    from django.db import connection
    
    def tearDown(self):
        with connection.cursor() as cursor:
            cursor.execute("SET CONSTRAINTS ALL DEFERRED;")
        super().tearDown()
    

内容的提问来源于stack exchange,提问作者luc

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.19 08:43:07