场景
省事,不用写urls.py文件相关的代码。
代码
1.全局的config/discovery.py,我放在根目录下的config目录里,位置随意:
import pkgutil
import sys
import typing as tdef import_string(import_name: str, silent: bool = False) -> t.Any:"""Imports an object based on a string. This is useful if you want touse import paths as endpoints or something similar. An import path canbe 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:raiseelse: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 Noneexcept ImportError as e: # noqapassreturn Nonedef 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 toautomatically import all views / controllers so that their metaclasses /function decorators have a chance to register themselves on theapplication.Packages are not returned unless `include_packages` is `True`. This canalso recursively list modules but in that case it will import all thepackages 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 + modnameif ispkg:if include_packages:yield modnameif recursive:yield from find_modules(modname, include_packages, True)else:yield modnamedef 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:passdef discovery():auto_register()
2.全局的config/api_router.py,我放在根目录下的config目录里,位置随意:
from django.conf import settings
from rest_framework.routers import DefaultRouter, SimpleRouterfrom config.discovery import discovery
from sk_scan.users.api.views import UserViewSetif settings.DEBUG:router = DefaultRouter()
else:router = SimpleRouter()router.register("users", UserViewSet)discovery()app_name = "api"
urlpatterns = router.urls
3.全局urls.py文件:
urlpatterns += [# API base urlpath("api/", include("config.api_router")),]
4.自动注册入口,作为工具类,我放在common/drf/mixins.py文件里:
class AutoRegisterMixin(object):basename = ""path = ""@classmethoddef register(cls):from config.api_router import routercls.path = cls.path.lstrip("/")print("ViewSet: %s register, basename: %s" % (cls, cls.basename))router.register(cls.path, viewset=cls, basename=cls.basename)
5.视图中使用:
from rest_framework.viewsets import ModelViewSetfrom common.drf.mixins import AutoRegisterMixinclass TestViewSet(AutoRegisterMixin, # 继承自动注册类ModelViewSet,
):path = "scan/record" # url中的uri部分name = "scan/record" # 名称queryset = models.TestModel.objects.all()serializer_class = serializers.TestSerializerTestViewSet.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/