0% found this document useful (0 votes)
13 views

Using Hashids To Hide Ids of Objects in Django - Var

Django tutorial

Uploaded by

TomDijkshoornn
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
13 views

Using Hashids To Hide Ids of Objects in Django - Var

Django tutorial

Uploaded by

TomDijkshoornn
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 7

25-07-2023 08:07 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

Using hashids to hide ids of objects in Django


Thu 07 January 2021

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:

/ships/create/ To add a new object


/ships/list/ To display a list of your objects
/ships/detail/id/ To display the particular object with that id (primary key)
/ships/update/id/ To update/edit the particular object with that id (primary key)
/ships/delete/id/ To delete the particular object with that id (primary key)

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

and then in your urls.py add the line:


urlpatterns = [
# ...
path(

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:

from hashids import Hashids


hashids = Hashids()
hashid = hashids.encode(123) # 'Mj3'
ints = hashids.decode('xoz') # (456,)

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:

from hashids import Hashids


from django.conf import settings

hashids = Hashids(settings.HASHIDS_SALT, min_length=8)

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,}'

def to_python(self, value):


return h_decode(value)

def to_url(self, value):


return h_encode(value)

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:

from core.utils import HashIdConverter

register_converter(HashIdConverter, "hashid")

and then use it in your urls.py like this:


urlpatterns = [
# ...
path(
"detail/<hashid:pk>/",
login_required(views.ShipDetailView.as_view()),
name="ship_detail",
),

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

Adding a timeline of A forward and reverse A forward and reverse Adding


your wagtail Posts — proxy primer for the … proxy primer for the … list to y

3 years ago 2 years ago 2 years ago 3 years ag


Intro In this small post I’ll Before some days I’d written Before some days I’d written I think th
present a small tutorial on an answer on HN where I an answer on HN where I tool for a
how to add a timelne of … explained as simply as … explained as simply as … Wagtail s

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

G Join the discussion…

LOG IN WITH OR SIGN UP WITH DISQUS ?

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 ›

spapas Mod >HK


2 years ago edited

Hello friend, reverse is a django function. You can import it like this

from django.urls import reverse

1 0 Reply • Share ›

H
HK > spapas
2 years ago

Thank you so much!


My app works now!

0 0 Reply • Share ›

Subscribe Privacy Do Not Sell My Data

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

Simple Django - DataTables integration


AI auto-subtitling
HTML form disable after submit
Multiple storages for the same FileField in Django
Using Unpoly with Django

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

Start a django project using a cookiecutter

cpu-benchmark-v9

cpu-benchmark

aoc2018-clj

Playing with aoc2018 and clojure

aoc2019-elixir

@spapas on GitHub
Follow @_serafeim_

Copyright © 2013–2023 Serafeim Papastefanos — Powered by Pelican

https://spapas.github.io/2021/01/07/django-hashids/ 7/7

You might also like