Welcome | Get started | Dive | Contribute | Topics | Reference | Changes | More

More about slave tables

We have been introduced to slave tables, but there is more to say about them.

Slave tables with special masters

The master of a slave table can be something else than a database row. For example

If the master instance is not a subclass of lino.core.fields.TableRow, it must be JSON serializable.

Slave tables with remote master

The master_key of a slave table can be a remote field.

digraph foo  {
     A -> B
     B -> C
}

When you have three models A, B and C with A.b being a pointer to B and B.c being a pointer to C, then you can design a table CsByA which shows the C instances of a given A instance by saying:

class CsByA(Cs):
    master_key = "c__b"

For example lino_xl.lib.courses.ActivitiesByTopic shows all activities about a given topic. But an activity has no foreign key topic, so you cannot say master_key = 'topic'. But a course does know its topic indirectly because it knows its activity line, and the activity line knows its topic. So you can specify a remote field:

class ActivitiesByTopic(Courses):
    master_key = 'line__topic'

    allow_create = False

A slave table with a remote master should have allow_create set to False because we cannot set a line for a new course.

Other examples

Multiple master fields

In Lino Prima, a cast is when a given teacher gives a given subject to a given group of pupils. And similarly, an exam is usually (but not always) done by a given teacher about a given subject in a given group.

digraph foo  {
     Cast -> Subject
     Cast -> Teacher
     Cast -> Group
     Exam -> Subject
     Exam -> Teacher
     Exam -> Group
}

But there is no direct pointer from exam to cast because not every exam requires a cast. When we open the detail view of a cast, we want to see a list of all exams for this cast. Here is how to declare the lino_prima.lib.ratings.ExamsByCast table:

class ExamsByCast(Exams):
    required_roles = dd.login_required(PrimaTeacher)
    master = "prima.Cast"
    column_names = "seqno designation period *"

    @classmethod
    def get_request_queryset(self, ar, **filter):
        mi = ar.master_instance
        assert isinstance(mi, rt.models.prima.Cast)
        qs = super().get_request_queryset(ar, **filter)
        # When there are two teachers for a same subject, they can optionally
        # manage exams together.
        if manage_exams_separately:
            qs = qs.filter(subject=mi.subject, group=mi.group, user=mi.user)
        else:
            qs = qs.filter(subject=mi.subject, group=mi.group)
        return qs