7
\$\begingroup\$

I'm generating a URL (in string) that depends on 3 optional parameters, file, user and active.

From a base url: /hey I want to generate the endpoint, this means:

  • If file is specificied, my desired output would is: /hey?file=example
  • If file and user is specified, my desired output is: /hey?file=example&user=boo
  • If user and active are specified, my desired output is: /hey?user=boo&active=1
  • If no optional parameters are specified, my desired output is: /hey
  • and so on with all the combinations...

My code, which is working correctly, is as follows (change the None's at the top if you want to test it):

file = None
user = None
active = 1

ep = "/hey"
isFirst = True

if file:
  if isFirst:
    ep+= "?file=" + file;
    isFirst = False;
  else: ep += "&file=" + file;

if user:
  if isFirst:
    ep+= "?user=" + user;
    isFirst = False;
  else: ep += "&user=" + user;

if active:
  if isFirst:
    ep+= "?active=" + str(active);
    isFirst = False;
  else: ep += "&active=" + str(active);

print ep

Can someone give me a more python implementation for this? I can't use modules as requests.

Thanks in advance.

\$\endgroup\$
  • 8
    \$\begingroup\$ Lose the ;. makes python look ugly \$\endgroup\$ – hjpotter92 Dec 17 '18 at 14:00
12
\$\begingroup\$

The most Pythonic version of this depends a bit on what you do with that URL afterwards. If you are using the requests module (which you probably should), this is already built-in by specifying the params keyword:

import requests

URL = "https://example.com/hey"

r1 = requests.get(URL, params={"file": "example"})
print(r1.url)
# https://example.com/hey?file=example

r2 = requests.get(URL, params={"file": "example", "user": "boo"})
print(r2.url)
# https://example.com/hey?file=example&user=boo

r3 = requests.get(URL, params={"user": "boo", "active": 1})
print(r3.url)
# https://example.com/hey?user=boo&active=1

r4 = requests.get(URL, params={})
print(r4.url)
# https://example.com/hey

If you do need a pure Python solution without any imports, this is what I would do:

def get_url(base_url, **kwargs):
    if not kwargs:
        return base_url
    params = "&".join(f"{key}={value}" for key, value in kwargs.items())
    return base_url + "?" + params

Of course this does not urlencode the keys and values and may therefore be a security risk or fail unexpectedly, but neither does your code.

Example usage:

print(get_url("/hey", file="example"))
# /hey?file=example

print(get_url("/hey", file="example", user="boo"))
# /hey?file=example&user=boo

print(get_url("/hey", user="boo", active=1))
# /hey?user=boo&active=1

print(get_url("/hey"))
# /hey
\$\endgroup\$
  • \$\begingroup\$ Due to the implementation of the rest of the code, I need to do it everything without any requests module, just improving the code I posted using strings. \$\endgroup\$ – Avión Dec 17 '18 at 10:09
  • 2
    \$\begingroup\$ @Avión: Just did. It captures all keyword arguments you pass to the function into one dictionary. \$\endgroup\$ – Graipher Dec 17 '18 at 10:13
  • 4
    \$\begingroup\$ Your code is good for illustrative purposes but it fails to URLencode the parameters and is therefore a potential security risk. \$\endgroup\$ – Konrad Rudolph Dec 17 '18 at 14:35
  • 1
    \$\begingroup\$ @KonradRudolph Added a short disclaimer regarding that. \$\endgroup\$ – Graipher Dec 17 '18 at 14:38
  • 1
    \$\begingroup\$ It's not just that it's a security risk, it's also that if you have & in one of the values, it will fail to send the correct value (and most likely fail in general, unless there's also another = in the values). \$\endgroup\$ – ChatterOne Dec 18 '18 at 8:33
18
\$\begingroup\$

You're pretty much reinventing urllib.parse.urlencode:

from urllib.parse import urlencode


def prepare_query_string(**kwargs):
    return urlencode([(key, value) for key, value in kwargs.items() if value is not None])

Usage being:

>>> prepare_query_string(active=1)
'active=1'
>>> prepare_query_string(active=1, user=None)
'active=1'
>>> prepare_query_string(active=1, user='bob')
'active=1&user=bob'
>>> prepare_query_string(file='foo.tar.gz', user='bob')
'file=foo.tar.gz&user=bob'
>>> prepare_query_string(file='foo.tar.gz', user='bob', active=None)
'file=foo.tar.gz&user=bob'
>>> prepare_query_string(file='foo.tar.gz', user='bob', active=1)
'file=foo.tar.gz&user=bob&active=1'
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.