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.

asked Dec 17, 2018 at 9:53
\$\endgroup\$
1
  • 8
    \$\begingroup\$ Lose the ;. makes python look ugly \$\endgroup\$ Commented Dec 17, 2018 at 14:00

2 Answers 2

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
answered Dec 17, 2018 at 10:07
\$\endgroup\$
9
  • \$\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\$ Commented Dec 17, 2018 at 10:09
  • 2
    \$\begingroup\$ @Avión: Just did. It captures all keyword arguments you pass to the function into one dictionary. \$\endgroup\$ Commented Dec 17, 2018 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\$ Commented Dec 17, 2018 at 14:35
  • 1
    \$\begingroup\$ @KonradRudolph Added a short disclaimer regarding that. \$\endgroup\$ Commented Dec 17, 2018 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\$ Commented Dec 18, 2018 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'
answered Dec 17, 2018 at 10:12
\$\endgroup\$

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.