9

I want to filter some database results, for a given url with some date parameter (like this url.com?start=2018-12-12). The normal way to read a parameter is with request.args.get, accessing the value of the underlying ImmutableMultiDict, which gives me the optional arguments default and type.

My first attempt now was this:

@app.route()
def event():
   ektempo = request.args.get('start', default = datetime.date.today(), type = datetime.date)
   ...

Which works for the default parameter, but not for the passed date string, since datetime.date needs three integers as parameters. Normally I would get my date object by datetime.datetime.strptime and a format string. Is there a way to pass a datetime string as a url parameter to flask and cast it pythonicly to datetime.date.

I like the way request.args.get works, but it seems I can not get a datetime.date object from it easily with a given url parameter. Is there another way to acheive it by flask bult-in methods, which verifies the parameter and on no parameter or ValueError gives me the default?

Paul Würtz
  • 1,641
  • 3
  • 22
  • 35
  • Get the date string, then convert to a `datetime.date` as a separate step. – John Gordon Nov 24 '18 at 17:01
  • FWIW It seems to me that you are trying to condense too much logic into a single line. I'd first get the string (whether a default value or from the request), and then worry about converting it to date object. – DeepSpace Nov 24 '18 at 17:02
  • Why don't you juts pass a date like a number - say 20181212120222 meaning 2018-12-12 12:02:22 and then parse it into date within your endpoint? – dmitryro Nov 24 '18 at 17:06
  • But I mean for other types it works perfectly... For me it fells, like if I had a type and a "convert" argument it would be perfect. I mean you can not expect, that what fits into a url parameter would map to a constructor of an object always... Don't know, maybe `request.args.get` was only intended for primitives, but it seems like a to basic thing to me, that you could have some possibilities, to get dates or geocordinates verificators for url parameters... don't know... It seems just to many LOC for me, to manually verify these basic types for websites... – Paul Würtz Nov 24 '18 at 17:11
  • 2
    I'm not sure of the distinction you are making here between type and a convert argument. The type argument is already a callable, you can pass your own function in there which accepts a string and returns whatever you like. – Daniel Roseman Nov 24 '18 at 17:39
  • You are absolutly right! I thougth it had something to do with pythons built-in `type` keyword, and you could only pass a class there, but you can pass any function! Terrific, so many thanks!!! – Paul Würtz Nov 24 '18 at 17:58

2 Answers2

16

As pointed out by Daniel Roseman, you can pass any function to type, so I just defined a little helper function to do the conversion of the date-string and here is it:

def toDate(dateString): 
    return datetime.datetime.strptime(dateString, "%Y-%m-%d").date()

@app.route()
def event():
    ektempo = request.args.get('start', default = datetime.date.today(), type = toDate)
    ...

Yeah, very cool, thanks a lot Daniel, just what I searched for! :)

Paul Würtz
  • 1,641
  • 3
  • 22
  • 35
6

The accepted solution does the work for the use case (and got my upvote :); yet if the parameter doesn't conform to the expected format, it will set it to the default value (today) silently, with no feedback to the caller - as this is flask's/werkzeug's intended behavior.

Here's how to use it to validate & return an error if the client has passed an invalid value - piggybacking on OP's solution:

from flask import jsonify

def to_date(date_string): 
    try:
        return datetime.datetime.strptime(date_string, "%Y-%m-%d").date()
    except ValueError:
        raise ValueError('{} is not valid date in the format YYYY-MM-DD'.format(date_string))

@app.route()
def event():
    try:
        ektempo = to_date(request.args.get('start', default = datetime.date.today().isoformat()))
    except ValueError as ex:
        return jsonify({'error': str(ex)}), 400   # jsonify, if this is a json api

Todor Minakov
  • 19,097
  • 3
  • 55
  • 60