Welcome | Get started | Dive into Lino | Contribute | Topics | Reference | More

Delayed values

This topic guide explains what a delayed value is.

This page is a tested document and the following instructions are used for initialization:

>>> from lino import startup
>>> startup('lino_book.projects.noi1r.settings')
>>> from lino.api.doctest import *
delayed value

A value of a store field that is not computed immediately and for which Lino returns a dict object that basically just tells the client to emit another AJAX request if it wants to know that value.

Lino currently uses a delayed value when rendering the summary of a slave table of a detail page.

Delayed values are used only when the front end supports them, which it says by setting support_async to True. Only react has this feature right now.

>>> settings.SITE.kernel.web_front_ends[0].support_async

About the name

A slave table can be a valid data element of a detail layout, and it is named foos.Foos (i.e. with a dot). But the corresponding store field will be named foos_Foos because we want the names of store fields to be valid Python attribute names.

>>> # ses = rt.login("robin", renderer=settings.SITE.kernel.default_renderer)
>>> t1 = tickets.Ticket.objects.get(pk=1)
>>> print(t1.order)
SLA 1/2014 (welket)
>>> dv = tickets.AllTickets
>>> ar = dv.request(user=rt.login("robin").get_user())
>>> store = dv.get_handle().store
>>> pprint(store.row2dict(ar, t1))
{'DuplicatesByTicket': <Element div at ...>,
 'assigned_to': None,
 'assigned_toHidden': None,
 'comment': None,
 'comments.CommentsByMentioned': {'delayed_value_url': 'values/tickets/AllTickets/1/comments.CommentsByMentioned'},
 'comments.CommentsByRFC': {'delayed_value_url': 'values/tickets/AllTickets/1/comments.CommentsByRFC'},
 'created': datetime.datetime(..., tzinfo=datetime.timezone.utc),
 'deadline': None,
 'description': '',
 'disable_editing': False,
 'disabled_fields': {'DuplicatesByTicket': True,
                     'created': True,
                     'end_session': True,
                     'fixed_since': True,
                     'id': True,
                     'mark_refused': True,
                     'modified': True,
                     'wf1': True},
 'duplicate_of': None,
 'duplicate_ofHidden': None,
 'end_user': 'Andreas Arens',
 'end_userHidden': 112,
 'extra_hours': None,
 'fixed_since': None,
 'free_hours': Duration('168:17'),
 'group': 'Developers',
 'groupHidden': 1,
 'id': 1,
 'modified': datetime.datetime(..., tzinfo=datetime.timezone.utc),
 'my_nickname': None,
 'order': 'SLA 1/2014 (welket)',
 'orderHidden': 1,
 'overview': <Element div at ...>,
 'parent': None,
 'parentHidden': None,
 'planned_time': None,
 'priority': 30,
 'private': False,
 'quick_assign_to': <Element p at ...>,
 'ref': None,
 'regular_hours': Duration('125:31'),
 'site': 'pypi',
 'siteHidden': 1,
 'state': 'New',
 'stateHidden': '10',
 'summary': 'Föö fails to bar when baz',
 'ticket_type': 'Bugfix',
 'ticket_typeHidden': 1,
 'tickets.TicketsByParent': {'delayed_value_url': 'values/tickets/AllTickets/1/tickets.TicketsByParent'},
 'upgrade_notes': '',
 'uploads.UploadsByController': {'delayed_value_url': 'values/tickets/AllTickets/1/uploads.UploadsByController'},
 'user': 'Jean',
 'userHidden': 7,
 'workflow_buttons': <Element span at ...>,
 'working.SessionsByTicket': {'delayed_value_url': 'values/tickets/AllTickets/1/working.SessionsByTicket'}}

When the client receives data values of type {'delayed_value_url': ... }, it will render the form with those fields empty, emit for each of them an AJAX request to the specified delayed_value_url and fill in the value as soon as it receives an answer. Here is how such an AJAX request looks like:

>>> url  = "values/tickets/AllTickets/1/working.SessionsByTicket"
>>> demo_get('robin', url, None, -1)
GET /values/tickets/AllTickets/1/working.SessionsByTicket for user Robin Rood got
{'data': '<div><p>Total 293:48 hours.</p></div>'}

Don't read on

>>> ses = rt.login("robin")
>>> ses.show('tickets.AllTickets.detail', selected_pks=[1])
GeneralMoreLinksFöö fails to bar when bazSubscriptionEnd userTicket typeProjectmarc, rolfPrivatePriorityPlanned timeDeadlineRegularExtraFreeTotal 0:00 hours.[✋] [▶] ⚹ New → [☾] [☎] [☉] [⚒] [☐] [☑]My commentNoneComments of #1 (⚹ Föö fails to bar when baz)BodyCreatedAuthor
WhoWhatDone?HimBar HerFoo the BarxThemFloop the pigx...Rolf Rompenbreaking

De : lino@foo.net [mailto:foo@bar.com]  Envoyé : mardi 18 octobre 2016 08:52 À : eexample@foo.com Objet : [welcht] YOU modified FOO BAR
 Dear Aurélie ,
this is to notify  / BAR
BAR modified
TODO: include a summary of the modifications.
Any subsequent notifications about foo/  until you view this notification in the Lino web interface. Please visit
and follow your welcome messages...JeanIDReferenceSummaryDescription Resolution Source document: NewStateAssigned toAuthorCreatedModifiedFixed sinceDuplicate ofParentChildren of #1 (⚹ Föö fails to bar when baz)PriorityIDSummaryAssign to302Bar is not always baz

More requests to /values/

>>> test_client.force_login(rt.login('robin').user)
>>> url = "/values/tickets/AllTickets/4694/comments.CommentsByRFC"
>>> res = test_client.get(url, REMOTE_USER='robin')
Traceback (most recent call last):
django.core.exceptions.ObjectDoesNotExist: Invalid primary key 4694 for tickets.AllTickets
>>> url = "/values/tickets/AllTickets/112/comments.CommentsByRFC"
>>> res = test_client.get(url, REMOTE_USER='robin')
>>> print(res.status_code)

The response to this AJAX request is in JSON:

>>> d = json.loads(res.content.decode())
>>> print(d['data'])  
>>> url = "/values/tickets/Tickets/112/working.SessionsByTicket"
>>> res = test_client.get(url, REMOTE_USER='robin')
>>> d = json.loads(res.content.decode())
>>> print(d['data'])