Skip to main content

A Hashids obfuscated Django Model Field

Project description

https://travis-ci.org/nshafer/django-hashid-field.svg?branch=master

Django Hashid Field

A custom Model Field that uses the Hashids library to obfuscate an IntegerField or AutoField. It can be used in new models or dropped in place of an existing IntegerField, explicit AutoField, or an automatically generated AutoField.

Features

  • Stores IDs as integers in the database

  • Allows lookups and filtering by either integer, hashid string or Hashid object

  • Can disable integer lookups

  • Can be used as sort key

  • Can drop-in replace an existing IntegerField (HashidField) or AutoField (HashidAutoField)

  • Allows specifying a salt globally

  • Supports custom salt, min_length and alphabet settings per field

  • Supports Django REST Framework Serializers

Requirements

This module is tested and known to work with:

  • Python 2.7, 3.5

  • Django 1.8, 1.9, 1.10, 1.11

  • Hashids 1.1

  • Django REST Framework 3.5

Installation

Install the package (preferably in a virtualenv):

$ pip install django-hashid-field

Configure a global SALT for all HashidFields to use by default in your settings.py.

HASHID_FIELD_SALT = "a long and secure salt value that is not the same as SECRET_KEY"
# Note: You can generate a secure key with:
#     from django.core.management.utils import get_random_secret_key; print(get_random_secret_key())

Add it to your model

from hashid_field import HashidField

class Book(models.Model):
    reference_id = HashidField()

Migrate your database

$ ./manage.py makemigrations
$ ./manage.py migrate

Upgrading

Please see the Change Log

Basic Usage

Use your field like you would any other, for the most part. You can assign integers:

>>> b = Book()
>>> b.reference_id = 123
>>> b.reference_id
Hashid(123): OwLxW8D

You can assign valid hashids. It’s valid only if it can be decoded into an integer based on your salt:

>>> b.reference_id = 'r8636LO'
>>> b.reference_id
Hashid(456): r8636LO

You can access your field with either integers, hashid strings or Hashid objects:

>>> Book.objects.filter(reference_id=123)
<QuerySet [<Book:  (OwLxW8D)>]>
>>> Book.objects.filter(reference_id='OwLxW8D')
<QuerySet [<Book:  (OwLxW8D)>]>
>>> b = Book.objects.get(reference_id='OwLxW8D')
>>> b
<Book:  (OwLxW8D)>
>>> h = b.reference_id
>>> h
Hashid(123): OwLxW8D
>>> Book.objects.filter(reference_id=h)
<Book:  (OwLxW8D)>

The objects returned from a HashidField are an instance of the class Hashid, and allow basic access to the original integer or the hashid:

>>> from hashid_field import Hashid
>>> h = Hashid(123)
>>> h.id
123
>>> h.hashid
'Mj3'
>>> print(h)
Mj3
>>> repr(h)
'Hashid(123): Mj3'

Hashid Auto Field

Along with HashidField there is also a HashidAutoField that works in the same way, but that auto-increments just like an AutoField.

from hashid_field import HashidAutoField

class Book(models.Model):
    serial_id = HashidAutoField(primary_key=True)

The only difference is that if you don’t assign a value to it when you save, it will auto-generate a value from your database, just as an AutoField would do. Please note that HashidAutoField inherits from AutoField and there can only be one AutoField on a model at a time.

>>> b = Book()
>>> b.save()
>>> b.serial_id
Hashid(1): AJEM7LK

It can be dropped into an existing model that has an auto-created AutoField (all models do by default) as long as you give it the same name and set primary_key=True. So if you have this model:

class Author(models.Model):
    name = models.CharField(max_length=40)

Then Django has created a field for you called ‘id’ automatically. We just need to override that by specifying our own field with primary_key set to True.

class Author(models.Model):
    id = HashidAutoField(primary_key=True)
    name = models.CharField(max_length=40)

And now you can use the ‘id’ or ‘pk’ attributes on your model instances:

>>> a = Author.objects.create(name="John Doe")
>>> a.id
Hashid(60): N8VNa8z
>>> Author.objects.get(pk='N8VNa8z')
<Author: Author object>

Settings

HASHID_FIELD_SALT

You can optionally set a global Salt to be used by all HashFields and HashidAutoFields in your project, or set the salt on each individual field. Please note that changing this value will cause all HashidFields to change their values, and any previously published IDs will become invalid.

Type:

string

Default:

“”

Example:
HASHID_FIELD_SALT = "a long and secure salt value that is not the same as SECRET_KEY"

HASHID_FIELD_ALLOW_INT

Global setting on whether or not to allow lookups or fetches of fields using the underlying integer that’s stored in the database. Enabled by default for backwards-compatibility. You can enable this to prevent users from being to do a sequential scan of objects by pulling objects by integers (1, 2, 3) instead of Hashid strings (“Ba9p1AG”, “7V9gk9Z”, “wro12zm”).

Type:

boolean

Default:

True

Example:
HASHID_FIELD_ALLOW_INT = False

Field Parameters

Besides the standard field options, there are 3 settings you can tweak that are specific to HashidField and AutoHashidField.

Please note that changing any of these values will affect the obfuscation of the integers that are stored in the database, and will change what are considered “valid” hashids. If you have links or URLs that include your HashidField values, then they will stop working after changing any of these values. It’s highly advised that you don’t change any of these settings once you publish any references to your field.

salt

Type:

string

Default:

settings.HASHID_FIELD_SALT, “”

Example:
reference_id = HashidField(salt="Some salt value")

min_length

Type:

int

Default:

7

Note:

This defaults to 7 for the field since the maximum IntegerField value can be encoded in 7 characters with the default alphabet setting of 62 characters.

Example:
reference_id = HashidField(min_length=15)

alphabet

Type:

string of characters (16 minimum)

Default:

Hashids.ALPHABET, which is “abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890”

Example:
# Only use numbers and lower-case letters
reference_id = HashidField(alphabet="0123456789abcdefghijklmnopqrstuvwxyz")

allow_int

Type:

boolean

Default:

settings.HASHID_FIELD_ALLOW_INT, True

Example:
reference_id = HashidField(allow_int=False)

Hashid Class

Operations with a HashidField or HashidAutoField return a Hashid object. This simple class does the heavy lifting of converting integers and hashid strings back and forth. There shouldn’t be any need to instantiate these manually.

Methods

__init__(id, salt=’’, min_length=0, alphabet=Hashids.ALPHABET):
id:

REQUIRED Integer you wish to encode

salt:

Salt to use. Default: ‘’

min_length:

Minimum length of encoded hashid string. Default: 0

alphabet:

The characters to use in the encoded hashid string. Default: Hashids.ALPHABET

set(id)
id:

Integer you with to encode

Instance Variables

id
type:

Int

value:

The decoded integer

hashid
type:

String

value:

The encoded hashid string

hashids
type:

Hashids()

value:

The instance of the Hashids class that is used to encode and decode

Django REST Framework Integration

If you wish to use a HashidField or HashidAutoField with a DRF ModelSerializer, there is one extra step that you must take. Automatic declaration of any Hashid*Fields will result in an ImproperlyConfigured exception being thrown. You must explicitly declare them in your Serializer, as there is no way for the generated field to know how to work with a Hashid*Field, specifically what ‘salt’, ‘min_length’ and ‘alphabet’ to use, and can lead to very difficult errors or behavior to debug, or in the worst case, corruption of your data. Here is an example:

from rest_framework import serializers
from hashid_field.rest import HashidSerializerCharField


class BookSerializer(serializers.ModelSerializer):
    reference_id = HashidSerializerCharField(source_field='library.Book.reference_id')

    class Meta:
        model = Book
        fields = ('id', 'reference_id')

The source_field allows the HashidSerializerCharField to copy the ‘salt’, ‘min_length’ and ‘alphabet’ settings from the given field at app_name.model_name.field_name so that it can be defined in just one place. Explicit settings are also possible:

reference_id = HashidSerializerCharField(salt="a different salt", min_length=10, alphabet="ABCDEFGHIJKLMNOPQRSTUVWXYZ")

If nothing is given, then the field will use the same global settings as a Hashid*Field. It is very important that the options for the serializer field matches the model field, or else strange errors or data corruption can occur.

HashidSerializerCharField will serialize the value into a Hashids string, but will deserialize either a Hashids string or integer and save it into the underlying Hashid*Field properly. There is also a HashidSerializerIntegerField that will serialize the Hashids into an un-encoded integer as well.

HashidSerializerCharField

Serialize a Hashid*Field to a Hashids string, de-serialize either a valid Hashids string or integer into a Hashid*Field.

Parameters

source_field

A 3-field dotted notation of the source field to load matching ‘salt’, ‘min_length’ and ‘alphabet’ settings from. Must be in the format of “app_name.model_name.field_name”. Example: “library.Book.reference_id”.

salt, min_length, alphabet

See Field Parameters

HashidSerializerIntegerField

Serialize a Hashid*Field to an integer, de-serialize either a valid Hashids string or integer into a Hashid*Field. See HashidSerializerCharField for parameters.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

django-hashid-field-1.2.2.tar.gz (14.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

django_hashid_field-1.2.2-py2.py3-none-any.whl (15.2 kB view details)

Uploaded Python 2Python 3

File details

Details for the file django-hashid-field-1.2.2.tar.gz.

File metadata

File hashes

Hashes for django-hashid-field-1.2.2.tar.gz
Algorithm Hash digest
SHA256 e2a7032efd685eca22c4c9d2dac7b09bec8efec4d4b66b5241dfeaac001d3333
MD5 fc3d38523dd5481d3fc26d096672c7fb
BLAKE2b-256 fd1d84304db28f4c1e534089da0c1e5f574548028e8e51e9ede3abcf5fcbddb8

See more details on using hashes here.

File details

Details for the file django_hashid_field-1.2.2-py2.py3-none-any.whl.

File metadata

File hashes

Hashes for django_hashid_field-1.2.2-py2.py3-none-any.whl
Algorithm Hash digest
SHA256 3fe1a7598c8b3eaed75266785da63041cb0123b6542f6b8b68543c120d81590a
MD5 b2da8c51c70fc4bbd0c2b7d3c696dac2
BLAKE2b-256 800d2da8c56ab72c24e79db258fd35d12dc2a2274d5d9c3ce906b5107dc0f072

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page