yunojuno / django-charid-field

Provides a prefixable, string-based ID field for your Django models. Supports cuid, ksuid, ulid & more.
MIT License
33 stars 4 forks source link

Initial commit #1

Closed djm closed 3 years ago

djm commented 3 years ago

Summary

Introduce django-cuidfield, a Django ModelField that allows easy handling & persistence of cuids for entity ID or other purposes.

Why?

So that we can have a global ID system for database entities @ YunoJuno to aid in looking up/discovery of entities, and a future-proof ID system to scale with.

Incremental Integer IDs

Example: 127192

Upsides: fast for database lookups due to monotonically increasing number, easy to understand; generally short but each character can only represent 1-9.

Downsides: guessable and thus iterable; reveal quantity of entities; not collision resistant, require the database sequencer to generate them thus cannot be generated client side; if presented alone, hard to tell what entity it represents.

UUIDs

Example: af4876bf-7e6d-4b48-8a0d-813c6d3087e1

Upsides: unguessable, can be generated client side, collision resistant to an extent.

Downsides: ugly in general & especially in URLs; they come in hyphenated and not-hyphenated versions so you end up having to support both; if presented alone, still hard to tell what entity it represents; collision resistance can fall apart when generating many per ms across many devices; not monotonically increasing and thus are bad as high-performance primary keys (cannot rely on binary search); long.

CUIDs

Example: ckoydyjiv000001jndl1mgtta

See the README for a longer description but..

Upsides: unlikely to be guessed; collision resistant across many devices; client side generation possible with tiny bundle; built for speedy generation; monotonically incrementing via the timestamp segment - so binary search works for fast lookups at db level.

Downsides: does not hide the order of ID generation; can reveal precise time something was created.

Prefixed CUIDs

What this library provides.

Example: cus_ckoydyjiv000001jndl1mgtta

All the benefits of the above, but when provided with the ID alone - you can also tell its type. With this known you can do lots of fun stuff like making an quick-lookup input where you can dump an ID and automatically be sent to the right location. It's also incredibly handy in logs and such because you no longer need to include what the entity is - the ID handles that for you.

djm commented 3 years ago

Planned usage in YJ in something like this..

# base model file

def get_model_prefix(model_class, field_instance, field_name):
    return model_class.PREFIX

class AbstractBaseModel():

    PREFIX: str

    public_id = CuidField(
        prefix=get_model_prefix,
        help_text="The public ID for this model"
    )

# actual model file

class CustomerModel(AbstractBaseModel):

    PREFIX = "cus_"