Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Hinting ModelSchema about custom fields #704

Open
jleclanche opened this issue Mar 16, 2023 · 1 comment
Open

Hinting ModelSchema about custom fields #704

jleclanche opened this issue Mar 16, 2023 · 1 comment

Comments

@jleclanche
Copy link

I use prefixed UUIDv7 as ID fields in my database. The field looks like this:

from django.db.models import UUIDField
from uuid6 import uuid7
from uuid import UUID
from base58 import b58encode, b58decode


class PrefixedIdentityField(UUIDField):
    def __init__(self, prefix: str, *args, **kwargs):
        self.prefix = prefix
        kwargs.setdefault("default", uuid7)
        kwargs.setdefault("primary_key", True)
        kwargs.setdefault("editable", False)
        super().__init__(*args, **kwargs)

    def deconstruct(self):
        name, path, args, kwargs = super().deconstruct()
        del kwargs["default"]
        kwargs["prefix"] = self.prefix
        return name, path, args, kwargs

    def to_python(self, value: UUID | str) -> UUID:
        if isinstance(value, UUID):
            return value
        value = value.rpartition("_")[-1]
        return UUID(bytes=b58decode(value))

    def from_db_value(self, value: UUID | str, expression, connection) -> str:
        if isinstance(value, str):
            value = UUID(value)
        return self.prefix + b58encode(value.bytes).decode()

UUIDv7 is great. It's lexicographically sortable, and the prefix means I can distinguish one ID from another and have a created timestamp without storing extra information in the db. So the idea is that the API returns IDs such as org_BwFJbmSZpBC3YZXUm3jjG, instead of 0186de4c-f634-7baa-9a1f-d77dbe2296eb.

But when I give a model with that field to python, I get this pydantic error:

1 validation error for TokenSchema
user -> organizations -> 0 -> id
  value is not a valid uuid (type=type_error.uuid)
Traceback (most recent call last):
  File "/home/adys/.cache/pypoetry/virtualenvs/financica-AU4l5g1E-py3.10/lib/python3.10/site-packages/ninja/operation.py", line 104, in run
    result = self.view_func(request, **values)
  File "/home/adys/src/financica/backend/financica/api/auth.py", line 31, in obtain_token
    return TokenSchema(token=token.secret, user=token.user)
  File "pydantic/main.py", line 341, in pydantic.main.BaseModel.__init__
pydantic.error_wrappers.ValidationError: 1 validation error for TokenSchema
user -> organizations -> 0 -> id
  value is not a valid uuid (type=type_error.uuid)
1 validation error for TokenSchema
user -> organizations -> 0 -> id
  value is not a valid uuid (type=type_error.uuid)
Traceback (most recent call last):
  File "/home/adys/.cache/pypoetry/virtualenvs/financica-AU4l5g1E-py3.10/lib/python3.10/site-packages/ninja/operation.py", line 104, in run
    result = self.view_func(request, **values)
  File "/home/adys/src/financica/backend/financica/api/auth.py", line 31, in obtain_token
    return TokenSchema(token=token.secret, user=token.user)
  File "pydantic/main.py", line 341, in pydantic.main.BaseModel.__init__
pydantic.error_wrappers.ValidationError: 1 validation error for TokenSchema
user -> organizations -> 0 -> id
  value is not a valid uuid (type=type_error.uuid)

Ideally, Django Ninja should be able to tell what the actual type should be, thanks to the from_db_value type hint. Failing that, is there an easier way to hint at ModelSchema what the type is supposed to be, that doesn't involve adding a custom field everywhere it's missing?

@vitalik
Copy link
Owner

vitalik commented Mar 17, 2023

@jleclanche

ok, will think about it - PRs/hints are welcome :)

BTW for now you can override TYPES with your own types fallbackes - see example here #694 (comment)

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

No branches or pull requests

2 participants