Search code examples
referenceweb2pydata-access-layer

Web2Py list:reference table, Load and set data


Maybe I'm missing something absurd, I'm not seeing, but this is my first app to study web2py. I am unable to enter the data in Table Movies, which has fields related to other tables.

The list is loaded, but it is not registered in Movies registration. Under the codes and the results.

db.py

Movie = db.define_table('movies',
 Field('title','string', label = 'Title'),
 Field('date_release','integer', label = 'Date Release'),
 Field('duraction','integer', label = 'Duraction'),
 Field('category','string','list:reference categories', label = 'Category'),
 Field('actor','list:reference actors', label = 'Actor'),
 Field('director','list:reference directors', label = 'Diretor'),
 )

Category = db.define_table('categories',
   Field('title','string', label = 'Title'),
)

validators.py

Movie.title.requires = [IS_NOT_EMPTY(), IS_NOT_IN_DB(db, 'movies.title')]
Movie.category.requires = IS_IN_DB(db, 'categories.title')
Movie.director.requires = IS_IN_DB(db, 'directors.name')
Movie.actor.requires = IS_IN_DB(db, 'actors.name')
Movie.duraction.requires = IS_INT_IN_RANGE(0, 1000)


Category.title.requires = IS_NOT_EMPTY()

movie.py

def add():
    form = SQLFORM(Movie)
    if form.process().accepted:
        response.flash = "Successful! New movie added!"
        redirect(URL('add'))
    elif form.errors:
        response.flash = 'Error'
    else:
        response.flash = 'Form, set data'
    return dict(form = form)

List Load another tables - ok:

enter image description here

The items of list not record in DB:

The items of list not record in DB


Solution

  • The widgets displayed in the form are based on the IS_IN_DB field validators you have specified, and there are three problems with the way you have coded them.

    First, list:reference fields, like standard reference type fields, store the record IDs of the records they reference -- they do not store values of other fields within the referenced records. So, the second argument to the IS_IN_DB validator should always be the ID field (e.g., categories.id).

    Second, although the field will store record IDs, you want the form widget to show some other more descriptive representation of each record, so you should specify the "label" argument of the IS_IN_DB validator (e.g., label='%(title)s').

    Third, list:reference fields allow for multiple selections, so you must set the "multiple" argument of the IS_IN_DB validator to True. This will result in a multi-select widget in the form.

    So, the resulting validator should look like this:

    Movie.category.requires = IS_IN_DB(db, 'categories.id', label='%(title)s', multiple=True)
    

    The above will allow multiple db.categories IDs to be selected, though the form widget will display category titles rather than the actual IDs.

    Now, all of the above can be made much easier if you instead define the referenced tables before the db.movies table and specify a format argument for each table:

    Category = db.define_table('categories',
       Field('title','string', label = 'Title'),
       format='%(title)s')
    
    Movie = db.define_table('movies',
       ...,
       Field('category', 'list:reference categories', label = 'Category'),
       ...)
    

    With the above code, there is no need to explicitly specify the IS_IN_DB validator at all, as the db.movies.category field will automatically get a default validator exactly like the one specified above (the format attribute of the db.categories table is used as the label argument).

    You might want to read the documentation on list:reference fields and the IS_IN_DB validator.

    As an aside, you might consider specifying your field validators within the table definitions (via the requires argument to Field()), as this is more concise, keeps all schema-related details in one place, and eliminates the need to read and execute an additional model file on every request.