web2pyweb2py-modules

Web2py : SQLFORM.grid : Making 2 columns unique


Here is my database definition:

db.define_table('emsAlertTokens',

Field('emsrelease','string',default=session.releasefield,writable=False,label=T('Release')),
            Field('emsmachine','string',default=session.machinefield,writable=False,label=T('Machine')),
            Field('emsstartdate','datetime',default=request.now,label=T('Start Date')),
            Field('emsenddate','datetime',IS_NOT_EMPTY(error_message='Please specify the End Date.'), label=T('End Date')),
            Field('errorgrepfile','upload',requires=IS_NOT_EMPTY(error_message='Please upload a valid error tokens file'),custom_store=ems_file, custom_retrieve=ems_retrieve, autodelete=True, uploadfield=True, uploadfs=None, label=T('Error Tokens')),
            Field('tokenname','string',requires=IS_NOT_EMPTY('What would you call your token file?'),label=T('Token File Name')))

As you can notice the emsmachine and emsrelease fields are from a session. My requirement is to make the combination of this 2 values to be unique.

I tried with below validator but its not working.

db.emsAlertTokens.emsrelease.requires = IS_NOT_IN_DB(db(db.emsAlertTokens.emsmachine == session.machinefield), 'emsAlertTokens.emsrelease', error_message='EMS token file already exist for this release.')

Even this doesnot work:

db.emsAlertTokens.emsrelease.requires = IS_NOT_IN_DB(db(db.emsAlertTokens.emsmachine == request.vars.emsmachine), 'emsAlertTokens.emsrelease', error_message='EMS token file already exist for this release.')

Kindly help me on this.


Solution

  • Validators are for form inputs, and since these two fields are not writable (and therefore not part of the form), the validator does not get applied (default values set for non-writable fields do not get validated).

    Given that the default values for these fields are added to the session at some prior point, you should validate their joint uniqueness at that time.

    Another option is to include the fields in the form with the values pre-filled, but include the HTML "readonly" attribute to prevent the user from changing it:

    Field('emsrelease', 'string', label=T('Release'),
          widget=lambda f, v: SQLFORM.widgets.string.widget(f, session.releasefield,
                                                            _readonly=True),
          requires=IS_NOT_IN_DB(db(db.emsAlertTokens.emsmachine == session.machinefield),
                                'emsAlertTokens.emsrelease',
                                error_message='EMS token file already exist for this release.')),
    Field('emsmachine', 'string', label=T('Release'),
          widget=lambda f, v: SQLFORM.widgets.string.widget(f, session.machinefield,
                                                            _readonly=True))
    

    So, instead of making the field non-writable (and therefore excluding it from the form) and setting a default value, the above pre-fills the value in the widget and sets the widget to "readonly" (note, with this method, an adversary could manipulate the value submitted with the form, so if you are worried about that possibility, you can add an additional validator to confirm the submitted value matches the value in the session).