Welcome | Get started | Dive | Contribute | Topics | Reference | Changes | More
Delayed values¶
This topic guide explains what a delayed value is.
Side note: Code snippets (lines starting with >>>
) in this document get
tested as part of our development workflow. The following
initialization snippet tells you which demo project is being used in
this document.
>>> 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
True
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).
>>> # 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
>>> for sf in store.all_fields:
... if sf is not None:
... print(sf.name, sf.__class__.__name__, repr(sf.field) )
...
id AutoStoreField <django.db.models.fields.BigAutoField: id>
summary StoreField <django.db.models.fields.CharField: summary>
group ForeignKeyStoreField <django.db.models.fields.related.ForeignKey: group>
user ForeignKeyStoreField <django.db.models.fields.related.ForeignKey: user>
description StoreField <lino.core.fields.RichTextField: description>
order ForeignKeyStoreField <django.db.models.fields.related.ForeignKey: order>
end_user ForeignKeyStoreField <django.db.models.fields.related.ForeignKey: end_user>
private BooleanStoreField <django.db.models.fields.BooleanField: private>
urgent BooleanStoreField <django.db.models.fields.BooleanField: urgent>
ticket_type ForeignKeyStoreField <django.db.models.fields.related.ForeignKey: ticket_type>
parent ForeignKeyStoreField <django.db.models.fields.related.ForeignKey: parent>
TicketsByParent MethodStoreField <lino.core.fields.HtmlBox: TicketsByParent>
priority IntegerStoreField <django.db.models.fields.IntegerField: priority>
regular_hours StoreField <lino.core.fields.DurationField: regular_hours>
free_hours StoreField <lino.core.fields.DurationField: free_hours>
created DateTimeStoreField <django.db.models.fields.DateTimeField: created>
modified DateTimeStoreField <django.db.models.fields.DateTimeField: modified>
ref StoreField <django.db.models.fields.CharField: ref>
upgrade_notes StoreField <lino.core.fields.RichTextField: upgrade_notes>
state ComboStoreField <lino.core.choicelists.ChoiceListField: state>
assigned_to ForeignKeyStoreField <django.db.models.fields.related.ForeignKey: assigned_to>
planned_time StoreField <lino.core.fields.DurationField: planned_time>
deadline DateStoreField <django.db.models.fields.DateField: deadline>
duplicate_of ForeignKeyStoreField <django.db.models.fields.related.ForeignKey: duplicate_of>
DuplicatesByTicket MethodStoreField <lino.core.fields.HtmlBox: DuplicatesByTicket>
disabled_fields DisabledFieldsStoreField None
disable_editing DisableEditingStoreField None
workflow_buttons VirtStoreField <lino.core.fields.DisplayField>
overview VirtStoreField <lino.core.fields.HtmlBox>
quick_assign_to VirtStoreField <lino.core.fields.DisplayField>
comments.CommentsByRFC VirtStoreField <lino.core.fields.HtmlBox: comments.CommentsByRFC>
uploads.UploadsByController VirtStoreField <lino.core.fields.HtmlBox: uploads.UploadsByController>
tickets.TicketsByParent VirtStoreField <lino.core.fields.HtmlBox: TicketsByParent>
tickets.TicketsByParent VirtStoreField <lino.core.fields.HtmlBox: TicketsByParent>
add_tag VirtStoreField <django.db.models.fields.related.ForeignKey: add_tag>
topics.TagsByOwner VirtStoreField <lino.core.fields.HtmlBox: topics.TagsByOwner>
my_nickname VirtStoreField <django.db.models.fields.CharField: my_nickname>
comments.CommentsByMentioned VirtStoreField <lino.core.fields.HtmlBox: comments.CommentsByMentioned>
working.SessionsByTicket VirtStoreField <lino.core.fields.HtmlBox: working.SessionsByTicket>
>>> d = store.row2dict(ar, t1)
>>> d['tickets.TicketsByParent']
{'delayed_value_url': 'values/tickets/AllTickets/1/tickets.TicketsByParent'}
>>> pprint(d)
...
{'DuplicatesByTicket': <Element div at ...>,
'TicketsByParent': <Element div at ...>,
'add_tag': None,
'add_tagHidden': None,
'assigned_to': None,
'assigned_toHidden': 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,
'TicketsByParent': True,
'assign_to_me': True,
'created': True,
'end_session': True,
'id': True,
'mark_refused': True,
'modified': True,
'take': True,
'wf1': True},
'duplicate_of': None,
'duplicate_ofHidden': None,
'end_user': 'Andreas Arens',
'end_userHidden': 112,
'free_hours': None,
'group': 'Developers',
'groupHidden': 1,
'id': 1,
'modified': datetime.datetime(..., tzinfo=datetime.timezone.utc),
'my_nickname': None,
'order': 'SLA 1/2014 (welket)',
'orderHidden': 1,
'overview': '<div><h2>Föö fails to bar when baz</h2></div>',
'parent': None,
'parentHidden': None,
'planned_time': None,
'priority': 30,
'private': False,
'quick_assign_to': <Element span at ...>,
'ref': None,
'regular_hours': None,
'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'},
'topics.TagsByOwner': {'delayed_value_url': 'values/tickets/AllTickets/1/topics.TagsByOwner'},
'upgrade_notes': '',
'uploads.UploadsByController': {'delayed_value_url': 'values/tickets/AllTickets/1/uploads.UploadsByController'},
'urgent': False,
'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 class="htmlText"><p>Total 0:00 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
None
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/
¶
Temporary diagnostics after https://gitlab.com/lino-framework/book/-/jobs/7885272346 regarding the level of the django.server logger:
>>> serverlogger = logging.getLogger('django.server')
>>> logging.getLevelName(serverlogger.getEffectiveLevel())
'INFO'
>>> logging.getLevelName(serverlogger.level)
'INFO'
>>> settings.DEBUG = False
>>> test_client.force_login(rt.login('robin').user)
>>> url = "/values/tickets/AllTickets/4694/comments.CommentsByRFC"
>>> res = test_client.get(url)
Bad Request: /values/tickets/AllTickets/4694/comments.CommentsByRFC
>>> res.status_code
400
>>> print(res.content.decode()[:9000])
{ "data": "No row with primary key 4694 in tickets.AllTickets" }
>>> url = "/values/tickets/AllTickets/112/comments.CommentsByRFC"
>>> res = test_client.get(url)
>>> print(res.status_code)
200
The response to this AJAX request is in JSON:
>>> res.content.decode()
'{ "data": "..." }'
We can parse it into a dict:
>>> d = json.loads(res.content.decode())
>>> print(d['data'])
<div class="htmlText">...</div>
>>> url = "/values/tickets/Tickets/112/working.SessionsByTicket"
>>> res = test_client.get(url)
>>> d = json.loads(res.content.decode())
>>> print(d['data'])
<div class="htmlText"><p>...</p></div>
When an incoming request to /values/ causes some error, this always returns a JSON error response, never an HTML error report.
>>> url = "/values/contacts/Persons/5338/contacts.RolesByPerson"
>>> ses = rt.login('robin')
>>> test_client.force_login(ses.user)
>>> settings.DEBUG = True
>>> res = test_client.get(url)
Bad Request: /values/contacts/Persons/5338/contacts.RolesByPerson
>>> print(res.status_code)
400
>>> print(res.content.decode())
{ "data": "No row with primary key 5338 in contacts.Persons" }
This is also true on a production server:
>>> settings.DEBUG = False
>>> res = test_client.get(url)
Bad Request: /values/contacts/Persons/5338/contacts.RolesByPerson
>>> print(res.status_code)
400
>>> print(res.content.decode())
{ "data": "No row with primary key 5338 in contacts.Persons" }