the “allow_migrate_model” function within my database routers keeps throw the following error when I try to run python manage.py makemigrations
:
...
File "C:Users...libsite-packagesdjangodbutils.py", line 262, in allow_migrate
allow = method(db, app_label, **hints)
TypeError: allow_migrate() missing 1 required positional argument: 'app_label'
My Routers look like this (routers.py
):
class BaseRouter:
route_app_labels = {}
db_name = ""
def db_for_read(self, model, **hints) -> Union[str, None]:
if model._meta.app_label in self.route_app_labels:
return self.db_name
return None
def db_for_write(self, model, **hints) -> Union[str, None]:
if model._meta.app_label in self.route_app_labels:
return self.db_name
return None
def allow_relation(self, obj1, obj2, **hints) -> Union[bool, None]:
if (
obj1._meta.app_label in self.route_app_labels
or obj2._meta.app_label in self.route_app_labels
):
return True
return None
def allow_migrate(
self, db, app_label, model_name=None, **hints
) -> Union[bool, None]:
if app_label in self.route_app_labels:
return db == self.db_name
return None
class DefaultRouter(BaseRouter):
route_app_labels = {"auth", "contenttypes", "sessions", "admin", "myapp"}
db_name = "default"
class LibraryRouter(BaseRouter):
""" separate router for backend stuff """
route_app_labels = {"library"}
db_name = "library"
In settings.py
DATABASE_ROUTERS = [
myproject.routers.DefaultRouter,
myproject.routers.LibraryRouter,
]
The above is basically a slightly modified version found on the Django Docs.
If I comment out the allow_migrate
the makemigrations works, however, I would like to modify the allow_migrate
further so that’s not a convincing option rn.
Ah also for some reason if a rewrite it into a staticmethod (like mentioned here) it works. But then I lose my variables stored in self
so also not optimal…
I’m on Django 4.2.14 and Python 3.9.10
1
Ah also for some reason if a rewrite it into a
staticmethod
.
You likely registered the router as:
# settings.py
# import of DefaultRouter and LibraryRouter
# …
DATABASE_ROUTERS = [DefaultRouter, LibraryRouter]
Then it is not an instance: it targets it as a class, that is the problem: the method(…)
is called as BaseRouter.allow_migrate(db, app_label, **hints)
, not through an instance.
We can solve this by constructing the instances, like:
# settings.py
# import of DefaultRouter and LibraryRouter
# …
DATABASE_ROUTERS = [DefaultRouter(), LibraryRouter()]
Another option is to move the logic one level up: by making it a @classmethod
[python-doc]:
class BaseRouter:
route_app_labels = {}
db_name = ''
@classmethod
def db_for_read(cls, model, **hints) -> Union[str, None]:
if model._meta.app_label in cls.route_app_labels:
return cls.db_name
return None
@classmethod
def db_for_write(cls, model, **hints) -> Union[str, None]:
if model._meta.app_label in cls.route_app_labels:
return cls.db_name
return None
@classmethod
def allow_relation(cls, obj1, obj2, **hints) -> Union[bool, None]:
if (
obj1._meta.app_label in cls.route_app_labels
or obj2._meta.app_label in cls.route_app_labels
):
return True
return None
@classmethod
def allow_migrate(
cls, db, app_label, model_name=None, **hints
) -> Union[bool, None]:
if app_label in cls.route_app_labels:
return db == cls.db_name
return None
The DATABASE_ROUTERS
should actually work with a string, but I agree that is perhaps not the best way from a software design perspective: if you rename the class, it is possible that the string does not change, and so it refers to a non-existing item that does not per se raises an exception, but the way it was designed is:
# settings.py
# …
DATABASE_ROUTERS = [
'module_name.DefaultRouter',
'module_name.LibraryRouter'
]
3