Dynamically update field in web2py SQLFORM - python

Using an example close to the one from the many to many relationship example in the book:
db = DAL("sqlites://storage.sqlite")
db.define_table('persons',
Field('name'))
db.define_table('things',
Field('name'))
db.define_table('ownership',
Field('person', 'reference persons'),
Field('thing', 'reference things'))
I have an additional table :
db.define_table('usages',
Field('person', 'reference person',requires = IS_IN_DB(db, db.person.id, '%(name)s'),
Field('thing', 'reference thing'),
Field('usage'))
For the table usages I want to have dropdown menus for both person and thing fields hence the use of requires .....
However I would like to have the dropdown menu from thing to changes according to the selection in person (to limit the choice to things a person owns).
I thought about using requires as i did to get the dropdown menu, however I cannot use db.usages.person in a requires = IS_IN_DB(...) because the entry is not created and therefore it throws an exception.
What I tried :
requires = IS_IN_DB(db, db((db.usages.person == db.persons.id) &(db.persons.id == db.ownership.person) &(db.ownership.person == db.things.name)))
Using requires = IS_IN_DB(db,db.things.id, '%(name)s') gives me a dropdown menu for thing but it lists all of the them regardless of the selection in person.
Any idea on how to solve this?

There is no built-in way to do what you want. You will need to use Javascript to (a) detect when a selection is made in the person field, and (b) make an Ajax request to the server to fetch a list of options for the thing select element.
For some examples, check out these posts on the web2pyslices site, and also check out plugin_lazy_options_widget.
Also, regarding your code:
requires = IS_IN_DB(db, db((db.usages.person == db.persons.id) &(db.persons.id == db.ownership.person) &(db.ownership.person == db.things.name)))
note, that the second argument to IS_IN_DB must be a Field object or field name, not a DAL Set object. If you want to limit the set of options via a filter, the first argument can be a Set object (though, as noted, that won't help in this case, as you don't know what the filter criterion will be until a selection is made in the browser).

Related

Access APScheduler cron trigger field values in python

Similar to this question I want to extract the info of a cron job trigger from APScheduler.
However, I need the "day_of_week" field and not everything. Using
for job in scheduler.get_jobs():
for f in job.trigger.fields:
print(f.name + " " + str(f))
i can see all the fields, e.g. week,hour,day_of_week , but
job.trigger.day_of_week is seemingly 'not an attribute' of the "CronTrigger" object. I'm confused as to what kind of object this job.trigger is and how its fields are packed. I tried to read the code on github, but it is even more puzzling.
How do I extract only the one field day_of_week, and how is this trigger class structured?
Diving deeper I found that
apscheduler.triggers.cron.fields.DayOfWeekField
I can find by indexing the job.trigger.fields[4], which seems really bad style, since it depends on the 'position'of the field. What I get is this DayOfWeekField, from which comically I am not able to retrieve it's value either:
a.get_value
<bound method DayOfWeekField.get_value of DayOfWeekField('day_of_week', '1,2,3,4')>
The structure of the fields is coded here, but I don't know what to do with dateval, the argument of get_value().
Eventually, after hopefully understanding the concept, I want to do
if job-day_of_week contains mon
if job-day_of_week == '*'
print ( job-day_of_week )
I am grateful for any suggestions/hints!
Looking at the code, you should be able to get the day_of_week field without hardcoding the index by using the CronTrigger class's FIELD_NAMES property, e.g.
dow_index = CronTrigger.FIELD_NAMES.index('day_of_week')
dow = job.trigger.fields[dow_index]
Getting the value of the field is a bit more complicated, but it appears that BaseField implements the str function that should give you the value of the expression that created the field as a string that you could parse to find what you want:
dow_value_as_string = str(dow)
if 'mon' in dow_value_as_string:
# do something
if dow_value_as_string = "*":
# do something else

web2py validating db model for group menbers

I am using web2py, and am trying to build a field for auth_user, that should be validated to be a member of a certain group. So, in the models/db.py I have added a field that tells who is the manager of the user:
auth.settings.extra_fields['auth_user']=[Field('manager', 'reference auth_user')]
Then I have set up to db.auth_user, db.auth_group and db.auth_membership to contain users that belong to group 'managers'
And what I would like now to achieve is to validate user input so, that the 'manager' field of the auth_user could contain only users from the group 'managers'. I have gone through quite a few variations, following is maybe closest to making sense in theory in my mind:
group_id = auth.id_group('managers')
all_users_in_group = db(db.auth_membership.group_id==group_id)._select(db.auth_membership.user_id)
db.auth_user.auditor.requires = IS_IN_DB(db, db(~db.auth_user.id.belongs(all_users_in_group)).select(db.auth_user.id))
But even that is failing with
<type 'exceptions.AttributeError'>('Table' object has no attribute 'managers')
A perfect solution to my problem would show in the drop down menu not auth_user.id, but auth_user.first_name concatenated with auth_user.last_name.
I have used similar code to the following and confirm that is works with web2py 2.17.2-stable+timestamp.2018.10.06.18.54.02 (Running on nginx/1.14.0, Python 3.6.7).The previous answer will not work for this version.
group_id = auth.id_group('managers')
user_rows = db(db.auth_membership.group_id == group_id)._select(db.auth_membership.user_id)
query = db(db.auth_user.id.belongs(user_rows))
db.auth_user.auditor.requires = IS_IN_DB(query, db.auth_user.id, '%(first_name)s %(last_name)s'
You have done it correctly in your answer, but you can improve it.
The first argument of the validator can be a database connection or a DAL Set. So you can pass
directly a db query as first argument, like this
query = db((db.auth_membership.group_id == auth.id_group('managers')) &
(db.auth_membership.user_id == db.auth_user.id))
db.auth_user.auditor.requires = IS_IN_DB(query, db.auth_user.id, '%(first_name)s %(last_name)s')
You can also write query like below:
query = db((db.auth_group.role == 'managers') &
(db.auth_membership.group_id == db.auth_group.id) &
(db.auth_membership.user_id == db.auth_user.id))
I actually managed to solve this, but I would definitely not call this elegant. Would there be more idiomatic way to do this in web2py? Following is what I added to models/db.py
group_id = auth.id_group('managers')
rows=db(db.auth_membership.group_id==group_id).select(db.auth_membership.user_id)
rset=set()
for r in rows:
rset.add(int((r.user_id)))
db.auth_user.auditor.requires = IS_IN_DB(db(db.auth_user.id.belongs(rset)), db.auth_user.id, '%(first_name)s %(last_name)s')

Pass kwargs into Django Filter

When viewing model entries from within Django Admin, you can specify filters. How can I mimic this behavior? Not to familiar with kwargs but something similar to this:
foo = Model.objects.filter(**__exact='**')
where the first set of ** would be a field in the model and the second set would be an entry. Basically making the queries variable, based on what the user chooses on the front end. How would I send that variable sort option to the view, and then return it back to the webpage. What about using a dictionary? Please help
This SO question has proven to be a little helpful, but still cannot grasp it completely.
You can unpack a python dict as your filter parameters using **
your_filters = {
'field_1__exact': value_1,
'field_2__gte': value_2,
}
Model.objects.filter(**your_filters)
Said that, you can built your query filters(a python dict) dynamically based on an user input.

Best option for alternate/suggested search in Django

I have a Django app that searches for data from a model Classified and displays them using simple querysets based on the input term. This works perfectly and I've got no complains with this method.
However, if someone enters a term that returns no data, I would like to provide an option with alternate/suggested search.
Eg: Someone searches 'Ambulance Services' which doesn't return data. I'd like to suggest 'Ambulance' or 'Services' as alternate search options which may return data from the model depending on the data present in the model.
I Googled suggested search in django and it gave me options of Haystack/elastic search, etc which I'm not sure are really required since the search is across just one model.
Note: SO tells me that my question may be closed as it is subjective. If thats the case, please suggest where I can move it to. Thank you!
This is just an idea, but might work for you:
The user enters the search data: "Ambulance Services"
If the query inside the view returns nothing, redirect to the same view using your selected alternative search data, lets say "Ambulance", and a flag value thats saids the view you're preforming a suggested search.
You must have two things in consideration:
What if the alternate search don't returns anything either? You have to set a recursion limit here.
How I'm going to select the data of the alternate search? Well, thats another question about a completly different topic.
This is this idea in code:
def search(request, data, alternate=False, recursion_level=3):
result = Classified.objects.filter(some_filed=data)
if 0 == result.count() and 0 != recursion_level: # Conditions needed for perform alternate search.
new_data = select_new_data(data) # The logic inside this function is up to you.
r_level = recursion_level - 1 # Decreas recursion level.
return redirect('search', alternate=True, recursion_level=r_level) # Redirecting using view name, you can use others
# forms of redirection see link below for visit
# the Djando redirect API doc.
else:
return_data = {}
if alternate:
# You can use this flag in order to reflect
# to the user you have performed an alternate search.
# Example:
return_data["alternate"] = True
# Build your return data.
# and render.
#return render_to_template()
Django redirect doc: redirect
Haystack is, indeed, a great option, here you will find how to give 'spelling suggestions', you can see an example in this OS question/answer
No matter you have only one model, this tool is really great, simple to install /set up/use, and very flexible.
Perhaps this helps as well.

Is it considered bad practice to use a widgets title attribute to refer to it?

Would it be considered bad practice to use a widgets title attribute to refer it?
For example I have a number of custom radioBoxCtrls on a panel
I only ever need to get/set all the values at once
so the container class(a panel) for the radioBoxCtrls objects has the following methods
get_options()
set_options()
To set options for all the radioBoxCtrls a dictionary is passed to the set_options() method.
Each key, value pair in the dictionary is a title of a radioBoxCtrl and the title of the button on the radioBoxCtrl that should be set
def set_options(self, options={}):
"""
This method sets which radio button is selected
on each RadioBoxCtrl object
#param options: A dictionary
Each key is the title of a RadioBoxCtrl
each keys value is the button on the radio box that is to be selected
"""
for option_box in self.option_boxes:
if option_box.title in options.keys()
option_box.set_selected_button(options[option_box.title])
def get_options(self):
"""
This method returns a dictionary of the selected options
Each key is the title of a RadioBoxCtrl object
and each keys value is the name of the button selected on the radio box
"""
options = defaultdict(list)
for option_box in self.option_boxes:
options[option_box.title]=option_box.get_selected_btn()
return options
So (in an attempt to be clear) when I call the set method from my controller
I pass in a dictionary like so:
options = {"Name of radioBoxCtrl": "Option 2", ... }
self.gui.optionsPanel.set_options(options)
Why the hell do you want do that? (you may ask)
Short answer: mvc
I want to create a suitable layer of abstraction. All that my controller needs to know with
regard to the options is how to get them to pass to the model when some processing needs to be done and how set them when a config file is loaded...
I thought it would simplify things if I could just call one method to set and vice-versa -but Im not so sure now!
I realize that this is probably more of question on the acceptability of refering to objects by some string attribute they posses, which in my case just happens to be the title.. so feel free to answer it accordingly..
Please feel free to improve the title of question(I wasnt really sure how to word it) and add tags.
Thanks
I can't tell whether the gurus would call it bad practive. I just know I'd never do it - it is slower, more error-prone, possibly makes it very hard to change a title's name (admittedly, only if you hardcode it everywhere) and (arguably) very inelegant. At least you should associate te options with the widget's identity, which still smells imho, but at least has a few problems less. Maybe that whole part of the application should be refactored to be less centralized so the issue disappears completely.
Edit on how to refactor: Honestly, I can't tell - I know little about the application. The obvious approach would be subclassing all widgets and making them responsible for somehow getting the options and changing accordingly. But I can't tell whether that's really feasible.

Categories

Resources