Django Multi-Database ValueError: "Cannot assign Role object: the current database router prevents this relation"
I'm encountering a ValueError in my Django application when trying to save a User object with a related UserRoleAssociation in the admin interface. The error occurs in a multi-database setup where both the UserRoleAssociation and Role models are intended to use the members database. The error message is:
ValueError: Cannot assign "<Role: Role object (67c43a2e-c4e7-4846-bd31-700bf5d35e82)>": the current database router prevents this relation.
I have a Django project with two databases: default and members. The User, UserRoleAssociation, and Role models are all configured to use the members database via admin classes. The error occurs when saving a User object with an inline UserRoleAssociation in the Django admin.
Models
# models.py
class Role(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
role_name = models.CharField(max_length=255, unique=True)
class Meta:
db_table = "roles"
class UserRoleAssociation(models.Model):
user = models.ForeignKey("User", on_delete=models.CASCADE)
role = models.ForeignKey("Role", on_delete=models.CASCADE)
def __str__(self):
return f"{self.role.role_name}"
class Meta:
unique_together = ["user", "role"]
db_table = "user_role_association"
class User(models.Model):
phone_number = models.CharField(max_length=15)
is_premium = models.BooleanField(default=False)
roles = models.ManyToManyField("Role", through=UserRoleAssociation, related_name="users")
class Meta:
db_table = "users"
app_label = "members"
base_manager_name = 'objects'
default_manager_name = 'objects'
Admin Configuration I use a custom MultiDBModelAdmin and MultiDBTabularInline to enforce the members database:
# admin.py
class MultiDBModelAdmin(admin.ModelAdmin):
using = "members"
def save_model(self, request, obj, form, change):
obj.save(using=self.using)
def delete_model(self, request, obj):
obj._state.db = self.using
obj.delete(using=self.using)
def delete_queryset(self, request, queryset):
queryset.using(self.using).delete()
def get_queryset(self, request):
return super().get_queryset(request).using(self.using)
def formfield_for_foreignkey(self, db_field, request, **kwargs):
return super().formfield_for_foreignkey(db_field, request, using=self.using, **kwargs)
def formfield_for_manytomany(self, db_field, request, **kwargs):
return super().formfield_for_manytomany(db_field, request, using=self.using, **kwargs)
def _save_related(self, request, form, formsets, change):
"""Ensure related objects are saved in the correct database."""
for formset in formsets:
formset.instance._state.db = self.using # Force the database
super()._save_related(request, form, formsets, change)
class MultiDBTabularInline(admin.StackedInline):
using = "members"
def get_queryset(self, request):
return super().get_queryset(request).using(self.using)
def formfield_for_manytomany(self, db_field, request, **kwargs):
= # Tell Django to populate ManyToMany widgets using a query
# on the 'members' database.
return super().formfield_for_manytomany(
db_field, request, using=self.using, **kwargs
)
class UserRoleAssociationInline(MultiDBTabularInline):
model = UserRoleAssociation
extra = 1
@admin.register(User)
class MemberAdmin(MultiDBModelAdmin):
list_display = ["id"]
inlines = [UserRoleAssociationInline]
The error occurs when saving a User object with a UserRoleAssociation inline in the admin interface.