feat: bidataset增加字段

This commit is contained in:
caoqianming 2023-11-14 10:23:44 +08:00
parent f8c67d8759
commit 53ca8f1742
4 changed files with 53 additions and 17 deletions

View File

@ -0,0 +1,18 @@
# Generated by Django 3.2.12 on 2023-11-14 02:14
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('bi', '0002_dataset_cache_seconds'),
]
operations = [
migrations.AddField(
model_name='dataset',
name='default_param',
field=models.JSONField(blank=True, default=dict, verbose_name='默认查询参数'),
),
]

View File

@ -2,12 +2,14 @@ from django.db import models
from apps.utils.models import BaseModel, CommonADModel, CommonBDModel from apps.utils.models import BaseModel, CommonADModel, CommonBDModel
# Create your models here. # Create your models here.
class Dataset(CommonBDModel): class Dataset(CommonBDModel):
name = models.CharField('名称', max_length=100) name = models.CharField('名称', max_length=100)
code = models.CharField('标识', max_length=100, default='', blank=True) code = models.CharField('标识', max_length=100, default='', blank=True)
description = models.TextField('描述说明', default='', blank=True) description = models.TextField('描述说明', default='', blank=True)
sql_query = models.TextField('sql查询语句', default='', blank=True) sql_query = models.TextField('sql查询语句', default='', blank=True)
echart_options = models.TextField(default='', blank=True) echart_options = models.TextField(default='', blank=True)
default_param = models.JSONField('默认查询参数', default=dict, blank=True)
cache_seconds = models.PositiveIntegerField('缓存秒数', default=10, blank=True) cache_seconds = models.PositiveIntegerField('缓存秒数', default=10, blank=True)
@ -15,4 +17,4 @@ class Dataset(CommonBDModel):
# name = models.CharField('名称', max_length=100) # name = models.CharField('名称', max_length=100)
# code = models.CharField('标识', max_length=100, default='', blank=True) # code = models.CharField('标识', max_length=100, default='', blank=True)
# js_function = models.TextField('数据转化函数', default='', blank=True) # js_function = models.TextField('数据转化函数', default='', blank=True)
# datasets = models.ManyToManyField(Dataset, verbose_name='关联数据集', blank=True) # datasets = models.ManyToManyField(Dataset, verbose_name='关联数据集', blank=True)

View File

@ -9,13 +9,14 @@ class DatasetCreateUpdateSerializer(CustomModelSerializer):
class Meta: class Meta:
model = Dataset model = Dataset
exclude = EXCLUDE_FIELDS exclude = EXCLUDE_FIELDS
def validate(self, attrs): def validate(self, attrs):
sql_query = attrs.get('sql_query', '') sql_query = attrs.get('sql_query', '')
if sql_query: if sql_query:
check_sql_safe(sql_query) check_sql_safe(sql_query)
return attrs return attrs
class DatasetSerializer(CustomModelSerializer): class DatasetSerializer(CustomModelSerializer):
class Meta: class Meta:
model = Dataset model = Dataset
@ -30,4 +31,6 @@ class DatasetSerializer(CustomModelSerializer):
class DataExecSerializer(serializers.Serializer): class DataExecSerializer(serializers.Serializer):
query = serializers.JSONField(label="查询字典参数", required=False, allow_null=True) query = serializers.JSONField(
label="查询字典参数", required=False, allow_null=True)
is_test = serializers.BooleanField(label='是否测试', default=False)

View File

@ -14,13 +14,14 @@ from rest_framework.exceptions import ParseError
from rest_framework.generics import get_object_or_404 from rest_framework.generics import get_object_or_404
# Create your views here. # Create your views here.
class DatasetViewSet(CustomModelViewSet): class DatasetViewSet(CustomModelViewSet):
queryset = Dataset.objects.all() queryset = Dataset.objects.all()
serializer_class = DatasetSerializer serializer_class = DatasetSerializer
create_serializer_class = DatasetCreateUpdateSerializer create_serializer_class = DatasetCreateUpdateSerializer
update_serializer_class = DatasetCreateUpdateSerializer update_serializer_class = DatasetCreateUpdateSerializer
search_fields = ['name', 'code'] search_fields = ['name', 'code']
def get_object(self): def get_object(self):
""" """
Returns the object the view is displaying. Returns the object the view is displaying.
@ -52,35 +53,45 @@ class DatasetViewSet(CustomModelViewSet):
self.check_object_permissions(self.request, obj) self.check_object_permissions(self.request, obj)
return obj return obj
@action(methods=['post'], detail=True, perms_map={'post': 'dataset.exec'}, serializer_class=DataExecSerializer, cache_seconds=0) @action(methods=['post'], detail=True, perms_map={'post': 'dataset.exec'}, serializer_class=DataExecSerializer, cache_seconds=0)
def exec(self, request, pk=None): def exec(self, request, pk=None):
"""执行sql查询 """执行sql查询
执行sql查询支持code 执行sql查询支持code
""" """
dt = self.get_object() dt: Dataset = self.get_object()
rdata = DatasetSerializer(instance=dt).data rdata = DatasetSerializer(instance=dt).data
query = request.data.get('query', {}) query = request.data.get('query', {})
is_test = request.data.get('is_test', False)
query['r_user'] = request.user.id query['r_user'] = request.user.id
query['r_dept'] = request.user.belong_dept.id if request.user.belong_dept else '' query['r_dept'] = request.user.belong_dept.id if request.user.belong_dept else ''
results = {} results = {}
results2 = {} results2 = {}
can_cache = True can_cache = True
if dt.sql_query: if dt.sql_query:
try: try:
sql_f_ = check_sql_safe(dt.sql_query.format(**query)) sql_f_ = check_sql_safe(dt.sql_query.format(**query))
except KeyError: except KeyError:
if is_test and dt.default_param:
new_query = dt.default_param
new_query.update(query)
try:
sql_f_ = check_sql_safe(dt.sql_query.format(**query))
except KeyError:
raise ParseError('需指定查询参数')
raise ParseError('需指定查询参数') raise ParseError('需指定查询参数')
sql_f_l = sql_f_.strip(';').split(';') sql_f_strip = sql_f_.strip(';')
hash_k = hash(sql_f_.strip(';')) sql_f_l = sql_f_strip.split(';')
hash_k = hash(sql_f_strip)
hash_v = cache.get(hash_k, None) hash_v = cache.get(hash_k, None)
if hash_v: if hash_v:
return Response(hash_v) return Response(hash_v)
with concurrent.futures.ThreadPoolExecutor(max_workers=6) as executor: # 多线程运行并返回字典结果 # 多线程运行并返回字典结果
with concurrent.futures.ThreadPoolExecutor(max_workers=6) as executor:
fun_ps = [] fun_ps = []
for ind, val in enumerate(sql_f_l): for ind, val in enumerate(sql_f_l):
fun_ps.append((f'ds{ind}', execute_raw_sql, val)) fun_ps.append((f'ds{ind}', execute_raw_sql, val))
# 生成执行函数 # 生成执行函数
futures = {executor.submit(i[1], i[2]): i for i in fun_ps} futures = {executor.submit(i[1], i[2]): i for i in fun_ps}
@ -88,7 +99,8 @@ class DatasetViewSet(CustomModelViewSet):
name, *_, sql_f = futures[future] # 获取对应的键 name, *_, sql_f = futures[future] # 获取对应的键
try: try:
res = future.result() res = future.result()
results[name], results2[name]= format_sqldata(res[0], res[1]) results[name], results2[name] = format_sqldata(
res[0], res[1])
except Exception as e: except Exception as e:
results[name] = 'error: ' + str(e) results[name] = 'error: ' + str(e)
can_cache = False can_cache = False
@ -98,11 +110,12 @@ class DatasetViewSet(CustomModelViewSet):
for key in results: for key in results:
if isinstance(results[key], str): if isinstance(results[key], str):
raise ParseError(results[key]) raise ParseError(results[key])
rdata['echart_options'] = format_json_with_placeholders(rdata['echart_options'], **results) rdata['echart_options'] = format_json_with_placeholders(
rdata['echart_options'], **results)
if results and can_cache: if results and can_cache:
cache.set(hash_k, rdata, dt.cache_seconds) cache.set(hash_k, rdata, dt.cache_seconds)
return Response(rdata) return Response(rdata)
@action(methods=['get'], detail=False, perms_map={'get': '*'}) @action(methods=['get'], detail=False, perms_map={'get': '*'})
def base(self, request, pk=None): def base(self, request, pk=None):
all_models = apps.get_models() all_models = apps.get_models()
@ -116,7 +129,8 @@ class DatasetViewSet(CustomModelViewSet):
# 获取字段信息 # 获取字段信息
fields = model._meta.get_fields() fields = model._meta.get_fields()
for field in fields: for field in fields:
rdict[table_name].append({'name': field.name, 'type': field.get_internal_type()}) rdict[table_name].append(
{'name': field.name, 'type': field.get_internal_type()})
return Response(rdict) return Response(rdict)
@ -166,4 +180,3 @@ class DatasetViewSet(CustomModelViewSet):
# rdata['data'] = results # rdata['data'] = results
# return Response(rdata) # return Response(rdata)