Batu Lab NotesPractical developer guides

Replace a URL query without string concatenation

By Batu · English technical notes

Also published in our primary archive.

Replacing a URL query with string concatenation is fragile because a URL can already contain a query and can also contain a fragment. Instead, split the URL into components, encode the replacement parameters, replace only the query field, and reassemble the result. This keeps the scheme, host, path, and fragment under structured control.

The fixture starts with view=grid&tag=python and a #section-2 fragment. urlsplit() returns a SplitResult; its _replace() method makes a new result with query changed to the locally encoded view=list&page=2 text. urlunsplit() then constructs the final URL. The first assertion confirms the complete expected URL. The second separately confirms that the fragment stayed section-2, which guards against accidentally treating it as query text.

This is URL-component manipulation, not input validation. urlsplit() can return components for strings an application should reject, so validate untrusted values according to the application’s rules. Reassembly can also normalize an otherwise equivalent URL by dropping unnecessary empty delimiters. urlsplit(), _replace(), urlencode(), and urlunsplit() used here are available in Python 3.2+. Read the official urllib.parse documentation for its parsing and reassembly details.

AI assistance disclosure: this article was drafted with AI assistance and should be adapted where an application has URL validation requirements.

from urllib.parse import urlencode, urlsplit, urlunsplit

original_url = "https://fixtures.example.test/items?view=grid&tag=python#section-2"
parts = urlsplit(original_url)
replacement_query = urlencode([("view", "list"), ("page", "2")])
updated_url = urlunsplit(parts._replace(query=replacement_query))

assert updated_url == "https://fixtures.example.test/items?view=list&page=2#section-2"
assert urlsplit(updated_url).fragment == "section-2"

print(updated_url)
https://fixtures.example.test/items?view=list&page=2#section-2