Redirect to another controller function with arguments from a callback function?

3,321 views
Skip to first unread message

brac...@gmail.com

unread,
May 23, 2013, 2:51:24 PM5/23/13
to web...@googlegroups.com
In my views, I have:

{{=A('click for more information', _href=URL("myCallback", args=[1]))}}


When the anchor button is clicked, my callback will do some lookup and processing in the db, and then will redirect to a new page populated with the new information:

def search_results():
    resultSet
= request.args(0)
   
# Build HTML helpers using resultSet
    div
= DIV(.....)

   
return dict(div=div)

def myButton():
     
# Figure out the id from the request
     someId
= request.args(0)

     
# get some data from db using the id
     resultSet
= db(....)


     
# want to redirect to another page with the new data in the resultSet
    redirect
(URL('search_results', args=resultSet))

But doing the redirect with the resultSet args will screw up the URL and I'll end up with an invalid request. 

I've thought of two ways around this:

1) Create a temporary session variable and store the resultSet there. Once I'm done with the data, I'll explicitly clear it.

2) Instead of doing the database logic in the callback, pass the id to the search_results() and do the database logic there.

I'm hesitant to adopt the first option because it seems messy to create a bunch of session variables for temporary things (unless this is standard practice?).

The second option seems okay, but I'm afraid that the code will become too specific to that particular anchor tag. That is, if I create a new anchor tag to do some other search, the database logic may be different than the one inside the search_results(). For this, I guess the better question should be if the database logic code should live in the callback function or in the target redirect controller function?


In spite of this, what would be the clean or proper way of sending data with a redirect from a callback function?

Anthony

unread,
May 23, 2013, 6:10:24 PM5/23/13
to
Why do you need to redirect at all? You can just call the search_results() function directly from the myButton() function:

def search_results(resultSet):
    div = DIV(.....)
   
return dict(div=div)

def myButton():
    someId = request.args(0)
    resultSet
= db(....)
   
return search_results(resultSet)

If the search_results() function is needed in other controllers, you could define it in a model file or in a module and import it. Note, functions that take arguments (as search_results does above) are not exposed as actions accessible via URL -- they are for internal use only (same for a function that begins with a double underscore, even if it doesn't take any arguments).

Anthony

brac...@gmail.com

unread,
May 23, 2013, 4:02:29 PM5/23/13
to web...@googlegroups.com
search_results() will have to be exposed since I want to display the results on a different page (in search_results.html). I think returning the div using your suggestion would still be on the same page?



On Thursday, May 23, 2013 3:38:17 PM UTC-4, Anthony wrote:
Why do you need to redirect at all? You can just call the search_results() function directly from the myButton() function:

def search_results(resultSet):

    div
= DIV(.....)
   
return dict(div=div)

def myButton():

    someId
= request.args(0)
    resultSet
= db(....)
   
return search_results(resultSet)

If the search_results() function is needed in other controllers, you could define it in a model file or in a module and import it. Note, functions that take arguments (as search_results does above) are not exposed as actions accessible via URL -- they are for internal use only (same for a function that begins with a double underscore, even if it doesn't take any arguments).
Anthony

On Thursday, May 23, 2013 2:51:24 PM UTC-4, brac...@gmail.com wrote:

Anthony

unread,
May 23, 2013, 7:54:32 PM5/23/13
to
But presumably the myButton() function does not have its own view given that it was originally written to always redirect to another URL. In that case, why not just make the myButton.html view render the search results as desired? If you want a search_results.html view to be used from multiple functions, that's no problem either -- here are three options:

def search_results(resultSet):
    div
= DIV(.....)
    response.view = 'default/search_results.html'
   
return dict(div=div)

So, any time the search_results() function is called by another function, it sets the view to search_results.html. Or, you can set the view within the calling function:

def myButton():
    someId
= request.args(0)
    resultSet
= db(....)
    response.view = 'default/search_results.html'
   
return search_results(resultSet)

Or you could include the search_results view in another view. For example, in myButton.html, you can do:

{{extend 'layout.html'}}
{{include 'default/search_results.html'}}

Anthony

brac...@gmail.com

unread,
May 24, 2013, 11:42:46 AM5/24/13
to web...@googlegroups.com
Thanks, however I just noticed in the original code that I had incorrectly written the callback function as "myButton()". It should have been "myCallback()". I modified the code:

def search_results():

    div
= DIV(.....)
    response
.view = 'default/search_results.html'
   
return dict(div=div)

def myCallback():

     someId
= request.args(0)
     resultSet
= db(....)

     
return search_results(resultSet)

Sorry about the confusion. This way works, but after clicking the button, it displays the resultSet on a page with the URL "default/myCallback/1". I wanted it to be something like "default/search_results/1". 

But web2py follows this pattern to URLs: 
/[application]/[controller]/f

So 'f' would be the "myCallback" since it's the function that was called when you clicked on the button. I thought a redirect would fix it since I would be directing the controller function to search_results, and then the URL would be correct. What I couldn't figure out was how to pass data from the callback function to the redirected function.

Is there a way to fix this using what you suggested or is there a better way to do this (given the fixed code)?

On Thursday, May 23, 2013 6:22:52 PM UTC-4, Anthony wrote:
But presumably the myButton() function does not have its own view given that it was originally written to always redirect to another URL. In that case, why not just make the myButton.html view render the search results as desired? If you want a search_results.html view to be used from multiple functions, that's no problem either -- here are three options:

def search_results(resultSet):
    div
= DIV(.....)
    response.view = 'default/search_results.html'
   
return dict(div=div)

So, any time the search_results() function is called by another function, it sets the view to search_results.html. Or, you can set the view within the calling function:

def myButton():
    someId
= request.args(0)
    resultSet
= db(....)
    response.view = 'default/search_results.html'
   
return search_results(resultSet)

Or you could include the search_results view in another view. For example, in myButton.html, you can do:

{{extend 'layout.html'}}
{{include 'default/search_results.html'}}

Anthony
 

On Thursday, May 23, 2013 4:02:29 PM UTC-4, brac...@gmail.com wrote:

Anthony

unread,
May 24, 2013, 12:34:40 PM5/24/13
to
It might be helpful if you explain what you're really trying to do. Do you want users to be able to click different links that all point to the /search_results page, but with different types of searches done depending on the link? In that case, why not something like:

{{=A('click for more information', _href=URL("search_results", args=['myCallback', 1]))}}

def search_results():
   
if request.args(0) == 'myCallback':
        resultSet
= myCallback(request.args(1))
    ...
    return dict(resultSet=resultSet)

def myCallback(someId):
   
return db(...)

Of course, in this simple example there's no need for the separate myCallback function, but I assume you want to have multiple such functions with more complexity. The idea is to send all the requests to the search_results function, and use the request.args to identify the type of results, and potentially dispatch to external functions to generate those results.

Anthony

brac...@gmail.com

unread,
May 24, 2013, 1:47:24 PM5/24/13
to web...@googlegroups.com
I wanted to display a resultset using the same view template. To be more specific, we can take an online store example:

A navbar will consist of several main links which are categories. A list of subcategory links will show up if you hover over a category link.

When a user clicks on a subcategory, all products associated with that category+subcategory will be displayed in the main html body.

When a user clicks on a category, all subcategories associated with that category will be displayed in the main html body. A user can then click on a subcategory and then it will refer to the scenario above (rendering its respective products).

For a category, this is an example anchor link:
{{=A(category.name, _href=URL('category_nav_callback', args=1))}} and the resulting URL should be 'default/search_results/category/1'

For a subcategory, this is an example anchor link:
{{=A(subcategory.name, _href=URL('subcategory_nav_callback', args=[1,1]))}} and the resulting URL should be 'default/search_results/category/1/subcategory/1'

From there, their respective callback functions in the controller would manipulate the database to get an eventual result set. 

To display these results, I wanted to use a generic template view (search_results.html) and pass a resultset to it and it would render it on that page.

In the future, I may want to add in new links (like genre or favorites), and then I can reuse the search_results template to display the results.

Maybe I overcomplicated this too much? I could just create individual search pages like this:

{{=A(category.name, _href=URL('searchCategory', args=1))}}

{{=A(category.name, _href=URL('searchSubcategory', args=[1,1]))}}



and then in controllers:

def searchCategory():
   
# do stuff
   
return dict(resultSet=resultSet)


def searchSubcategory():
   
# do stuff
   
return dict(resultSet=resultSet)


And I could create their views in both searchCategory.html and searchSubcategory.html:

{{for i in resultSet:}}
   
# do stuff
{{pass}}


But it seems like a lot of repetitive work, although it is probably the easier approach?

On Friday, May 24, 2013 12:33:33 PM UTC-4, Anthony wrote:
It might be helpful if you explain what you're really trying to do. Do you want users to be able to click different links that all point to the /search_results page, but with different types of searches done depending on the link? In that case, why not something like:

{{=A('click for more information', _href=URL("search_results", args=['myCallback', 1]))}}

def search_results():
   
if request.args(0) == 'myCallback':
        resultSet
= myCallback(request.args(1))
    ...
    return dict(resultSet=resultSet)

def myCallback(someId):
   
return db(...)

Of course, in this simple example there's no need for the separate myCallback function, but I assume you want to have multiple such functions with more complexity. The idea is to send all the requests to the search_results function, and use the request.args to identify the type of results, and potentially dispatch to external functions to generate those results.

Anthony

On Friday, May 24, 2013 11:42:46 AM UTC-4, brac...@gmail.com wrote:

Anthony

unread,
May 24, 2013, 1:51:59 PM5/24/13
to web...@googlegroups.com
So why not use a single function (e.g., search_results), and just pass all the other information as URL args? In the search_results function, you can read the request.args and deliver the appropriate results accordingly.

Anthony

brac...@gmail.com

unread,
May 24, 2013, 2:09:35 PM5/24/13
to web...@googlegroups.com
Yeah, I'll try your suggestion and see what happens:

{{=A('click for more information', _href=URL("search_results", args=['myCallback', 1]))}}
Reply all
Reply to author
Forward
0 new messages