The following information is from page 270 of the web2py book:
person = db.person(id)
for dog in person.dog.select(orderby=db.dog.name):
print person.name, 'owns', dog.name
In this last expressions person.dog
is a shortcut for
db(db.dog.owner==person.id)
Now:
The book does not reference a section where the person
table is defined and I dont believe any prior definition of that table actually had a dog
column. And it wouldnt make sense if the person table did have a dog
column, because presumably a person can have many dogs.
So are they saying that somehow the reference from the dog
table to person automatically created a forward reference from the person to the dog
, without any explicit column in person
?
Nothing was automatically created in the "person" database table itself. Rather, the DAL Row object representing the record of a given person includes a DAL Set object that represents the set of records in the "dog" table that reference the person. In the code, person
is a Row object (i.e., db.person(id)
) -- it has a "dog" attribute, which is just the following DAL Set object:
db(db.dog.owner == person.id)
which is the set of dogs that reference this person.id. If you do:
>>> print db.person(1)
you'll see something like:
<Row {'name': 'John', 'update_record': <function <lambda> at 0x2b08758>,
'dog': <gluon.dal.Set object at 0x2b00d10>, 'id': 1,
'delete_record': <function <lambda> at 0x2b08b90>}>
So, in addition to containing the "name" and "id" values for the record, the Row object includes update_record()
and delete_record()
functions as well as a Set object representing the "dog" table records that reference this person. If you print the query associated with that Set object, you get the following:
>>> print db.person(1).dog.query
(dog.owner = 1)
So, the forward reference is created in the Row object itself. Note, the db.person(1).dog
attribute is only a Set object defining a query -- it does not contain the actual "dog" table records or even references to their id's. No query is done against the database until you call the select()
method via db.person(1).dog.select()
, which will return the actual records from the "dogs" table.