0

I'm just learning Flask and I want to have two pages in my views: the search page has a form where the user can input some parameters, and the view results page (which I haven't written yet) will just display the results of the search.

Right now I'm having trouble getting the search page to redirect the form data to the view results page. I would expect the view results page to be at a URL that has the query string in it, like /search/?=..., and then, of course, I need to be able to access that data easily in view_results().

When I pass form.data into view_results(), as below, I get a string representation of the form's dictionary in the URL and in data... which makes sense I think... but isn't quite what I want. I think I'm misunderstanding this...

@main.route('/search/', methods=['GET', 'POST'])
def search():
    form = SearchForm()
    if form.validate_on_submit():
        return redirect(url_for('.view_results', data=form.data))
    return render_template('search.html', form=form)

@main.route('/search/<data>')
def view_results(data):
    return "placeholder"
KaleidoEscape
  • 115
  • 1
  • 13
  • Don't pass ``form.data`` to the view , pass ``form.field.data`` . replace the field with your field name – Ali Faki Feb 12 '16 at 09:56
  • @AliFaki Oh, I see... just pass them one at a time and build the query string in the route myself? e.g. `@main.route('/search/results?field1=&field2=&field3=')` – KaleidoEscape Feb 12 '16 at 20:52

1 Answers1

0

To answer my own question (although there might be a simpler and better way that I'm just not aware of, but here's what I ended up doing for now)...

I created a custom routing converter (the method for doing this is described here), and registered it in my app so I could use it in my view_results() route. I added this to my utils.py that I have in the same directory as my app's init:

from werkzeug.routing import BaseConverter

class DictionaryConverter(BaseConverter):
    """Convert python dictionary to URL query string and back."""

    def to_python(self, value):
        d = {}
        for item in value.split('&'):
            k, v = item.split('=')
            if k != 'csrf_token':
                d[k] = v
        return d

    def to_url(self, values):
        pairs = []
        for k in values:
            #don't expose the csrf token in the query string
            if k != 'csrf_token':
                pairs.append('{}={}'.format(k, values[k]))
        return '&'.join(BaseConverter.to_url(self, value=pair) for pair in pairs)

Then in my app __init__.py I made sure to register the custom converter after initializing the app, but before loading the blueprint that uses it (my main blueprint):

app = Flask(__name__)

# ...

from .utils import DictionaryConverter
app.url_map.converters['dict'] = DictionaryConverter

from .main import main as main_blueprint
app.register_blueprint(main_blueprint)

My search view sends the data from request.form (which is where the data goes for POSTs) along to the results view. I have it using code=303 to remove ambiguity and make certain the request is sent as a GET, but this is might not be a big deal here.

@main.route('/search/', methods=['GET', 'POST'])
def search():
    form = SearchForm()
    if form.validate_on_submit():
        return redirect(url_for('.view_results', data=request.form), code=303)
    return render_template('search.html', form=form)

Finally, my results view uses the new converter, and then I can access the data as per usual (as you can see, I'm still not really doing anything with it yet, but now I should be able to start):

@main.route('/search/results?<dict:data>')
def view_results(data):
    for i in data:
        print(i, data[i])
    return "placeholder"

This seems to work but I still feel like I'm just missing something obvious from Flask that wouldn't require creating a custom converter...

Community
  • 1
  • 1
KaleidoEscape
  • 115
  • 1
  • 13