Using Hashids To Hide Ids of Objects in Django - Var
Using Hashids To Hide Ids of Objects in Django - Var
/var/
Various programming stuff
Ai
Android
Clojure
Css
Django
Elixir
Flask
Gaming
Git
Html
Javascript
Networking
Pelican
Postgresql
Python
Spring
Unix
Wagtail
Hello! If you are using an ad blocker but find something useful here and want to support me please
consider disabling your ad blocker for this site.
Thank you,
Serafeim
A common pattern in Django urls is to have the following setup for CRUD operations of your objects.
Let’s suppose we have a Ship object. It’s CRUD urls would be something like:
This is very easy to implement using class based views. For example for the detail view add the following
to your views.py:
class ShipDetailView(DetailView):
model = models.Ship
https://spapas.github.io/2021/01/07/django-hashids/ 1/7
25-07-2023 08:07 Using hashids to hide ids of objects in Django — /var/
"detail/<int:pk>/",
login_required(views.ShipDetailView.as_view()),
name="ship_detail",
),
This path means that it expects an integer (int) which will be used as the primary key of the ship (pk).
Now, a common requirement if you are using integers as primary keys is to not display them to the public.
So you shouldn’t allow the users to write something like /ships/detail/43 to see the details of ship 43.
Even if you have add proper authorization (each user only sees the ids he has access to) you are opening a
window for abuse. Also you don’t want the users to be able to estimate how many objects there are in your
database (if a user creates a new ship he’ll get the latest id and know approximately how many ships are in
your database).
One simple requirement is to use some encryption mechanism to encode the ids to some string and display
that string to the public urls. When you receive the string you’ll then decode it to get the id.
Thankfully, not only there’s a particular library that makes this whole encode/decode procedure very easy
but Django has functionality to make trivial to integrate this functionality to an existing project with only
miniman changes!
The library I propose for this is called hashids-python. This is the python branch of the hashids library that
works for many languages. If you take a look at the documentation you’ll see that it can be used like this:
This library offers two useful utilities: Define a random salt so that the generated hashids will be unique
for your app and add a minimum hash length so that the real length of the id will be obfuscated. I’ve found
out that a length of 8 characters will be more than enough to encode all possible ids up to 99 billion:
hashids = Hashids(min_length=8)
len(hashids.encode(99_999_999_999)) # 8
This is more than enough since by default django will use an integer to store the primary keys which is
around 4 billion (you actually can
use 7 characters to encode up to 5 billion but I prefer even numbers.
Finally, you can use a different alphabet, for example to use all greek characters:
hashids = Hashids(alphabet='ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩ')
hashids.encode(123) # 'ΣΝΦ'
This isn’t recommended though for our case because not all characters are url-safe.
To integrate the hashids with Django we are going to use a custom path converter. The custom path
converter is similar to the int portion of the "detail/<int:pk>/" of the url i.e it will retrieve something
and convert it to a python object. To implement your custom path converter just add a file named utils.py
in one of your applications with the following conents:
def h_encode(id):
return hashids.encode(id)
https://spapas.github.io/2021/01/07/django-hashids/ 2/7
25-07-2023 08:07 Using hashids to hide ids of objects in Django — /var/
def h_decode(h):
z = hashids.decode(h)
if z:
return z[0]
class HashIdConverter:
regex = '[a-zA-Z0-9]{8,}'
The above will generate a hashids global object with a min length of 8 as discussed above and retrieving a
custom salt from your settings (just add HASHIDS_SALT=some_random_string to your project settings). The
HashIdConverter defines a regex that will match the default aplhabet that hasid uses and two methods to
convert from url to python and vice versa. Notice that hashids.decode returns an array so we’ll retrieve the
first number only.
To use that custom path converter you will need to add the following lines to your urls.py to register your
HashIdConverter as hashid:
register_converter(HashIdConverter, "hashid")
That’s it! Your CBVs do not need any other changes! The hashid will match the hashid in the url and
convert it to the model’s pk using the to_python method we defined above!
Of course you should also add the opposite direction (i.e convert from the primary key to the hashid). To
do that we’ll add a get_absolute_url method to our Ship model, like this:
class Ship(models.Model):
def get_hashid(self):
return h_encode(self.id)
def get_absolute_url(self):
return reverse("ship_detail", args=[self.id])
Notice that you just call the reverse function passing self.id; everything else will be done automatically
from the hashid custom path generator to_url method. I’ve also added a get_hashid method to my model
to have quick access to the id in case I need it.
Now you don’t have any excuses to not hide your database ids from the public!
Posted by Serafeim Papastefanos Thu 07 January 2021 django django, hashids, python
https://spapas.github.io/2021/01/07/django-hashids/ 3/7
25-07-2023 08:07 Using hashids to hide ids of objects in Django — /var/
Tweet
Comments
ALSO ON SPAPAS.GITHUB.IO
https://spapas.github.io/2021/01/07/django-hashids/ 4/7
25-07-2023 08:07 Using hashids to hide ids of objects in Django — /var/
3 Comments
Name
Share
H
HK
2 years ago
Hello Serafeim.
Really thank you for this article, it's very helpful!
I'm kind of stuck at the reverse function that you mentioned at the end. It's showing a Namerror th
Is reverse a function that you made or you imported from a library?
1 0 Reply • Share ›
Hello friend, reverse is a django function. You can import it like this
1 0 Reply • Share ›
H
HK > spapas
2 years ago
0 0 Reply • Share ›
Atom | RSS
Support me
You can show your appreciation for anything useful you've found here by buying me a coffee
https://spapas.github.io/2021/01/07/django-hashids/ 5/7
25-07-2023 08:07 Using hashids to hide ids of objects in Django — /var/
Buy me a coffee
or donating me some bitcoin to this address:
bc1qx42vp8napheaewhnx0rk0mttdrdrtvw6czvxhl
Subscribe
Do you want to get notified via email when I post new content? Click here to subscribe!
Recent Posts
Categories
ai
android
clojure
css
django
elixir
flask
gaming
git
html
javascript
networking
pelican
postgresql
python
spring
unix
wagtail
Tags
python, django, jquery, datatables, ai, whisper, whisper.cpp, subtitles, video, auto-subtitling, subtitiling,
transcribe, html, javascript, media, storage, unpoly, access, database, windows, accdb, mdb, postgresql,
https://spapas.github.io/2021/01/07/django-hashids/ 6/7
25-07-2023 08:07 Using hashids to hide ids of objects in Django — /var/
development, inlines, clojure, cmd, pdf, wkhtmltopdf, forward-proxy, reverse-proxy, proxy, networking, http,
dj-rest-auth, rest, django-rest-framework, authentication, tokens, migrations, foreignkey, dark-souls, dark-
souls-2, dark-souls-3, autohotkey, matplotlib, hashids, wagtail, elixir, osmon, phoenix, erlang, os-monitoring,
forms, django-crispy-forms, django-widget-tweaks, ecto, queries, declarative, form, php, select2,
autocompelte, ajax, android, kotlin, adapter, filter, async, tasks, django-rq, rq, du, unix, linux, disk-usage,
cbv, middleware, class-based-views, react, redux, hyperapp, immutable, es6, youtube, youtube-dl, ffmpeg,
django-rest-auth, python-2, python-3, plpgsql, component, ag-grid, grid, bash, cron, pandas, scipy, numpy,
pivot, pivot_table, ipython, jupyter, notebook, query, q, imgur, console, research, debug, werkzeug, django-
extensions, 404, error, spring, spring-boot, java, ldap, profiles, settings, properties, yaml, configuration,
deploy, init.d, react-redux, redux-thunk, redux-form, react-router, react-router-redux, react-notification,
history, babel, babelify, browserify, watchify, uglify, boilerplate, tutorial, introduction, fixed-data-tables,
FixedDataTable, reportlab, xhtml2pdf, node, npm, generic, tables, flux, jobs, asynchronous, scheduling,
redis, pusher, auditing, css, design, boostrap-material-design, less, node.js, gmail, security, google, flask,
mongodb, heroku, spring-security, git, github, branching, static-html, github.io, pelican, github-pages, rst
Yearly archives
2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021, 2022, 2023
GitHub Repos
cookiecutter-django-starter
cpu-benchmark-v9
cpu-benchmark
aoc2018-clj
aoc2019-elixir
@spapas on GitHub
Follow @_serafeim_
https://spapas.github.io/2021/01/07/django-hashids/ 7/7