Skip to content

Conversation

@alvaroMaleno
Copy link

@alvaroMaleno alvaroMaleno commented Dec 1, 2025

Description

Title: Add deterministic, reversible UUID helper for model IDs

This PR introduces a small utility that generates deterministic UUIDs from a numeric model ID plus the model name, while allowing the original numeric ID to be recovered on the server side.

What is the idea

The intent of this PR is to solve the public UUIDs requirement with no changes neither, on DB or in the models. We can calculate fixed UUIDs and manage them in execution time. By the generation of deterministic UUIDs as output from a function, being capable of revert their state and to recover the original, the application is able to add a calculated attribute called "public_id" that differs substantially from the DB numeric ID. Furthermore, it generates the possibility of - by reverting the UUID to its original form - communicate the external inputs with the internal DB.

Rationale

For this project we need UUIDs that:

  • Are deterministic for a given (numeric_id, model_name) pair.
  • Can be mapped back to the original numeric ID.
  • Do not expose the numeric ID directly in the UUID string, even when printed or logged.

By encrypting a single 16‑byte block with AES and treating the ciphertext as a UUID, we get a reversible mapping controlled by a secret key that lives in configuration rather than in the database schema.

Notes

  • Only the numeric ID is reversible. The model name is used via a hash to diversify UUIDs between models and cannot be reconstructed from the UUID.
  • Changing UUID_AES_KEY will change the mapping for all existing UUIDs, so deployments should treat this key similarly to SECRET_KEY and keep it stable.

A simple demostration

image

Checklist

  • Ran the Black Formatter and
    djLint-er on any new code
  • Made any changes or additions to the documentation where required
  • Changes generate no new warnings/errors
  • New and existing unit tests pass locally with my
    changes

What type of PR is this?

  • ✨ Feature
  • 🐛 Bug Fix
  • 🚨 Breaking Change
  • ♻️ Code Refactor
  • 📝 Documentation Update

Added/updated tests?

  • 👍 yes
  • 🙅 no, because they aren't needed
  • 🙋 no, because I need help

Related PRs, Issues etc

@TreyWW
Copy link
Owner

TreyWW commented Dec 1, 2025

Hey @alvaroMaleno really appreciate your efforts here. Just wondering though how this differs from adding an extra UUID field to the DB models?

if it’s not reversible anyways to get the model from uuid there doesn’t seem to be a difference.

Instead of decrypting we’d just do a database lookup by uuid.

Thanks

@alvaroMaleno
Copy link
Author

alvaroMaleno commented Dec 1, 2025

Hello @TreyWW .

The idea here is to add an extra layer. When an entity is retrieved from db a new calculated field public_id or somewhat would be assigned with the get_uuid method in execution time. Parameters would be: id, name_of_the_model.

Later, when the client request via GET or any operation that entity with the public id, in execution time the app would be able to recover the numeric id merely callig to the method revert. Any other operation would remain unchanged in the subsequent steps.

Hey @alvaroMaleno really appreciate your efforts here. Just wondering though how this differs from adding an extra UUID field to the DB models?

if it’s not reversible anyways to get the model from uuid there doesn’t seem to be a difference.

Instead of decrypting we’d just do a database lookup by uuid.

Thanks

@alvaroMaleno
Copy link
Author

alvaroMaleno commented Dec 2, 2025

Perhaps an example could be much clarifier than texting @TreyWW
In Django framework would be something like

@property
def resource_id(self)
    return f'prefix_{secure_uuid.get_uuid(model.pk, model.name)}'
    
@property
def id_from_uuid(self, resource_id)
    return secure_uuid.revert_uuid(resource_id.replace(prefix_, ""))

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants