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

从Django 2.0迁移到2.2时视图权限及跨库关联报错排查

Fixing "Cannot assign ContentType: current database router prevents this relation" in Django 2.2 Migration

Let's break down this issue and cover both the root cause and actionable solutions that don't require modifying Django's core code.

What's Causing the Error?

When upgrading from Django 2.0 to 2.2, you hit this error because Django 2.1 introduced view permissions (the view_<model> permission type). During migrations, Django automatically tries to create these permissions for your models.

The problem arises because:

  • Permission objects are linked to ContentType objects (which track your app's models)
  • Your custom database router's allow_relation rule was blocking cross-database relations between the Permission (being created in the demo database) and the ContentType (likely stored in your default database)

Solution 1: Adjust Your Custom Database Router

The simplest fix is updating your router's allow_relation method to explicitly allow relations involving the demo database. This lets Django link the Permission to the ContentType without hitting the router block.

Here's the modified router code:

class AppRouter:
    def allow_relation(self, obj1, obj2, **hints):
        # Allow relations if either object is in the demo database
        if obj1._state.db == "demo" or obj2._state.db == "demo":
            return True
        # Fallback to default rule: only allow same-database relations
        return obj1._state.db == obj2._state.db

After updating the router, re-run your migration command:

python manage.py migrate demo --database demo

Solution 2: Manually Create View Permissions (No Router Changes)

If you don't want to adjust your router's rules, you can bypass Django's automatic permission generation by creating the view permissions manually in a custom migration. This mimics the core code change you tried, but keeps modifications in your own codebase.

  1. Create an empty migration for your demo app:
python manage.py makemigrations demo --empty
  1. Open the generated migration file and replace its content with this code (update placeholders to match your models):
from django.db import migrations
from django.contrib.contenttypes.models import ContentType
from django.contrib.auth.models import Permission

def create_view_permissions(apps, schema_editor):
    # Replace 'your_model_name' with the name of your model (lowercase)
    ct = ContentType.objects.get(app_label='demo', model='your_model_name')
    # Create the view permission using content_type_id instead of the object
    Permission.objects.using('demo').get_or_create(
        codename='view_your_model_name',
        name='Can view your model name',
        content_type_id=ct.id
    )

class Migration(migrations.Migration):
    dependencies = [
        # Add the dependency on your app's last migration, e.g.:
        # ('demo', '0001_initial'),
    ]

    operations = [
        migrations.RunPython(create_view_permissions),
    ]
  1. Run the migration as usual:
python manage.py migrate demo --database demo

Why Your Core Code Change Worked

When you modified Django's core django/contrib/auth/management/__init__.py file to use content_type_id=ct.id instead of content_type=ct:

  • Originally, passing the full ContentType object triggered Django's database router check (allow_relation), which blocked the cross-database link.
  • Using content_type_id directly sets the foreign key's numeric value without passing the object. This skips the router's relation check, so the Permission is saved successfully.

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.13 08:00:52