Ok this one should be an easy question id imagine, but cannot figure it out.
Im passing form data to the controller and attempting to do a data search there, which in turn, runs this..
def initLogin():
userName = request.vars.user_name;
counter = db(db.Users.UserName == userName).count()
if counter > 0:
return DIV("User exists")
return DIV("user does not exist")
I have checked the value is being passed correctly( which is is) by returning userName instead of the string, which shown me it was the correct value, and when i had a direct string of a correct username, it seemed to work. So my question is.. how do you run a count() function with web2py databases correctly using variables?
Your code is correct and shouldn't be giving you any problems, the only possible problem should be in your userName var not being what you expected or an incorrect sql query. I recommend that you try changing your controller to:
def initLogin():
userName = request.vars.user_name;
counter = db(db.Users.UserName == userName).count()
lastExecutedQuery = db._lastsql
return DIV( lastExecutedQuery )
And check if the query being performed is the one that you expected.
Related
I return object from the MongoDB database, using python. I want to see now if the property revokedTokens exists. This is my code:
usersCollection = db["users"]
searchQuery = { "username": username }
user = usersCollection.find_one(searchQuery)
print("user is:")
print(user)
if hasattr(user, "revokedTokens"):
print("appending")
user["revokedTokens"].append("another")
#user["revokedTokens"].append(jwt)
else:
print("adding first element")
user["revokedTokens"] = ["first"]
#user["revokedTokens"] = [jwt]
but I always end up in the wrong print ( print("adding first element") ) , so I don't know how to check this. Thanks everyone for correcting me, I am a python newbie!
find_one returns a dict so what you need to ask is if "revokedTokens" in user or if user.get('revokedTokens') is not None
Generally, it is probably better practice to use the $exists MongoDB operator.
To make your code work (if you don't want to use the MongoDB query way), you should replace:
if hasattr(user, "revokedTokens"):
with
if "revokedTokens" in user:
This is because the query returns a dict object, and not a structured class
For my web app I have some variables being injected into the base/all templates with the following code:
#app.context_processor
def inject_into_base():
# If user is not logged in
if not current_user.is_authenticated:
# Set count to none
count = None
role = None
# Else Count is set to the number of unread messages
else:
count = db.session.query(message).filter(message.user_id_to == current_user.user_id).filter(message.status == False).count()
role = db.session.query(user_society_role.role_id).filter_by(user_id=current_user.user_id).order_by(user_society_role.role_id.desc()).first().role_id
return dict(count=count, role=role)
All is well until I hit a certain route that contains the line:
Survey.q9 = Survey.q9.split(',')
Which just simply splits the string into a list. It results in
sqlalchemy.exc.InterfaceError: <unprintable InterfaceError object>
If I hard code the two db.session.query's found in inject_into_base it will run just fine, and if I comment out the .split(', ') in the route it also works fine.
I have also found assigning anything to Survey.q9 breaks my app, and ONLY on this one certain route.
Any help is much appreciated, this is my first time asking a question on here so please go easy on me :)
Fixed my issue, turns out sqlalchemy's autoflush feature was breaking my app, all I had to do to fix this was add db = SQLAlchemy(app, session_options={"autoflush": False}) to my __init__.py
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.
I have the following Django Model:
class State(models.Model):
name = models.CharField(max_length=80,null=False)
latitude = models.CharField(max_length=80,null=False)
longitude = models.CharField(max_length=80,null=False)
def __unicode__(self):
return self.name
In my views.py file I've created the following method:
def getCoords(request):
if request.is_ajax():
if request.method == 'POST':
try:
state = request.POST['state'] #receives the state from JS
stateInstance = State.objects.get(name=state)
stateLat = stateInstance.latitude
stateLong = stateInstance.longitude
data = {"lat" : stateLat, "long" : stateLong}
except State.DoesNotExist:
return HttpResponse("No record")
return HttpResponse(simplejson.dumps(data)) #returns json
So I'm sending this method a param through ajax, let's say "California". The state variable gets the value (I have proved that) but the query doesn't get executed, it returns the query doesn't match message. I've tried with the following as well:
state = request.POST['state']
if state == 'California':
return HttpResponse("Yes!")
else:
return HttpResponse(state)
When this snippet returns the state it displays California which means state's value is correct but the query is not executed properly. I don't know what's going on. Any thoughts?
Make sure you're connecting to the correct database in your settings.py. Make sure the record actually does exist with plain old SQL.
SELECT * FROM [app_name]_state WHERE name = 'California';
Also, try it from the Django shell. python manage.py shell
>>> from [app_name].models import State
>>> s = State.objects.get(name='California')
>>> s
<State: California>
Make sure that what is actually being sent is sent as POST, and not accidentally a GET variable in the URL. Also, make sure that the post variable has no extra characters and that it is actually valid. A decent way to do that is to just print it to the console, or if AJAX, with Firebug.
# Single quotes added so you can see any extra spaces in the console
print "'%s'" % state
I have a class that is taking in an Id and trying to update the variable current_account but when I print out the details of the current_account it hasn't updated.
Anyone got any ideas for this? New to python so might be doing something stupid that I can't see.
class UserData:
def __init__(self, db_conn=None):
if None == db_conn:
raise Exception("DB Connection Required.")
self.db = db_conn
self.set_my_account()
self.set_accounts()
self.set_current_account()
def set_current_account(self, account_id=None):
print account_id
if None == account_id:
self.current_account = self.my_account
else:
if len(self.accounts) > 0:
for account in self.accounts:
if account['_id'] == account_id:
self.current_account = account
print self.current_account['_id']
else:
raise Exception("No accounts available.")
Assume that set_my_account() gets a dictionary of account data and that set_accounts() get a list of dictionaries of account data.
So when I do the following:
user_data = UserData(db_conn=db_conn)
user_data.set_current_account(account_id=account_id)
Where db_conn is a valid database connection and account_id is a valid account id.
I get the following out of the above two lines.
None
518a310356c02c0756764b4e
512754cfc1f3d16c25c350b7
So the None value is from the declaration of the class and then the next two are from the call to set_current_account(). The first id value is what I'm trying to set. The second id value is what was already set from the class __init__() method.
There were a lot of redundancies an un-Pythonic constructions. I cleaned up the code to help me understand what you trying to do.
class UserData(object):
def __init__(self, db_conn):
self.db = db_conn
self.set_my_account()
self.set_accounts()
self.set_current_account()
def set_current_account(self, account_id=None):
print account_id
if account_id is None:
self.current_account = self.my_account
else:
if not self.accounts:
raise Exception("No accounts available.")
for account in self.accounts:
if account['_id'] == account_id:
self.current_account = account
print self.current_account['_id']
user_data = UserData(db_conn)
user_data.set_current_account(account_id)
You used default arguments (db_conn=None) when a call without an explicit argument is invalid. Yes, you can now call __init__(None) but you could also call __init__('Nalum'); you can't protect against everything.
By moving the "No accounts" exception the block fast-fails and you save one level of indention.
The call UserData(db_conn=db_conn) is valid but unecessarily repetitive.
Unfortunately, I still can't figure out what you are trying to accomplish and this is perhaps the largest flaw. Variable names are terribly important for help the reader (which may be the future you) make sense of code. current_account, my_account, account_id and current_account['_id'] so obscure the intention that you should really consider more distinct, informative names.
Figured out what it was.
The data was being changed else where in the code base. It is now working as expected.
Thanks guys for pointing out the Python centric things that I was doing wrong, good to get it.