场景
省事,不用写urls.py文件相关的代码。
代码
1.全局的config/discovery.py,我放在根目录下的config目录里,位置随意:
python
import pkgutil
import sys
import typing as t
def import_string(import_name: str, silent: bool = False) -> t.Any:
"""Imports an object based on a string. This is useful if you want to
use import paths as endpoints or something similar. An import path can
be specified either in dotted notation (``xml.sax.saxutils.escape``)
or with a colon as object delimiter (``xml.sax.saxutils:escape``).
If `silent` is True the return value will be `None` if the import fails.
:param import_name: the dotted name for the object to import.
:param silent: if set to `True` import errors are ignored and
`None` is returned instead.
:return: imported object
"""
import_name = import_name.replace(":", ".")
try:
try:
__import__(import_name)
except ImportError:
if "." not in import_name:
raise
else:
return sys.modules[import_name]
module_name, obj_name = import_name.rsplit(".", 1)
module = __import__(module_name, globals(), locals(), [obj_name])
try:
return getattr(module, obj_name)
except AttributeError as e:
raise ImportError(e) from None
except ImportError as e: # noqa
pass
return None
def find_modules(
import_path: str, include_packages: bool = False, recursive: bool = False
) -> t.Iterator[str]:
"""Finds all the modules below a package. This can be useful to
automatically import all views / controllers so that their metaclasses /
function decorators have a chance to register themselves on the
application.
Packages are not returned unless `include_packages` is `True`. This can
also recursively list modules but in that case it will import all the
packages to get the correct load path of that module.
:param import_path: the dotted name for the package to find child modules.
:param include_packages: set to `True` if packages should be returned, too.
:param recursive: set to `True` if recursion should happen.
:return: generator
"""
module = import_string(import_path)
path = getattr(module, "__path__", None)
if path is None:
raise ValueError(f"{import_path!r} is not a package")
basename = f"{module.__name__}."
for _importer, modname, ispkg in pkgutil.iter_modules(path):
modname = basename + modname
if ispkg:
if include_packages:
yield modname
if recursive:
yield from find_modules(modname, include_packages, True)
else:
yield modname
def auto_register():
base_path = "sk_scan"
for x in find_modules(base_path, recursive=True):
if "views" in x:
try:
import_string(x)
except Exception as e:
pass
def discovery():
auto_register()
2.全局的config/api_router.py,我放在根目录下的config目录里,位置随意:
python
from django.conf import settings
from rest_framework.routers import DefaultRouter, SimpleRouter
from config.discovery import discovery
from sk_scan.users.api.views import UserViewSet
if settings.DEBUG:
router = DefaultRouter()
else:
router = SimpleRouter()
router.register("users", UserViewSet)
discovery()
app_name = "api"
urlpatterns = router.urls
3.全局urls.py文件:
python
urlpatterns += [
# API base url
path("api/", include("config.api_router")),
]
4.自动注册入口,作为工具类,我放在common/drf/mixins.py文件里:
python
class AutoRegisterMixin(object):
basename = ""
path = ""
@classmethod
def register(cls):
from config.api_router import router
cls.path = cls.path.lstrip("/")
print("ViewSet: %s register, basename: %s" % (cls, cls.basename))
router.register(cls.path, viewset=cls, basename=cls.basename)
5.视图中使用:
python
from rest_framework.viewsets import ModelViewSet
from common.drf.mixins import AutoRegisterMixin
class TestViewSet(
AutoRegisterMixin, # 继承自动注册类
ModelViewSet,
):
path = "scan/record" # url中的uri部分
name = "scan/record" # 名称
queryset = models.TestModel.objects.all()
serializer_class = serializers.TestSerializer
TestViewSet.register() # 调用自动注册
6.最终,生成的接口有以下接口:
http://127.0.0.1:8000/api/scan/record/ 支持GET/POST
http://127.0.0.1:8000/api/scan/record/\<id>/ 支持GET/PUT/DELETE/