I have this form that gets built from the SQLFORM method,
flow_step_forms[step.name] = SQLFORM(db.approval_flows_progress_steps,
record=thisstep.id if thisstep is not None else None,
readonly=not __userCanPerformStep(step) or flow_completed)
the db for approval_flows_progress_steps is as follows
db.define_table('approval_flows_progress_steps',
Field('flow_progress', 'reference approval_flows_progress'),
Field('flow_step', 'reference approval_flows_steps'),
Field('completed', 'boolean'),
Field('completed_on', 'datetime'),
Field('completed_by', 'reference auth_user'),
Field('notes', 'text', writable = True),
auth.signature
)
the field notes gets rendered as a textbox.
After I assign the SQLFORM there is this code:
if __userCanPerformStep(step) and not flow_completed:
flow_step_forms[step.name].element('textarea')['_rows'] = 5
I figured I can reference properties of the db like they do here but I get the following error:
'NoneType' object has no attribute 'writable'
Q: How can I make the text box conditionally enabled for certain users??
I am not sure about why you are getting a NoneType. But to make the textarea readonly you can do something like this:
if __userCanPerformStep(step) and not flow_completed:
flow_step_forms[step.name].element('textarea')['_readonly'] = True
else:
flow_step_forms[step.name].element('textarea')['_readonly'] = False
If your NoneType problem is only occurring when the user is not authorized. You could catch it with a Try/Except block. Saying except NoneType: _readonly = True.
Related
Im writing a google forms submitter and I'm having problems with the textfield-type questions.
Basically I am using:
textfield = question.find_element_by_class_name("quantumWizTextinputPaperinputInput")
to find the textfield and then the problems start coming in. The type of "textfield" is:<selenium.webdriver.firefox.webelement.FirefoxWebElement (session="1b49148e-2a24-4efb-b3a5-e84be92223ae", element="3b437c8b-8d05-4410-8047-bcac9ea81f0f")>
and when I want to call .send_keys(string) on it it says that Exception has occurred: AttributeError 'list' object has no attribute 'send_keys'
So basically it says that the element returned is a list (noenetheless that type() returns a firefoxwebdriver element type).
So if I try to go with textfield[0] or textfield[1] etc... it of course throws an error that a FirefoxWebDriver is not subscribable.
What the frick?
Here's the block of code:
buttons = question.find_elements_by_class_name("appsMaterialWizToggleRadiogroupRadioButtonContainer")
buttons2 = question.find_elements_by_class_name("quantumWizTogglePapercheckboxInnerBox")
try:
textfield = question.find_element_by_class_name("quantumWizTextinputPaperinputInput")
except:
print("not found")
textfield = []
pass
And then below to send keys into it:
if len(buttons) == 0 and len(buttons2) == 0:
print(textfield)
textfield.send_keys("lol spam")
try:
textfield = question.find_element_by_class_name("quantumWizTextinputPaperinputInput")
except:
print("not found")
textfield = []
pass
The problem lies within this snippet. If textfield, or to be more specific the class_name quantumWizTextinputPaperinputInput can't be found, Python continues to evaluate the except block. Within there you stated textfield = [] - that's the reason for your problems:
Exception has occurred: AttributeError 'list' object has no attribute
'send_keys' So basically it says that the element returned is a list
(noenetheless that type() returns a firefoxwebdriver element type). So
if I try to go with textfield[0] or textfield[1] etc... it of course
throws an error that a FirefoxWebDriver is not subscribable.
You can't send send_keys to a list.
List is empty, hence a textfield[0] should throw IndexError.
A solution to this problem is to find the proper class_name. Without a HTML code we can't help you to do that.
I use SQLFORM.smartgrid to show a list of records from a table (service_types). In each row of the smartgrid there is a delete link/button to delete the record. I want to executive some code before smartgrid/web2py actually deletes the record, for example I want to know if there are child records (services table) referencing this record, and if any, flash a message telling user that record cannot be deleted. How is this done?
db.py
db.define_table('service_types',
Field('type_name', requires=[IS_NOT_EMPTY(), IS_ALPHANUMERIC()]),
format='%(type_name)s',
)
db.define_table('services',
Field('service_name',requires=[IS_NOT_EMPTY(),IS_NOT_IN_DB(db,'services.service_name')]),
Field('service_type','reference service_types',requires=IS_IN_DB(db,db.service_types.id,
'%(type_name)s',
error_message='not in table',
zero=None),
ondelete='RESTRICT',
),
Field('interest_rate','decimal(15,2)',requires=IS_DECIMAL_IN_RANGE(0,100)),
Field('max_term','integer'),
auth.signature,
format='%(service_name)s',
)
db.services._plural='Services'
db.services._singular='Service'
if db(db.service_types).count() < 1:
db.service_types.insert(type_name='Loan')
db.service_types.insert(type_name='Contribution')
db.service_types.insert(type_name='Other')
controller
def list_services():
grid = SQLFORM.smartgrid(db.services
, fields = [db.services.service_name,db.services.service_type]
)
return locals()
view
{{extend 'layout.html'}}
{{=grid}}
There are two options. First, the deletable argument can be a function that takes the Row object of a given record and returns True or False to indicate whether the record is deletable. If it returns False, the "Delete" button will not be shown for that record, nor the delete operation be allowed on the server.
def can_delete(row):
return True if [some condition involving row] else False
grid = SQLFORM.smartgrid(..., deletable=can_delete)
Second, there is an ondelete argument that takes the db Table object and the record ID. It is called right before the delete operation, so to prevent the delete, you can do a redirect within that function:
def ondelete(table, record_id):
record = table(record_id)
if [some condition]:
session.flash = 'Cannot delete this record'
redirect(URL())
grid = SQLFORM.smartgrid(..., ondelete=ondelete)
Note, if the grid is loaded via an Ajax component and its actions are therefore performed via Ajax, using redirect within the ondelete method as shown above will not work well, as the redirect will have no effect and the table row will still be deleted from the grid in the browser (even though the database record was not deleted). In that case, an alternative approach is to return a non-200 HTTP response to the browser, which will prevent the client-side Javascript from deleting the row from the table (the delete happens only on success of the Ajax request). We should also set response.flash instead of session.flash (because we are not redirecting/reloading the whole page):
def ondelete(table, record_id):
record = table(record_id)
if [some condition]:
response.flash = 'Cannot delete this record'
raise HTTP(403)
Note, both the deletable and ondelete arguments can be dictionaries with table names as keys, so you can specify different values for different tables that might be linked from the smartgrid.
Finally, notice the delete URLs look like /appname/list_services/services/delete/services/[record ID]. So, in the controller, you can determine if a delete is being requested by checking if 'delete' in request.args. In that case, request.args[-2:] represents the table name and record ID, which you can use to do any checks.
From Anthony's answer I chose the second option and came up with the following:
def ondelete_service_type(service_type_table, service_type_id):
count = db(db.services.service_type == service_type_id).count()
if count > 0:
session.flash = T("Cant delete")
#redirect(URL('default','list_service_types#'))
else:
pass
return locals()
def list_service_types():
grid = SQLFORM.smartgrid(db.service_types
, fields = [db.service_types.type_name, db.services.service_name]
, ondelete = ondelete_service_type
)
return locals()
But, if I do this...
if count > 0:
session.flash = T("Cant delete")
else:
pass
return locals()
I get this error:
And if I do this:
if count > 0:
session.flash = T("Cant delete")
redirect(URL('default','list_service_types#')) <== please take note
else:
pass
return locals()
I get the flash error message Cant delete but the record appears deleted from the list, and reappears after a page refresh with F5 (apparently because the delete was not allowed in the database, which is intended).
Which one should I fix and how?
Note
If any of these issue is resolved I can accept Anthony's answer.
Learning how to use Django. I'm trying this code:
if form.is_valid() and form.has_changed():
try:
p = profile.objects.get(user_id= user.id)
#Append data to profile record
profile_record.save()
except p.DoesNotExist as e:
#Create new profile record
profile_record.save()
When user_id (FK field) isn't found I get an error:
local variable 'p' referenced before assignment
I've assumed that p = ... is the assignment?
Thank you for any pointers as to what I'm doing wrong here.
Your error is occurring because p is first set during the try section. On except, you reference p, but p has not been assigned to anything at that point, because if the except is reached, then the try section is "undone" and that is where p is assigned.
Try changing except p.DoesNotExist as e: to except Person.DoesNotExist:
Maybe you could use get_or_create https://docs.djangoproject.com/en/1.8/ref/models/querysets/#django.db.models.query.QuerySet.get_or_create
p, created = profile.objects.get_or_create(user_id=user.id)
Try to filter the query.. .get should always be used in case you know there is going to only one object while querying.
if form.is_valid() and form.has_changed():
p = profile.objects.filter(user_id = user.id)
if p:
p = p[0]
#Append data to profile record
profile_record.save()
else:
#Create new profile record
profile_record.save()
.get(field=something) will always throw error if there is no result or more than 1 result, whereas .filter() does not throw error in these cases.
Why not using
if form.is_valid() and form.has_changed():
form.save()
I am trying this form a while. I have a page which displays a form and whose databse definition looks like:
db.define_table('nskrelease',
Field('sprid',length=128,requires=IS_IN_SET(['R3.2', 'R3.3', 'R3.4'],zero=T('choose one'),error_message='must be R3.2 or R3.3 or R3.4 '),label = T('SPR')),
Field('releaseid',length=128, requires = IS_NOT_EMPTY(error_message='Release ID cant be empty'),label = T('Release')),
Field('coordinator',requires=IS_EMAIL(error_message='invalid email!') ,label=T('Co-ordinator Email')),
Field('startdate', 'datetime', default=request.now,requires = IS_NOT_EMPTY(error_message='Start date cant be empty'), label=T('Start date')),
Field('enddate', 'datetime', default=request.now, requires = IS_NOT_EMPTY(error_message='End date cant be empty'), label=T('End Date')),format='%(%releaseid)s')
db.nskrelease.releaseid.requires = IS_NOT_IN_DB(db,'nskrelease.releaseid')
db.nskrelease.coordinator.requires = IS_IN_DB(db,'auth_user.email','%(email)s')
But the problem here is the Coordinator field displays all the user in present in db.auth_user. Instead i need that to be restricted only to the Logged in user. So I tried:
db.nskrelease.coordinator.requires = IS_IN_DB(db(db.nskrelease.coordinator == 'auth.user.email'))
But it gives me error:
TypeError: __init__() takes at least 3 arguments (2 given)
Please help. Thanks in advance.
If the only email address allowed in this field is that of the currently logged in user, then maybe just set that as the default value and don't make the field writable:
Field('coordinator', writable=False,
default=auth.user.email if auth.user else None,
update=auth.user.email if auth.user else None)
However, a better approach might be to make this a reference field, so you don't have to worry about updating this field whenever there is a profile update:
Field('coordinator', 'reference auth_user', writable=False,
default=auth.user_id, represent=lambda v, r: v.email)
Can anyone see the issues with this?
def is_valid(self, bundle, request=None):
errors = {}
# Check if user already exists before allowing API to create a new one.
this_email = bundle.data.get('email', None)
object.count = MemberParticipant.objects.filter(email=this_email).count()
if object.count != 0:
errors['Login']='Duplicate email address! A participant with the ' \
'email address %s already exists.' % this_email
return errors
I'm getting the following error:
can't set attributes of built-in/extension type 'object'
I'm using a Tastypie's custom validator
Are you trying to use object.count as a single variable name? You can't mix dots into identifiers in Python without triggering attribute resolution - use something like object_count instead. As written you're trying to reassign the count attribute of the built in object class.