Tuesday, September 1, 2020

Fixed some issues with learning foreign keys

I worked on #3653. Summary of changes: Learning foreign keys now also work in React front end. The lino_xl.lib.cal.Guest.partner field is now a learning foreign key.

This didn’t require any change in React front end because react actually handled LFKs better than extjs. I rather adapted Lino to accept the react way as well.

I reviewed the internal API for defining learning FKs. Removed the disable_create_choice attribute of lino.core.model.Model. The lino.core.model.Model.choice_text_to_dict() method is now the only thing to implement. When it returns a dict, then the database object gets created. When it returns None, the user will see a warning. Default implementation returns None. The only implemented usage so far is in contacts.Person.

NB it is still necessary to explicitly create a chooser and a create_FOO_choice() method. But now the create_FOO_choice() method is a one-liner.

An easy place for reproducing the problem:

  • go amici1, runserver, sign in as robin, go to the detail of some appointment. Click on the “Person” field in the Presences panel to add a new person. Type “Foo Bar” (name of a person that doesn’t yet exist). Type Enter.

  • Expected behaviour: Lino should create a person (first_name=”Foo”, last_name=”Bar”).

  • Actual behaviour: the JS console says SyntaxError: JSON.parse: unexpected character at line 1 column 1 of the JSON data

It is –correctly– sending a POST to http://127.0.0.1:8000/api/cal/GuestsByEvent

And the POST request says:

an=grid_post&mk=209&mt=24&partner=foo+bar&rp=weak-key-24

That is, it doesn’t send a partnerHidden field, as it would when we select an existing person:

an=grid_post&mk=209&mt=24&partner=Odette+Adam&partnerHidden=227&rp=weak-key-24

The extjs front end worked because there the combobox puts the value “foo bar” in both fields of the AJAX request (partner and partnerHidden). Which is not very elegant. I changed Lino to support an empty partner field and to look up the partnerHidden field for comboboxes of a learning FK.

storeField.form2obj() has three steps:

  • extract_form_data()

  • parse_form_value()

  • set_value_in_object()

The code to create the partner is no longer executed in parse_form_value() but in extract_form_data(). The internal “value” returned by extract_form_data() is no longer the parsed primary key but the model instance (which potentially has been created on the fly).

In ParameterStore.parse_params we do not use the learning FK feature.

I moved the functions choices_response() and choices_for_field() from lino_react.react.views to lino.core.views and to lino.core.fields because this was duplicated code.

Another bug

I opened #3773: When I call, in react, in a slave table, the delete_selected action, react doesn’t specify the mk and mt parameters. Which sometimes works (when the master instance is not needed), but e.g. for a cal.Guest it failed. cal.GuestsByEvent.disable_delete() now raises an explicit exception in that case:

raise Exception("You must specify a master instance")

Reviewing the LETS tutorial

I realized that The LETS tutorial is actually a great example of developing your own Lino application from scratch. A moved it to the “Getting started” section of the developer guide.

I moved the code and the specs for lets1 into a new repository https://gitlab.com/lino-framework/lets