范文健康探索娱乐情感热点
投稿投诉
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文

从0到1开发自动化运维平台接口文档分页视图和权限配置

  安装依赖pip install djangorestframework-simplejwt pip install django-filter pip install coreapi pip install drf-yasg配置swagger接口文档
  1、添加drf_yasg到settings.pyINSTALLED_APPS = [ ...     "rest_framework",     "drf_yasg",     "cmdb.apps.CmdbConfig", ]
  2、配置路由from rest_framework import permissions from drf_yasg.views import get_schema_view from drf_yasg import openapi  schema_view = get_schema_view(     openapi.Info(         title="DevOps运维平台",         default_version="v1",         description="DevOps运维平台 接口文档",         terms_of_service="",         contact=openapi.Contact(email="qqing_lai@hotmail.com"),         license=openapi.License(name="BSD License"),     ),     public=True,     permission_classes=(permissions.AllowAny,), ) ... urlpatterns = [     path("apidoc/", schema_view.with_ui("swagger",  ... ]restframework配置# drf配置 REST_FRAMEWORK = {     # 自定义分页     "DEFAULT_PAGINATION_CLASS": "common.extends.pagination.CustomPagination",     "PAGE_SIZE": 20,     # 用户登陆认证方式     "DEFAULT_AUTHENTICATION_CLASSES": (         "rest_framework_simplejwt.authentication.JWTAuthentication",         "rest_framework.authentication.SessionAuthentication",         "rest_framework.authentication.BasicAuthentication",     ),     # 全局权限拦截     "DEFAULT_PERMISSION_CLASSES": (         "common.extends.permissions.RbacPermission",     ),     "DEFAULT_SCHEMA_CLASS": "rest_framework.schemas.coreapi.AutoSchema",     "DEFAULT_FILTER_BACKENDS": (         "django_filters.rest_framework.DjangoFilterBackend",         "rest_framework.filters.SearchFilter",         "rest_framework.filters.OrderingFilter",     ),     "DEFAULT_RENDERER_CLASSES": ["rest_framework.renderers.JSONRenderer",                                  "rest_framework.renderers.BrowsableAPIRenderer"] if DEBUG else [         "rest_framework.rend分页扩展#!/usr/bin/env python # -*- encoding: utf-8 -*- """ @author  :   Charles Lai @file    :   pagination.py @time    :   2023/03/26 15:22 @contact :   qqing_lai@hotmail.com """  # here put the import lib from rest_framework.pagination import PageNumberPagination, LimitOffsetPagination from rest_framework.response import Response from rest_framework import status   class CustomPagination(PageNumberPagination):     def get_paginated_response(self, data):         return Response({"data": {"list": data, "total": self.page.paginator.count, "next": self.get_next_link(),             "previous": self.get_previous_link()}, "success": True, "errorCode": 0, "errorMessage": None}, status=status.HTTP_200_OK) 自定义公共视图#!/usr/bin/env python # -*- encoding: utf-8 -*- """ @author  :   Charles Lai @file    :   viewsets.py @time    :   2023/03/26 14:42 @contact :   qqing_lai@hotmail.com """  # here put the import lib import inspect from rest_framework.permissions import IsAuthenticated from rest_framework.response import Response from rest_framework import status from rest_framework import viewsets from rest_framework import pagination from rest_framework.settings import api_settings from rest_framework.filters import OrderingFilter from django.db.models.query import QuerySet from django.db.models import ProtectedError from django.core.cache import cache import pytz import logging  logger = logging.getLogger(__name__)   def ops_response(data, success=True, errorCode=0, errorMessage=None, status=status.HTTP_200_OK):     """     返回自定义     data列表数据格式:{  list: [  ],  current?: number,  pageSize?: number,  total?: number, }     """     return Response({"data": data, "success": success, "errorCode": errorCode, "errorMessage": errorMessage}, status=status)   class AutoModelViewSet(viewsets.ModelViewSet):     """     A viewset that provides default `create()`, `retrieve()`, `update()`,     `partial_update()`, `destroy()` and `list()` actions.     """      permission_classes = [IsAuthenticated]     permission_classes_by_action = {}     filter_backends = (OrderingFilter, )      def __init__(self, *args, **kwargs):         if not hasattr(self, "queryset"):             raise AttributeError("必须定义 类属性 queryset")          if not hasattr(self, "serializer_class"):             raise AttributeError("必须定义 类属性 serializer_class")          super().__init__(*args, **kwargs)      def get_serializer(self, *args, **kwargs):         """         重写 get_serializer 类,用来支持自动获取不同的 serializer_class         例子:  list 方法, 设置一个serializer_list_class, 则调用get_serializer的时候, 优先获取         命名格式 serializer_{call_func_name}_class         :param args:         :param kwargs:         :return:         """         call_func_name = inspect.stack()[1][3]         serializer_class = getattr(self, f"serializer_{call_func_name}_class", None)         if not serializer_class:             serializer_class = self.get_serializer_class()         kwargs["context"] = self.get_serializer_context()         return serializer_class(*args, **kwargs)      def get_object(self):         return super(AutoModelViewSet, self).get_object()      def get_permissions(self):         try:             return [permission() for permission in self.permission_classes_by_action[self.action]]         except KeyError:             return [permission() for permission in self.permission_classes]              def get_permission_from_role(self, request):         try:             perms = request.user.roles.values(                 "permissions__method",             ).distinct()             return [p["permissions__method"] for p in perms]         except (AttributeError, TypeError):             return []      def extend_filter(self, queryset):         return queryset      def get_queryset(self):         assert self.queryset is not None, (                 ""%s" should either include a `queryset` attribute, "                 "or override the `get_queryset()` method."                 % self.__class__.__name__         )         queryset = self.extend_filter(self.queryset)         if isinstance(queryset, QuerySet):             queryset = queryset.all()         return queryset.distinct()      def create(self, request, *args, **kwargs):         try:             request.data["name"] = request.data["name"].strip(" ").replace(" ", "-")         except BaseException as e:             logger.debug("exception ", str(e))         serializer = self.get_serializer(data=request.data)         if not serializer.is_valid():             return ops_response({}, success=False, errorCode=40000, errorMessage=serializer.errors)         try:             self.perform_create(serializer)         except BaseException as e:             return ops_response({}, success=False, errorCode=50000, errorMessage=str(e), status=status.HTTP_500_INTERNAL_SERVER_ERROR)         return ops_response(serializer.data)      def list(self, request, pk=None, *args, **kwargs):         queryset = self.filter_queryset(self.get_queryset())         page_size = request.query_params.get("page_size", None)         if not page_size:             page_size = api_settings.PAGE_SIZE         pagination.PageNumberPagination.page_size = page_size         page = self.paginate_queryset(queryset)         if page is not None:             serializer = self.get_serializer(page, many=True)             return self.get_paginated_response(serializer.data)         serializer = self.get_serializer(queryset, many=True)         return ops_response({"list": serializer.data, "total": queryset.count()})      def update(self, request, *args, **kwargs):         instance = self.get_object()         partial = kwargs.pop("partial", False)         try:             request.data["name"] = request.data["name"].strip(" ").replace(" ", "-")         except BaseException as e:             logger.warning(f"不包含name字段: {str(e)}")         serializer = self.get_serializer(instance, data=request.data, partial=partial)         if not serializer.is_valid():             return ops_response({}, success=False, errorCode=40000, errorMessage=serializer.errors)         try:             self.perform_update(serializer)         except BaseException as e:             return ops_response({}, success=False, errorCode=50000, errorMessage=str(e))          if getattr(instance, "_prefetched_objects_cache", None):             instance._prefetched_objects_cache = {}         data = {"data": serializer.data, "status": "success", "code": 20000}         return ops_response(serializer.data)      def retrieve(self, request, *args, **kwargs):         instance = self.get_object()         serializer = self.get_serializer(instance)         return ops_response(serializer.data)      def destroy(self, request, *args, **kwargs):         instance = self.get_object()         try:             self.perform_destroy(instance)         except ProtectedError:             # 存在关联数据,不可删除             return ops_response({}, success=False, errorCode=40300, errorMessage="存在关联数据,禁止删除!")         except BaseException as e:             logger.exception(f"删除数据发生错误 {e}, {e.__class__}")             return ops_response({}, success=False, errorCode=50000, errorMessage=f"删除异常: {str(e)}")         return ops_response("删除成功")   class AutoModelParentViewSet(AutoModelViewSet):      def get_queryset(self):         assert self.queryset is not None, (                 ""%s" should either include a `queryset` attribute, "                 "or override the `get_queryset()` method."                 % self.__class__.__name__         )         queryset = self.extend_filter(self.queryset)         if self.action == "list":             if not self.request.query_params.get("search"):                 queryset = queryset.filter(parent__isnull=True)         if isinstance(queryset, QuerySet):             queryset = queryset.all()         return queryset.distinct() 自定义权限校验#!/usr/bin/env python # -*- encoding: utf-8 -*- """ @author  :   Charles Lai @file    :   permissions.py @time    :   2023/03/26 15:27 @contact :   qqing_lai@hotmail.com """  # here put the import lib from rest_framework.permissions import BasePermission from config import platform  import logging  logger = logging.getLogger(__name__)   class RbacPermission(BasePermission):     """     自定义权限     """      @classmethod     def check_is_admin(cls, request):         return request.user.is_authenticated and request.user.roles.filter(name="管理员").count() > 0      @classmethod     def get_permission_from_role(cls, request):         try:             perms = request.user.roles.values(                 "permissions__method",             ).distinct()             return [p["permissions__method"] for p in perms]         except AttributeError:             return []      def _has_permission(self, request, view):         """         权限获取方式             从 perms_map 中获取, 通过 request.method, http 请求方法来获取对应权限点             1. 默认格式                 perms_map = (                     {"*": ("admin", "管理员")},                     {"*": ("k8s_all", "k8s管理")},                     {"get": ("k8s_list", "查看k8s")},                     {"post": ("k8s_create", "创建k8s")},                     {"put": ("k8s_edit", "编辑k8s")},                     {"delete": ("k8s_delete", "删除k8s")}                 )             2. 自定义方法格式                 perms_map = (                     {"get_test_data": ("get_test_data", "获取测试数据")},                 )                 此时 格式为  {http请求方法}_{ViewSet自定义action}          :param request: rest_framework request 对象         :param view: rest_framework view 对象         :return:         """         _method = request._request.method.lower()         url_whitelist = platform["whitelist"] if platform else []         path_info = request.path_info         for item in url_whitelist:             url = item["url"]             if url in path_info:                 logger.debug(f"请求地址 {path_info} 命中白名单 {url}, 放行")                 return True                      is_superuser = request.user.is_superuser         # 超级管理员 或者 白名单模式 直接放行         if is_superuser:             logger.debug(f"用户 {request.user} 是超级管理员, 放行 is_superuser = {is_superuser}")             return True          is_admin = RbacPermission.check_is_admin(request)         perms = self.get_permission_from_role(request)         # 不是管理员 且 权限列表为空的情况下, 直接拒绝         if not is_admin and not perms:             logger.debug(f"用户 {request.user} 不是管理员 且 权限列表为空, 直接拒绝")             return False          perms_map = view.perms_map         # 未配置权限映射的视图一律禁止访问         if not hasattr(view, "perms_map"):             logger.debug(f"未配置权限映射的视图一律禁止访问 {view}")             return False          # _custom_method = None         # default_funcs = ["create", "list", "retrieve", "update", "destroy"]         action = view.action         _custom_method = f"{_method}_{action}"         for i in perms_map:             logger.debug(f"perms_map item ===  {i}")             for method, alias in i.items():                 # 如果是管理员, 判断当前perms_map是否带有 {"*": ("admin", "管理员")} 标记,如果有, 则当前 ViewSet 所有方法全放行                 if is_admin and (method == "*" and alias[0] == "admin"):                     logger.debug("管理员判断通过, 放行")                     return True                 # 如果带有某个模块的管理权限, 则当前模块所有方法都放行                 if method == "*" and alias[0] in perms:                     logger.debug("模块管理权限 判断通过, 放行")                     return True                  # 判断自定义action的情况                 # {"get_test_data": ("get_test_data", "获取测试数据")},                 # {"*_test_data": ("get_test_data", "获取测试数据")},                 if _custom_method and alias[0] in perms and (_custom_method == method or method == f"*_{action}"):                     logger.debug("自定义action权限 判断通过, 放行")                     return True                  # 判断是否拥有ViewSet 某个方法的权限, 有则放行                 # {"get": ("workflow_list", "查看工单")},                 if _method == method and alias[0] in perms:                     logger.debug(f"{method}方法权限 判断通过, 放行")                     return True         logger.debug(f"{path_info} 没有符合条件的, 则默认禁止访问")         return False      def has_permission(self, request, view):         res = self._has_permission(request, view)         # 记录权限异常的操作         if not res:             pass         return res   class AdminPermission(BasePermission):      def has_permission(self, request, view):         if RbacPermission.check_is_admin(request):             return True         return False   class ObjPermission(BasePermission):     """     密码管理对象级权限控制     """      def has_object_permission(self, request, view, obj):         perms = RbacPermission.get_permission_from_role(request)         if "admin" in perms:             return True         elif request.user.id == obj.uid_id:             return True 更新cmdb模块视图
  我们之前已经完成的view_cmdb.py文件里,有几处需要更新:
  1、将viewsets.ModelViewSet更改成公共视图里的AutoModelViewSet
  2、将Response更改成ops_reponse,确保返回内容格式一致.运行项目(venv) ➜  ydevops-backend  cd /home/charles/ydevops-backend ; /usr/bin/env /home/charles/ydevops-backend/venv/bin/python /home/charles/.vscode-server/extensions/ms-python.python-2023.5.10791008/pythonFiles/lib/python/debugpy/adapter/../../debugpy/launcher 41223 -- /home/charles/ydevops-backend/manage.py runserver 0.0.0.0:9000  Watching for file changes with StatReloader Performing system checks...  System check identified no issues (0 silenced). March 26, 2023 - 18:50:30 Django version 4.1.7, using settings "devops_backend.settings" Starting development server at http://0.0.0.0:9000/ Quit the server with CONTROL-C.
  访问接口文档 http://localhost:9000/apidoc/
  在这个页面,我们可以做一些CRUD操作,如查看环境(由于数据量小,可以先把settings.py里的默认PAGE_SIZE改为1)
  Okay,今天先到这吧...

女星乐宜晒比基尼美照,趴沙滩上翘臀纹身太抢镜,修长美腿很吸睛最近,香港女艺术家乐宜也抓住了夏天的尾巴。她在社交平台上发布了一组性感美丽的照片,这些照片是她在海边度假时的照片,也是比基尼性感美丽的照片,展示了一个不同的她。我不得不说,谁不喜欢敷尔佳上市过会产品暴利,被疑传销,曾分红超10亿,占70利润近日,敷尔佳于深圳创业板首发过会。凭着医美面膜这一称号,成立不到5年的敷尔佳过去四年净赚23亿元,且2021年综合毛利率达82,位居国内同行业第一。它大幅超出美妆行业70左右的平均大胸怎么搭配显瘦胸大女生必备的穿搭小技巧夏天皮肤暴露,很多女性比较苦恼,没有办法改变乳房,减肥也不能立竿见影。暴露出来的问题会越来越多,无论穿什么样的衣服,眼神都会变得更加专注。其实很多时候,是因为他们没有掌握穿衣搭配的蚕丝干湿巾女性和婴幼儿护理秋天天气干燥,早晚温差大,皮肤屏障比较脆弱的人群如婴幼儿,往往容易皮肤干燥且容易感冒,那清洁护理就成了护理重中之重。一般护理用品有棉柔巾干湿巾等,擦拭皮肤用等产品。干湿巾属于一次性分享五款热门好用的抗氧化精华,抗老还美白,值得尝试自从早C晚A的护肤方式深入人心,抗氧化提亮已经成为了很多姐妹日常不可或缺的护肤步骤。提到抗氧化精华,大家首先想到的就是VC成分。VC的抗氧化实力相当优秀,但同时也是一款比较娇气的成美妆圈再迎潜力股概念无水美妆或成下一片蓝海作者孙笑笑编辑谢康玉成分党纯净纯素美妆绿色有机等趋势之后,越来越多的品牌开始押注无水美容。据WGSN发布的2022年世界流行美容趋势报告显示,节水环保快速化妆人与人之间的关系,不要走的太近中国自古以来就是礼仪之邦,礼尚往来是我们中华民族的传统文化,经过几千年的总结和洗礼,形成了今天我们民族人与人交往的一些原则和准则,是刻在我们每一个中国人骨子里的东西。生活中各种矛盾人与人的差距究竟在哪?有人工作,有人上学,大家千万不要错过这篇文章,能看到这篇文章也是一种幸运,真的受益匪浅,对我有很大启迪,这篇文章将会改变你我的一生,真的太好了,希望与有缘人分享,也希望对有缘人有所你有多自律,人生就有多美好一个人闲着无聊的时侯,随意地打开手机,看到了一篇励志的美文,看完之后,心里深深地受到触动。大千世界,芸芸众生。普通人的每一天,都在为生活奔波忙碌,在他们的脸上看到的是满眼焦虑和惆怅一个人是富贵命,还是贫穷命,就看他每天的生活习惯01hr曾国藩说人生一日,或闻一善言,见一善行,行一善事,此日方不虚生遇富贵人,宜劝他宽,见聪明人宜劝他厚。一个人命中是富贵,还是贫穷,可以从他的每天生活习惯中表现出来。有句话叫我仁波切谈人生关于幸福幸福就是让自己的心安定下来,珍惜拥有,感恩一切。真正的慷慨是没有执着,真正的戒律是没有贪欲,真正的忍辱是没有嗔怒。天分是老天给的,应该保持谦虚荣誉是人民给的,应该表示感谢骄
巩俐姐姐把嫁妆塞给自己当学费,姐姐去世,巩俐含泪把孩子养大八旬老夫人坐在月光下面深深地叹了一口气嗨!又是一个团圆日!老夫人的眼泪止不住地滚下了脸颊。妈,吃月饼了!巩俐和三个嫂子端着刚刚烤好的月饼走到母亲的身边。看到母亲湿润的眼睛,就知道她华尔街惊天诈骗案尘埃落地,天才美女创业诈骗给投资人带来的启发最近一个新闻让leo感到非常沮丧,因为这是投资中根本避不开的坑,尤其是对于绝大多数成长风格投资者而言。前几天,美国加州血液检测公司Theranos创始人伊丽莎白霍尔姆斯(Eliza广州作为一线城市,物价到底有多亲民?外地游客都惊呆了这里是刘小顺的旅行和生活研究所。很多人都说,广州是北上广深这四大一线城市里最有烟火气息最适合生活的一座一线城市,因为广州的物价很亲民。刚开始,我还没什么感觉,广州毕竟是一线城市,物江苏省的区划变动,13个地级市之一,盐城市为何有9个区县?在之前的文章中给大家介绍了很多的城市地区划变迁沿革,因为我国的历史比较悠久,而且疆域规模比较庞大,五千年的历史文化传承让每个地方的变迁都非常的复杂,尤其是我国沿海一些城市,由于常年全国大奖!深圳6所学校获得7块金牌,有你的学校吗?近日,由教育部中央统战部中央网络安全和信息化委员会办公室等共同主办,重庆大学承办的第八届中国国际互联网大学生创新创业大赛决赛圆满落幕。深圳表现出色,共有6所学校上榜,获得7块金牌(湖北经济学院财经类准一本李老师高考升学详解湖北经济学院是湖北省人民政府举办的全日制普通本科院校,国务院学位委员会批准的硕士学位授予单位,国家十四五教育强国推进工程支持高校。学校办学历史最早可追溯到1907年张之洞创办的湖北学习成绩差的孩子在学校真的好可怜头条创作挑战赛望子成龙望女成凤是每个家长的心愿,但是在孩子上学的过程中,由于种种原因,导致一部分学生在学习上逐步落伍了,也就成了我们口中的成绩差的孩子。大家都知道,在学校老师对学习女儿又被欺负了,对于不讲理的家长,这次我选择不再沉默家长百问百答文兰妈谈育儿著名教育家陶行知说过集体生活,是孩子向社会发展道路上最重要的一个推动力,也是孩子心理正常健康发展的必须,如果孩子不能获得这种正常发展,可能终其身都是一份遗憾孩子有这些行为,是缺爱的表现,家长需要多陪伴曾有著名的心理学团队研究表示,一个合格的养育者的标准只需达到50的正确即可。因此每个人都一样,内心都存在不同程度的缺失感。从孩子出生开始,对父母就是依赖的,极高的陪伴的关心需求性,大S的伪大女主人设,3个细节证明,她只是个风一吹就破的泡沫大S的体面人设崩了?3个细节看出大S的气急败坏引言。大S一家有哪些奇女子大s原生家庭汪小菲和大S两家的家庭伦理剧,短时间看来是不会结束了。两人吵架的场景就像是一个人疯狂跳脚,气得半林青霞与邓丽君法国裸泳,把私照送男闺蜜,因男人与邓丽君绝交在林青霞倾国倾城的容颜背后隐藏着一颗狂野的心,她的感情经历非常乱,虽然追她的富豪和明星无数,但她却在两个男人之间徘徊了二十年年,此外林青霞还大胆的把自己私照送给搞艺术的男闺蜜张叔平