From 0437e9d753095dd45cbfbdadff84b20b7c550082 Mon Sep 17 00:00:00 2001 From: Chunlin Zhang Date: Wed, 22 May 2019 14:18:41 +0800 Subject: [PATCH 1/4] more strict check with model name in get action; fix request_tag should get with tag not with model name --- uliweb_apijson/apijson/views.py | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/uliweb_apijson/apijson/views.py b/uliweb_apijson/apijson/views.py index f1bf6c7..7ef0379 100644 --- a/uliweb_apijson/apijson/views.py +++ b/uliweb_apijson/apijson/views.py @@ -208,6 +208,8 @@ def _get_array(self,key): name = n[:-1] if hasattr(model,name): q = q.filter(getattr(model.c,name).like(model_param[n])) + else: + return json({"code":400,"msg":"'%s' does not have '%s'"%(modelname,name)}) elif n[-1]=="}" and n[-2]=="{": name = n[:-2] if hasattr(model,name): @@ -370,10 +372,9 @@ def _post_one(self,key,tag): log.error("try to find model '%s' but not found: '%s'"%(model_name,e)) return json({"code":400,"msg":"model '%s' not found"%(model_name)}) - request_tag_config = request_tag.get(model_name,{}) - if not request_tag_config: + if not request_tag: return json({"code":400,"msg":"tag '%s' not found"%(tag)}) - tag_POST = request_tag_config.get("POST",{}) + tag_POST = request_tag.get("POST",{}) if not tag_POST: return json({"code":400,"msg":"tag '%s' not support apijson_post"%(tag)}) ADD = tag_POST.get("ADD") @@ -482,10 +483,9 @@ def _put_one(self,key,tag): log.error("try to find model '%s' but not found: '%s'"%(model_name,e)) return json({"code":400,"msg":"model '%s' not found"%(model_name)}) - request_tag_config = request_tag.get(model_name,{}) - if not request_tag_config: + if not request_tag: return json({"code":400,"msg":"tag '%s' not found"%(tag)}) - tag_PUT = request_tag_config.get("PUT",{}) + tag_PUT = request_tag.get("PUT",{}) ADD = tag_PUT.get("ADD") if ADD: ADD_role = ADD.get("@role") @@ -604,10 +604,9 @@ def _delete_one(self,key,tag): log.error("try to find model '%s' but not found: '%s'"%(model_name,e)) return json({"code":400,"msg":"model '%s' not found"%(model_name)}) - request_tag_config = request_tag.get(model_name,{}) - if not request_tag_config: + if not request_tag: return json({"code":400,"msg":"tag '%s' not found"%(tag)}) - tag_DELETE = request_tag_config.get("DELETE",{}) + tag_DELETE = request_tag.get("DELETE",{}) ADD = tag_DELETE.get("ADD") if ADD: ADD_role = ADD.get("@role") From 4a5e758bb8502ea4933782ee2a48c51585576891 Mon Sep 17 00:00:00 2001 From: Chunlin Zhang Date: Wed, 22 May 2019 15:46:21 +0800 Subject: [PATCH 2/4] modelname -> model_name --- uliweb_apijson/apijson/views.py | 58 ++++++++++++++++----------------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/uliweb_apijson/apijson/views.py b/uliweb_apijson/apijson/views.py index 7ef0379..144ab97 100644 --- a/uliweb_apijson/apijson/views.py +++ b/uliweb_apijson/apijson/views.py @@ -50,22 +50,22 @@ def get(self): return json(self.rdict) def _get_one(self,key): - modelname = key + model_name = key params = self.request_data[key] params_role = params.get("@role") try: - model = getattr(models,modelname) - model_setting = settings.APIJSON_MODELS.get(modelname,{}) + model = getattr(models,model_name) + model_setting = settings.APIJSON_MODELS.get(model_name,{}) except ModelNotFound as e: - log.error("try to find model '%s' but not found: '%s'"%(modelname,e)) - return json({"code":400,"msg":"model '%s' not found"%(modelname)}) + log.error("try to find model '%s' but not found: '%s'"%(model_name,e)) + return json({"code":400,"msg":"model '%s' not found"%(model_name)}) model_column_set = None q = model.all() GET = model_setting.get("GET") if not GET: - return json({"code":400,"msg":"'%s' not accessible"%(modelname)}) + return json({"code":400,"msg":"'%s' not accessible"%(model_name)}) roles = GET.get("roles") permission_check_ok = False @@ -75,7 +75,7 @@ def _get_one(self,key): else: params_role = "UNKNOWN" if params_role not in roles: - return json({"code":400,"msg":"'%s' not accessible by role '%s'"%(modelname,params_role)}) + return json({"code":400,"msg":"'%s' not accessible by role '%s'"%(model_name,params_role)}) if params_role == "UNKNOWN": permission_check_ok = True elif functions.has_role(request.user,params_role): @@ -88,7 +88,7 @@ def _get_one(self,key): if params_role=="OWNER": owner_filtered,q = self._filter_owner(model,model_setting,q) if not owner_filtered: - return json({"code":400,"msg":"'%s' cannot filter with owner"%(modelname)}) + return json({"code":400,"msg":"'%s' cannot filter with owner"%(model_name)}) params = self.request_data[key] if isinstance(params,dict): @@ -99,7 +99,7 @@ def _get_one(self,key): elif hasattr(model,n): q = q.filter(getattr(model.c,n)==params[n]) else: - return json({"code":400,"msg":"'%s' have no attribute '%s'"%(modelname,n)}) + return json({"code":400,"msg":"'%s' have no attribute '%s'"%(model_name,n)}) o = q.one() if o: o = o.to_dict() @@ -116,7 +116,7 @@ def _get_one(self,key): def _get_array(self,key): params = self.request_data[key] - modelname = None + model_name = None model_param = None model_column_set = None @@ -147,14 +147,14 @@ def _get_array(self,key): for n in params: if n[0]!="@": # TODO: support join in the future, now only support 1 model - modelname = n + model_name = n break - if not modelname: + if not model_name: return json({"code":400,"msg":"no model found in array query"}) #model settings - model_setting = settings.APIJSON_MODELS.get(modelname,{}) + model_setting = settings.APIJSON_MODELS.get(model_name,{}) secret_fields = model_setting.get("secret_fields") #model params @@ -164,10 +164,10 @@ def _get_array(self,key): if model_column: model_column_set = set(model_column.split(",")) try: - model = getattr(models,modelname) + model = getattr(models,model_name) except ModelNotFound as e: - log.error("try to find model '%s' but not found: '%s'"%(modelname,e)) - return json({"code":400,"msg":"model '%s' not found"%(modelname)}) + log.error("try to find model '%s' but not found: '%s'"%(model_name,e)) + return json({"code":400,"msg":"model '%s' not found"%(model_name)}) #order model_order = model_param.get("@order") @@ -175,7 +175,7 @@ def _get_array(self,key): GET = model_setting.get("GET") if not GET: - return json({"code":400,"msg":"'%s' not accessible by apijson"%(modelname)}) + return json({"code":400,"msg":"'%s' not accessible by apijson"%(model_name)}) roles = GET.get("roles") params_role = model_param.get("@role") @@ -186,7 +186,7 @@ def _get_array(self,key): else: params_role = "UNKNOWN" if params_role not in roles: - return json({"code":400,"msg":"'%s' not accessible by role '%s'"%(modelname,params_role)}) + return json({"code":400,"msg":"'%s' not accessible by role '%s'"%(model_name,params_role)}) if params_role == "UNKNOWN": permission_check_ok = True elif functions.has_role(request.user,params_role): @@ -200,7 +200,7 @@ def _get_array(self,key): if params_role == "OWNER": owner_filtered,q = self._filter_owner(model,model_setting,q) if not owner_filtered: - return json({"code":400,"msg":"'%s' cannot filter with owner"%(modelname)}) + return json({"code":400,"msg":"'%s' cannot filter with owner"%(model_name)}) for n in model_param: if n[0]!="@": @@ -209,7 +209,7 @@ def _get_array(self,key): if hasattr(model,name): q = q.filter(getattr(model.c,name).like(model_param[n])) else: - return json({"code":400,"msg":"'%s' does not have '%s'"%(modelname,name)}) + return json({"code":400,"msg":"'%s' does not have '%s'"%(model_name,name)}) elif n[-1]=="}" and n[-2]=="{": name = n[:-2] if hasattr(model,name): @@ -280,22 +280,22 @@ def head(self): return json(self.rdict) def _head(self,key): - modelname = key + model_name = key params = self.request_data[key] params_role = params.get("@role") try: - model = getattr(models,modelname) - model_setting = settings.APIJSON_MODELS.get(modelname,{}) + model = getattr(models,model_name) + model_setting = settings.APIJSON_MODELS.get(model_name,{}) except ModelNotFound as e: - log.error("try to find model '%s' but not found: '%s'"%(modelname,e)) - return json({"code":400,"msg":"model '%s' not found"%(modelname)}) + log.error("try to find model '%s' but not found: '%s'"%(model_name,e)) + return json({"code":400,"msg":"model '%s' not found"%(model_name)}) q = model.all() HEAD = model_setting.get("HEAD") if not HEAD: - return json({"code":400,"msg":"'%s' not accessible"%(modelname)}) + return json({"code":400,"msg":"'%s' not accessible"%(model_name)}) roles = HEAD.get("roles") permission_check_ok = False @@ -305,7 +305,7 @@ def _head(self,key): else: params_role = "UNKNOWN" if params_role not in roles: - return json({"code":400,"msg":"'%s' not accessible by role '%s'"%(modelname,params_role)}) + return json({"code":400,"msg":"'%s' not accessible by role '%s'"%(model_name,params_role)}) if params_role == "UNKNOWN": permission_check_ok = True elif functions.has_role(request.user,params_role): @@ -318,14 +318,14 @@ def _head(self,key): if params_role=="OWNER": owner_filtered,q = self._filter_owner(model,model_setting,q) if not owner_filtered: - return json({"code":400,"msg":"'%s' cannot filter with owner"%(modelname)}) + return json({"code":400,"msg":"'%s' cannot filter with owner"%(model_name)}) for n in params: if n[0]=="@": pass else: param = params[n] if not hasattr(model.c,n): - return json({"code":400,"msg":"'%s' don't have field '%s'"%(modelname,n)}) + return json({"code":400,"msg":"'%s' don't have field '%s'"%(model_name,n)}) q = model.filter(getattr(model.c,n)==param) rdict = { "code":200, From 01852056da884e522a3a85d4f6eca30f166bfeaa Mon Sep 17 00:00:00 2001 From: Chunlin Zhang Date: Thu, 23 May 2019 14:31:51 +0800 Subject: [PATCH 3/4] add @expr support in apijson_get array --- uliweb_apijson/apijson/views.py | 82 ++++++++++++++++++++++++++------- 1 file changed, 66 insertions(+), 16 deletions(-) diff --git a/uliweb_apijson/apijson/views.py b/uliweb_apijson/apijson/views.py index 144ab97..9bcbe16 100644 --- a/uliweb_apijson/apijson/views.py +++ b/uliweb_apijson/apijson/views.py @@ -1,6 +1,7 @@ #coding=utf-8 -from uliweb import expose, functions, models +from uliweb import expose, functions, models, UliwebError from uliweb.orm import ModelNotFound +from sqlalchemy.sql import and_, or_, not_ from json import loads import logging import traceback @@ -202,21 +203,16 @@ def _get_array(self,key): if not owner_filtered: return json({"code":400,"msg":"'%s' cannot filter with owner"%(model_name)}) - for n in model_param: - if n[0]!="@": - if n[-1]=="$": - name = n[:-1] - if hasattr(model,name): - q = q.filter(getattr(model.c,name).like(model_param[n])) - else: - return json({"code":400,"msg":"'%s' does not have '%s'"%(model_name,name)}) - elif n[-1]=="}" and n[-2]=="{": - name = n[:-2] - if hasattr(model,name): - # TODO - pass - elif hasattr(model,n): - q = q.filter(getattr(model.c,n)==model_param[n]) + model_expr = model_param.get("@expr") + + if model_expr: + c = self._expr(model,model_param,model_expr) + q = q.filter(c) + else: + for n in model_param: + if n[0]!="@": + c = self._get_filter_condition(model,model_param,n) + q = q.filter(c) if query_type in [1,2]: self.vars["/%s/total"%(key)] = q.count() @@ -266,6 +262,60 @@ def _filter_owner(self,model,model_setting,q): owner_filtered = True return owner_filtered,q + def _expr(self,model,model_param,model_expr): + if not isinstance(model_expr,list): + raise UliwebError("only accept array in @expr: '%s'"%(model_expr)) + num = len(model_expr) + if (num<2 or num>3): + raise UliwebError("only accept 2 or 3 items in @expr: '%s'"%(model_expr)) + op = model_expr[-2] + if op=='&': + if num!=3: + raise UliwebError("'&'(and) expression need 3 items: '%s'"%(model_expr)) + c1 = self._get_filter_condition(model,model_param,model_expr[0],expr=True) + c2 = self._get_filter_condition(model,model_param,model_expr[2],expr=True) + return and_(c1,c2) + elif op=='|': + if num!=3: + raise UliwebError("'|'(or) expression need 3 items: '%s'"%(model_expr)) + c1 = self._get_filter_condition(model,model_param,model_expr[0],expr=True) + c2 = self._get_filter_condition(model,model_param,model_expr[2],expr=True) + return or_(c1,c2) + elif op=='!': + if num!=2: + raise UliwebError("'!'(not) expression need 2 items: '%s'"%(model_expr)) + return not_(self._get_filter_condition(model,model_param,model_expr[1],expr=True)) + else: + raise UliwebError("unknown operator: '%s'"%(op)) + + def _get_filter_condition(self,model,model_param,item,expr=False): + if isinstance(item,list): + if expr: + return self._expr(model,model_param,model_expr=item) + else: + raise UliwebError("item can be array only in @expr: '%s'"%(item)) + if not isinstance(item,str): + raise UliwebError("item should be array or string: '%s'"%(item)) + n = item + if n[0]=="@": + raise UliwebError("param key should not begin with @: '%s'"%(n)) + if n[-1]=="$": + name = n[:-1] + if hasattr(model,name): + return getattr(model.c,name).like(model_param[n]) + else: + raise UliwebError("'%s' does not have '%s'"%(model_name,name)) + elif n[-1]=="}" and n[-2]=="{": + name = n[:-2] + if hasattr(model,name): + # TODO + pass + raise UliwebError("still not support '%s'"%(name)) + elif hasattr(model,n): + return getattr(model.c,n)==model_param[n] + else: + raise UliwebError("not support item: '%s'"%(item)) + def head(self): try: for key in self.request_data: From 214bdc22d2879fdc563c7037e61ad36f9d1e7e76 Mon Sep 17 00:00:00 2001 From: Chunlin Zhang Date: Thu, 23 May 2019 14:31:57 +0800 Subject: [PATCH 4/4] add 2 example of @expr; fix escape problem in demo --- demo/apps/apijson_demo/templates/index.html | 10 +++--- demo/apps/apijson_demo/views.py | 36 ++++++++++++++++++++- 2 files changed, 40 insertions(+), 6 deletions(-) diff --git a/demo/apps/apijson_demo/templates/index.html b/demo/apps/apijson_demo/templates/index.html index ac3c029..5955000 100644 --- a/demo/apps/apijson_demo/templates/index.html +++ b/demo/apps/apijson_demo/templates/index.html @@ -101,11 +101,11 @@ el: '#app', delimiters: ['{', '}'], data: { - request_get : {{=request_get_json}}, - request_head : {{=request_head_json}}, - request_post : {{=request_post_json}}, - request_put : {{=request_put_json}}, - request_delete : {{=request_delete_json}}, + request_get : {{<