misc: add dynamic autocomplete for <select> of cards (#47152)
This commit is contained in:
parent
342ab5251d
commit
73b138dc64
|
@ -6091,6 +6091,49 @@ def test_item_field_autocomplete_jsonp_source(http_requests, pub):
|
|||
assert '0_structured' not in formdef.data_class().select()[0].data
|
||||
|
||||
|
||||
def test_item_field_autocomplete_cards_source(pub):
|
||||
user = create_user(pub)
|
||||
formdef = create_formdef()
|
||||
formdef.data_class().wipe()
|
||||
|
||||
carddef = CardDef()
|
||||
carddef.name = 'items'
|
||||
carddef.digest_template = '{{form_var_name}}'
|
||||
carddef.fields = [
|
||||
fields.StringField(id='0', label='string', varname='name'),
|
||||
fields.StringField(id='1', label='string', varname='attr'),
|
||||
]
|
||||
carddef.store()
|
||||
for i, value in enumerate(['foo', 'bar', 'baz']):
|
||||
carddata = carddef.data_class()()
|
||||
carddata.data = {
|
||||
'0': value,
|
||||
'1': 'attr%s' % i,
|
||||
}
|
||||
carddata.just_created()
|
||||
carddata.store()
|
||||
|
||||
ds = {'type': 'carddef:%s' % carddef.url_name}
|
||||
formdef.fields = [
|
||||
fields.ItemField(id='0', label='string', type='item',
|
||||
data_source=ds,
|
||||
display_mode='autocomplete',
|
||||
),
|
||||
]
|
||||
formdef.store()
|
||||
|
||||
app = get_app(pub)
|
||||
resp = app.get('/test/')
|
||||
select2_url = resp.pyquery('select').attr['data-select2-url']
|
||||
resp2 = app.get(select2_url + '?q=ba')
|
||||
assert [x['text'] for x in resp2.json['data']] == ['bar', 'baz']
|
||||
resp.form['f0'].force_value(str(resp2.json['data'][0]['id']))
|
||||
resp = resp.form.submit('submit') # -> validation page
|
||||
resp = resp.form.submit('submit') # -> submit
|
||||
assert formdef.data_class().select()[0].data['0'] == '2'
|
||||
assert formdef.data_class().select()[0].data['0_display'] == 'bar'
|
||||
|
||||
|
||||
def test_form_data_keywords(pub):
|
||||
formdef = create_formdef()
|
||||
formdef.keywords = 'hello,world'
|
||||
|
|
13
wcs/api.py
13
wcs/api.py
|
@ -829,11 +829,18 @@ class ApiTrackingCodeDirectory(Directory):
|
|||
|
||||
class AutocompleteDirectory(Directory):
|
||||
def _q_lookup(self, component):
|
||||
url = get_session().get_data_source_query_url_from_token(component)
|
||||
if not url:
|
||||
info = get_session().get_data_source_query_info_from_token(component)
|
||||
if not info:
|
||||
raise AccessForbiddenError()
|
||||
get_response().set_content_type('application/json')
|
||||
if info.startswith('carddef:'):
|
||||
from wcs.carddef import CardDef
|
||||
values = CardDef.get_data_source_items(info,
|
||||
query=get_request().form['q'],
|
||||
limit=get_request().form.get('page_limit'))
|
||||
return json.dumps({'data': [{'id': x['id'], 'text': x['text']} for x in values]})
|
||||
url = info
|
||||
url += urllib.quote(get_request().form['q'])
|
||||
unsigned_url = url
|
||||
url = sign_url_auto_orig(url)
|
||||
get_response().set_content_type('application/json')
|
||||
return misc.urlopen(url).read()
|
||||
|
|
|
@ -19,7 +19,7 @@ import types
|
|||
|
||||
from quixote import get_publisher
|
||||
from .qommon import _, N_, misc
|
||||
from .qommon.storage import Equal, NotEqual
|
||||
from .qommon.storage import Equal, NotEqual, ILike
|
||||
|
||||
from wcs.carddata import CardData
|
||||
from wcs.formdef import FormDef
|
||||
|
@ -154,7 +154,7 @@ class CardDef(FormDef):
|
|||
yield (data_source_id, '%s - %s' % (carddef['name'], custom_view.title), data_source_id)
|
||||
|
||||
@classmethod
|
||||
def get_data_source_items(cls, data_source_id):
|
||||
def get_data_source_items(cls, data_source_id, query=None, limit=None):
|
||||
assert data_source_id.startswith('carddef:')
|
||||
parts = data_source_id.split(':')
|
||||
try:
|
||||
|
@ -178,10 +178,13 @@ class CardDef(FormDef):
|
|||
criterias.extend(form_page.get_view_criterias())
|
||||
order_by = custom_view.order_by
|
||||
|
||||
if query:
|
||||
criterias.append(ILike('digest', query))
|
||||
items = [x.get_data_source_structured_item()
|
||||
for x in carddef.data_class().select(
|
||||
clause=criterias,
|
||||
order_by=order_by)]
|
||||
order_by=order_by,
|
||||
limit=limit)]
|
||||
if order_by is None:
|
||||
items.sort(key=lambda x: misc.simplify(x['text']))
|
||||
return items
|
||||
|
|
|
@ -335,6 +335,8 @@ class NamedDataSource(XmlStorableObject):
|
|||
return True
|
||||
if self.type == 'json' and self.query_parameter:
|
||||
return True
|
||||
if self.type and self.type.startswith('carddef:'):
|
||||
return True
|
||||
return False
|
||||
|
||||
def migrate(self):
|
||||
|
@ -416,7 +418,10 @@ class NamedDataSource(XmlStorableObject):
|
|||
return self.data_source.get('value')
|
||||
if self.type == 'json' and self.query_parameter:
|
||||
return '/api/autocomplete/%s' % (
|
||||
get_session().get_data_source_query_url_token(self.get_json_query_url()))
|
||||
get_session().get_data_source_query_info_token(self.get_json_query_url()))
|
||||
if self.type and self.type.startswith('carddef:'):
|
||||
return '/api/autocomplete/%s' % (
|
||||
get_session().get_data_source_query_info_token(self.type))
|
||||
return None
|
||||
|
||||
def get_value_by_id(self, param_name, param_value):
|
||||
|
|
|
@ -142,18 +142,21 @@ class BasicSession(Session):
|
|||
del session.visiting_objects[object_key]
|
||||
session.store()
|
||||
|
||||
def get_data_source_query_url_token(self, url):
|
||||
def get_data_source_query_info_token(self, info):
|
||||
# info can be an URL in case of remote JSON source or a cards
|
||||
# reference, like carddef:foobar. It is stored in a dictionary
|
||||
# with _url_ in its name for historical reasons.
|
||||
if not self.data_source_query_url_tokens:
|
||||
self.data_source_query_url_tokens = {}
|
||||
for key, value in self.data_source_query_url_tokens.items():
|
||||
if value == url:
|
||||
if value == info:
|
||||
return key
|
||||
key = str(uuid.uuid4())
|
||||
self.data_source_query_url_tokens[key] = url
|
||||
self.data_source_query_url_tokens[key] = info
|
||||
self.store()
|
||||
return key
|
||||
|
||||
def get_data_source_query_url_from_token(self, token):
|
||||
def get_data_source_query_info_from_token(self, token):
|
||||
if not self.data_source_query_url_tokens:
|
||||
return None
|
||||
return self.data_source_query_url_tokens.get(token)
|
||||
|
|
Loading…
Reference in New Issue