我正在使用 DRF 和 swagger UI 文档制作一个 API, 我想将基本的 CRUD 操作作为动态视图集类,这样我就可以将类用于另一个模型, 但我有问题。我使用 @swagger_auto_schema 和 @action 作为装饰器。这里的例子 对于我获取所有数据表:
#dynamic_crud.py
class DynamicCrud(viewsets.ViewSet):
def __init__(self, serializer_class, model_class, table_name):
self.serializer_class = serializer_class
self.model_class = model_class
self.table_name = table_name
def api_response(self, message, status, data=None):
return {'message': message, 'status': status, 'data': data}
#here is the problem
@swagger_auto_schema(
operation_summary=f'Get all {table_name}',
responses={200: serializer_class(many=True)}
)
@action(detail=False, methods=['get'], url_path='getall', url_name=f'{table_name}_getall')
def get_all(self, request, *args, **kwargs):
try:
snippets = self.model_class.objects.filter(is_deleted=0)
serializer = self.serializer_class(snippets, many=True)
response_data = self.api_response(f'{self.table_name} retrieved successfully', 'success', serializer.data)
return Response(response_data, status=status.HTTP_200_OK)
except Exception as e:
response_data = self.api_response(str(e), 'error')
return Response(response_data, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
我将装饰器放在类中,装饰器需要引用数据。这个类是父类,数据引用在子类中。
#views.py
table_data = [
{
'serializer_class': serializer.TblMCategories_serializer,
'model_class': models.TblMCategories,
'table_name': 'category'
},
{
'serializer_class': serializer.TblMProduct_serializer,
'model_class': models.TblMProduct,
'table_name': 'product'
},
# {
# rest of my table data...
# }
]
class TblMCategories(dynamic_crud.DynamicCrud):
def __init__(self):
self.serializer_class = table_data[0]['serializer_class']
self.model_class = table_data[0]['model_class']
self.table_name = table_data[0]['table_name']
我知道我不能在装饰器中使用类自数据,因为它尚未声明或自数据在代码构建期间不可用。有没有什么方法可以将数据作为 self 放入装饰器中,或者使我的装饰器具有所需的引用数据?
由于
self
仅在实际调用装饰方法时才可用,因此这里的技巧是避免在类定义时装饰该方法。
相反,您可以创建一个临时装饰器,在调用方法时使用
swagger_auto_schema
动态装饰给定方法,以便在装饰方法之前可以使用从 swagger_auto_schema
派生的参数传递 self
。
改变:
@swagger_auto_schema(
operation_summary=f'Get all {table_name}',
responses={200: serializer_class(many=True)}
)
至:
@(lambda method:
lambda self, *args, **kwargs:
swagger_auto_schema(
operation_summary=f'Get all {self.table_name}',
responses={200: serializer_class(many=True)}
)(method)(self, *args, **kwargs)
)
这同样适用于
action
:
改变:
@action(detail=False, methods=['get'], url_path='getall', url_name=f'{table_name}_getall')
至:
@(lambda method:
lambda self, *args, **kwargs:
action(
detail=False,
methods=['get'],
url_path='getall',
url_name=f'{self.table_name}_getall'
)(method)(self, *args, **kwargs)
)
最终结果是如下代码:
class DynamicCrud(viewsets.ViewSet):
@(lambda method:
lambda self, *args, **kwargs:
swagger_auto_schema(
operation_summary=f'Get all {self.table_name}',
responses={200: serializer_class(many=True)}
)(method)(self, *args, **kwargs)
)
@(lambda method:
lambda self, *args, **kwargs:
action(
detail=False,
methods=['get'],
url_path='getall',
url_name=f'{self.table_name}_getall'
)(method)(self, *args, **kwargs)
)
def get_all(self, request, *args, **kwargs):
...
我怎么会忘记OOP的基础,解决方案很简单。我不应该将数据发送回父类作为参考。相反,我将函数继承给子类,并在子类 python 文件中有我需要的引用数据的地方运行装饰器。
table_data = [
{
'serializer_class': serializer.TblMCategories_serializer,
'model_class': models.TblMCategories,
'table_name': 'category'
},
# rest my table references data
]
class TblMCategories(dynamic_crud_viewset.DynamicCrud):
def __init__(self):
super().__init__(
serializer_class=table_data[0]['serializer_class'],
model_class=table_data[0]['model_class'],
table_name=table_data[0]['table_name']
)
#run decorator in child class
@swagger_auto_schema(
operation_summary=f'Get {table_data[0]["table_name"]} by ID',
responses={200: serializer.TblMCategories_serializer()}
)
@action(detail=True, methods=['get'], url_path='getbyid',url_name=f'{table_data[0]["table_name"]}_getbyid')
def get_all(self, request, *args, **kwargs):
return super().get_all(request, *args, **kwargs)