Skip to content

Commit 872eeeb

Browse files
committed
add associated query feature with multi-table in a array
1 parent 9b5d0e1 commit 872eeeb

File tree

3 files changed

+272
-170
lines changed

3 files changed

+272
-170
lines changed

demo/apps/apijson_demo/views.py

Lines changed: 28 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -11,41 +11,34 @@ def index():
1111

1212
request_get = [
1313
{
14-
"label":"Single record query: self user",
14+
"label":"Single query: self user",
1515
"value":'''{
1616
"user":{
1717
"@role":"OWNER"
1818
}
1919
}''',
2020
},
21+
2122
{
22-
"label":"Single record query: with id as parameter",
23+
"label":"Single query: with id as parameter",
2324
"value":'''{
2425
"user":{
2526
"id":2,
2627
"@role":"ADMIN"
2728
}
2829
}''',
2930
},
31+
3032
{
31-
"label":"Single record query: @column",
33+
"label":"Single query: @column",
3234
"value":'''{
3335
"user":{
3436
"@column": "id,username,email",
3537
"@role":"OWNER"
3638
}
3739
}''',
3840
},
39-
{
40-
"label":"Single record query: association query",
41-
"value":'''{
42-
"moment":{},
43-
"user":{
44-
"@column": "id,username,email",
45-
"id@": "moment/user_id"
46-
}
47-
}''',
48-
},
41+
4942
{
5043
"label":"Array query: user",
5144
"value":'''{
@@ -60,6 +53,7 @@ def index():
6053
}
6154
}''',
6255
},
56+
6357
{
6458
"label":"Array query: moment",
6559
"value":'''{
@@ -74,6 +68,7 @@ def index():
7468
"total@":"/moment[]/total"
7569
}''',
7670
},
71+
7772
{
7873
"label":"Array query: like",
7974
"value":'''{
@@ -89,6 +84,7 @@ def index():
8984
}
9085
}''',
9186
},
87+
9288
{
9389
"label":"Array query: simple @expr",
9490
"value":'''{
@@ -106,6 +102,7 @@ def index():
106102
}
107103
}''',
108104
},
105+
109106
{
110107
"label":"Array query: complex @expr",
111108
"value":'''{
@@ -124,24 +121,37 @@ def index():
124121
}
125122
}''',
126123
},
124+
125+
{
126+
"label":"Association query: Two tables, one to one",
127+
"value":'''{
128+
"moment":{},
129+
"user":{
130+
"@column": "id,username,email",
131+
"id@": "moment/user_id"
132+
}
133+
}''',
134+
},
135+
127136
{
128-
"label":"Array query: association query one to many",
137+
"label":"Association query: Two tables, one to many",
129138
"value":'''{
130139
"moment": {},
131140
"[]": {
132141
"comment": {
133-
"moment_id@": "moment/id"
142+
"moment_id@": "moment/id",
143+
"@order":"date-"
134144
}
135145
}
136146
}''',
137147
},
148+
138149
{
139-
"label":"Array query: association query",
150+
"label":"Association query: Two tables in array",
140151
"value":'''{
141152
"[]": {
142153
"moment": {
143-
"@column": "id,date,user_id",
144-
"id": 3
154+
"@column": "id,date,user_id"
145155
},
146156
"user": {
147157
"id@": "/moment/user_id",

uliweb_apijson/apijson/__init__.py

Lines changed: 205 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
#coding=utf-8
22

3+
from uliweb import settings, models, request, functions
4+
from uliweb.orm import ModelNotFound
5+
6+
37
def get_apijson_tables(role="UNKNOWN"):
48
from uliweb import settings
59

@@ -47,3 +51,204 @@ def get_apijson_table(role="UNKNOWN",name=None):
4751
editable = role in roles
4852
c["editable"] = editable
4953
return c
54+
55+
class ApiJsonModelQuery(object):
56+
def __init__(self,name,params,parent,key):
57+
self.name = name
58+
self.params = params
59+
self.parent = parent
60+
self.key = key
61+
self.query_params = self.parent.request_data[key]
62+
63+
try:
64+
self.model = getattr(models,name)
65+
except ModelNotFound as e:
66+
log.error("try to find model '%s' but not found: '%s'"%(name,e))
67+
raise UliwebError("model '%s' not found"%(name))
68+
69+
self.setting = settings.APIJSON_MODELS.get(name,{})
70+
self.secret_fields = self.setting.get("secret_fields")
71+
self.column = params.get("@column")
72+
if self.column:
73+
self.column_set = set(self.column.split(","))
74+
if self.secret_fields:
75+
self.column_set -= set(self.secret_fields)
76+
self.column_set &= set(self.model.columns.keys())
77+
else:
78+
self.column_set = None
79+
80+
self.permission_check_ok = False
81+
82+
def _check_GET_permission(self):
83+
GET = self.setting.get("GET")
84+
if not GET:
85+
raise UliwebError("'%s' not accessible by apijson"%(name))
86+
87+
roles = GET.get("roles")
88+
params_role = self.params.get("@role")
89+
90+
if not params_role:
91+
if request.user:
92+
params_role = "LOGIN"
93+
else:
94+
params_role = "UNKNOWN"
95+
if params_role not in roles:
96+
raise UliwebError("'%s' not accessible by role '%s'"%(model_name,params_role))
97+
if params_role == "UNKNOWN":
98+
self.permission_check_ok = True
99+
elif functions.has_role(request.user,params_role):
100+
self.permission_check_ok = True
101+
else:
102+
raise UliwebError("user doesn't have role '%s'"%(params_role))
103+
104+
if not self.permission_check_ok:
105+
raise UliwebError("no permission")
106+
107+
self.params_role = params_role
108+
109+
def _get_array_params(self):
110+
query_count = self.query_params.get("@count")
111+
if query_count:
112+
try:
113+
query_count = int(query_count)
114+
except ValueError as e:
115+
log.error("bad param in '%s': '%s'"%(n,self.query_params))
116+
raise UliwebError("@count should be an int, now '%s'"%(query_count))
117+
self.query_count = query_count
118+
119+
query_page = self.query_params.get("@page")
120+
if query_page:
121+
#@page begin from 0
122+
try:
123+
query_page = int(query_page)
124+
except ValueError as e:
125+
log.error("bad param in '%s': '%s'"%(n,self.query_params))
126+
raise UliwebError("@page should be an int, now '%s'"%(query_page))
127+
if query_page<0:
128+
raise UliwebError("page should >0, now is '%s'"%(query_page))
129+
self.query_page = query_page
130+
131+
#https://github.com/TommyLemon/APIJSON/blob/master/Document.md#32-%E5%8A%9F%E8%83%BD%E7%AC%A6
132+
query_type = self.query_params.get("@query",0)
133+
if query_type not in [0,1,2]:
134+
raise UliwebError("bad param 'query': %s"%(query_type))
135+
self.query_type = query_type
136+
137+
#order not in query params but in model params
138+
self.order = self.params.get("@order")
139+
140+
def _filter_owner(self,q):
141+
owner_filtered = False
142+
if hasattr(self.model,"owner_condition"):
143+
q = q.filter(model.owner_condition())
144+
owner_filtered = True
145+
if not owner_filtered:
146+
user_id_field = self.setting.get("user_id_field")
147+
if user_id_field:
148+
q = q.filter(getattr(model.c,user_id_field)==request.user.id)
149+
owner_filtered = True
150+
if not owner_filtered:
151+
raise UliwebError("'%s' cannot filter with owner"%(model_name))
152+
return q
153+
154+
def _get_array_q(self,params):
155+
q = self.model.all()
156+
if self.params_role == "OWNER":
157+
q = self._filter_owner(q)
158+
159+
#@expr
160+
model_expr = params.get("@expr")
161+
if model_expr:
162+
c = self.parent._expr(self.model,params,model_expr)
163+
q = q.filter(c)
164+
else:
165+
for n in params:
166+
if n[0]!="@":
167+
c = self.parent._get_filter_condition(self.model,params,n)
168+
q = q.filter(c)
169+
return q
170+
171+
def _get_info(self,i,as_dict_child=False):
172+
173+
d = i.to_dict()
174+
if self.secret_fields:
175+
for k in self.secret_fields:
176+
del d[k]
177+
if self.column_set:
178+
keys = list(d.keys())
179+
for k in keys:
180+
if k not in self.column_set:
181+
del d[k]
182+
if as_dict_child:
183+
resultd = {}
184+
resultd[self.name] = d
185+
return resultd
186+
else:
187+
return d
188+
189+
def query_array(self):
190+
self._check_GET_permission()
191+
self._get_array_params()
192+
params = self.params.copy()
193+
194+
#update reference
195+
ref_fields = []
196+
refs = {}
197+
for n in params:
198+
if n[-1]=="@":
199+
ref_fields.append(n)
200+
col_name = n[:-1]
201+
path = params[n]
202+
refs[col_name] = self.parent._ref_get(path)
203+
if ref_fields:
204+
for i in ref_fields:
205+
del params[i]
206+
params.update(refs)
207+
208+
q = self._get_array_q(params)
209+
210+
if self.query_type in [1,2]:
211+
self.parent.vars["/%s/total"%(self.key)] = q.count()
212+
213+
if self.query_type in [0,2]:
214+
if self.query_count:
215+
if self.query_page:
216+
q = q.offset(self.query_page*self.query_count)
217+
q = q.limit(self.query_count)
218+
if self.order:
219+
for k in self.order.split(","):
220+
if k[-1] == "+":
221+
sort_key = k[:-1]
222+
sort_order = "asc"
223+
elif k[-1] == "-":
224+
sort_key = k[:-1]
225+
sort_order = "desc"
226+
else:
227+
sort_key = k
228+
sort_order = "asc"
229+
column = getattr(self.model.c,sort_key)
230+
q = q.order_by(getattr(column,sort_order)())
231+
l = [self._get_info(i,True) for i in q]
232+
self.parent.rdict[self.key] = l
233+
234+
def associated_query_array(self):
235+
self._check_GET_permission()
236+
self._get_array_params()
237+
for item in self.parent.rdict[self.key]:
238+
params = self.params.copy()
239+
#update reference
240+
ref_fields = []
241+
refs = {}
242+
for n in params:
243+
if n[-1]=="@":
244+
ref_fields.append(n)
245+
col_name = n[:-1]
246+
path = params[n]
247+
refs[col_name] = self.parent._ref_get(path,context=item)
248+
if ref_fields:
249+
for i in ref_fields:
250+
del params[i]
251+
params.update(refs)
252+
q = self._get_array_q(params)
253+
q = q.limit(1)
254+
item[self.name] = self._get_info(q.one())

0 commit comments

Comments
 (0)