misc: add autocomplete support to live dynamic custom views (#48472)

This commit is contained in:
Frédéric Péters 2020-11-12 19:30:49 +01:00
parent 71a4174d63
commit 00f1b271b4
5 changed files with 85 additions and 21 deletions

View File

@ -6091,7 +6091,7 @@ def test_dynamic_item_field_from_custom_view_on_cards(pub):
formdef.store()
resp = get_app(pub).get('/test/')
assert len(resp.form['f1'].options) == 1
assert resp.form['f1'].options == [('', False, '---')]
resp.form['f0'] = 'baz'
live_resp = app.post('/test/live?modified_field_id=0', params=resp.form.submit_fields())
assert len(live_resp.json['result']['1']['items']) == 10
@ -6102,7 +6102,33 @@ def test_dynamic_item_field_from_custom_view_on_cards(pub):
# simulate javascript filling the <select>
resp.form['f1'].options.append((item['id'], False, item['text']))
resp.form['f1'] = resp.form['f1'].options[0][0]
resp.form['f1'] = resp.form['f1'].options[0][0]
resp = resp.form.submit('submit') # -> validation page
resp = resp.form.submit('submit') # -> submit
assert formdef.data_class().select()[0].data['1'] in baz_ids
assert formdef.data_class().select()[0].data['1_structured']['item'] == 'baz'
# same in autocomplete mode
formdef.fields[1].display_mode = 'autocomplete'
formdef.store()
app = get_app(pub)
resp = app.get('/test/')
# simulate select2 mode, with qommon.forms.js adding an extra hidden widget
resp.form.fields['f1_display'] = Hidden(form=resp.form, tag='input', name='f1_display', pos=10)
select2_url = resp.pyquery('select:last').attr['data-select2-url']
resp_json = app.get(select2_url + '?q=')
assert len(resp_json.json['data']) == 0
resp.form['f0'] = 'baz'
live_resp = app.post('/test/live?modified_field_id=0', params=resp.form.submit_fields())
new_select2_url = live_resp.json['result']['1']['source_url']
resp_json = app.get(new_select2_url + '?q=')
assert len(resp_json.json['data']) == 10
assert set([str(x['id']) for x in resp_json.json['data']]) == baz_ids
resp.form['f1'].force_value(str(resp_json.json['data'][0]['id']))
resp.form.fields['f1_display'].force_value(resp_json.json['data'][0]['text'])
resp = resp.form.submit('submit') # -> validation page
resp = resp.form.submit('submit') # -> submit
assert formdef.data_class().select()[0].data['1'] in baz_ids

View File

@ -877,17 +877,30 @@ class AutocompleteDirectory(Directory):
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'])
url = sign_url_auto_orig(url)
get_response().set_content_type('application/json')
return misc.urlopen(url).read()
if isinstance(info, str) and not info.startswith('carddef:'):
# legacy json source
info = {'url': info}
elif isinstance(info, str):
# legacy carddef source
info = {'carddef_ref': info}
if 'url' in info:
url = info['url']
url += urllib.quote(get_request().form['q'])
url = sign_url_auto_orig(url)
get_response().set_content_type('application/json')
return misc.urlopen(url).read()
# carddef_ref in info
carddef_ref = info['carddef_ref']
from wcs.carddef import CardDef
values = CardDef.get_data_source_items(
carddef_ref,
custom_view=info.get('dynamic_custom_view'),
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]})
class ApiDirectory(Directory):

View File

@ -193,7 +193,7 @@ class CardDef(FormDef):
@classmethod
def get_data_source_items(cls, data_source_id, query=None, limit=None,
get_by_id=None, get_by_text=None):
custom_view=None, get_by_id=None, get_by_text=None):
from wcs.workflows import WorkflowStatusItem
assert data_source_id.startswith('carddef:')
parts = data_source_id.split(':')
@ -204,9 +204,10 @@ class CardDef(FormDef):
criterias = [NotEqual('status', 'draft')]
order_by = None
if len(parts) > 2:
custom_view = cls.get_data_source_custom_view(data_source_id)
if not custom_view:
return []
if custom_view is None:
custom_view = cls.get_data_source_custom_view(data_source_id)
if not custom_view:
return []
order_by = custom_view.order_by
criterias.extend(cls.get_data_source_criterias(carddef, custom_view))
for criteria in criterias:

View File

@ -468,11 +468,35 @@ class NamedDataSource(XmlStorableObject):
if self.type == 'jsonp':
return self.data_source.get('value')
if self.type == 'json' and self.query_parameter:
json_url = self.get_json_query_url()
info = None
if json_url:
info = {'url': json_url}
return '/api/autocomplete/%s' % (
get_session().get_data_source_query_info_token(self.get_json_query_url()))
get_session().get_data_source_query_info_token(info))
if self.type and self.type.startswith('carddef:'):
parts = self.type.split(':')
if len(parts) > 2:
# custom view, check if it's dynamic
from wcs.carddef import CardDef
from wcs.workflows import WorkflowStatusItem
custom_view = CardDef.get_data_source_custom_view(self.type)
had_template = False
for filter_key, filter_value in custom_view.filters.items():
if not Template.is_template_string(filter_value):
continue
custom_view.filters[filter_key] = WorkflowStatusItem.compute(filter_value)
had_template = True
if had_template:
# keep altered custom view in session
return '/api/autocomplete/%s' % (
get_session().get_data_source_query_info_token({
'carddef_ref': self.type,
'dynamic_custom_view': custom_view}))
return '/api/autocomplete/%s' % (
get_session().get_data_source_query_info_token(self.type))
get_session().get_data_source_query_info_token({
'carddef_ref': self.type,
}))
return None
def get_value_by_id(self, param_name, param_value):

View File

@ -701,11 +701,11 @@ class FormStatusPage(Directory, FormTemplateMixin):
continue
varnames = data_source.get_referenced_varnames(field.formdef)
if (not modified_field_varnames or modified_field_varnames.intersection(varnames)) and (
field.display_mode == 'autocomplete' and data_source.query_parameter):
field.display_mode == 'autocomplete' and data_source.can_jsonp()):
# computed earlier, in perform_more_widget_changes, when the field
# was added to the form
result[field.id]['source_url'] = field.url
if modified_field_varnames.intersection(varnames):
elif modified_field_varnames.intersection(varnames):
if 'template-' in (field.extra_css_class or ''):
# custom template, it may need all option attributes
result[field.id]['items'] = field.get_extended_options()