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

Add logic to handle globalns and localns in pydantic_model_creator #1876

Open
wants to merge 2 commits into
base: develop
Choose a base branch
from

Conversation

alistairmaclean
Copy link

Description

When using typing.get_type_hints in Python 3.12, the get_type_hints method will not evaluate forward references unless the referenced objects are part of the objects module scope, or the references are specified in the globalns/localns argument of get_type_hints.

Motivation and Context

When the models are not in the same module, forward references can't be evaluated.

My project structure resembles:

# models/__init__.py
from .event import Event
from .tournament import Tournament

__all__ = [
    Event,
    Tournament,
]
# models/event.py
from tortoise import Model, fields

from .tournament import Tournament


class Event(Model):
    id = fields.IntField(primary_key=True)
    name = fields.TextField()
    tournament: fields.ForeignKeyRelation[Tournament] = fields.ForeignKeyField(
        "models.Tournament", related_name="events"
    )

    def __str__(self):
        return self.name
# models/tournament.py
from tortoise import Model, fields


class Tournament(Model):
    id = fields.IntField(primary_key=True)
    name = fields.TextField()

    events: fields.ReverseRelation["Event"]

    def __str__(self):
        return self.name
# main.py
from models import Tournament

from tortoise.contrib.pydantic.creator import pydantic_model_creator

if __name__ == "__main__":
    a = pydantic_model_creator(Tournament)

Output:

NameError: name 'Event' is not defined

As a temporary fix, I've added this logic in models/__init__.py, which is not ideal:

for m in __all__:
    m.__module__ = __name__

I propose a solution where users specify the globalns for typing.get_type_hints:

# main.py
import models

from tortoise.contrib.pydantic.creator import pydantic_model_creator


globalns = {
    "Tournament": models.Tournament,
    "Event": models.Event,
}

if __name__ == "__main__":
    a = pydantic_model_creator(models.Tournament, globalns=globalns)

I might be missing something, or maybe there's a more elegant solution. I'm open to discussion and feedback.

How Has This Been Tested?

I have been able to generate pydantic models by specifying globalns Dict[str, Type[Model]] when using pydantic_model_creator of my fork.

Checklist:

  • My code follows the code style of this project.
  • My change requires a change to the documentation.
  • I have updated the documentation accordingly.
  • I have added the changelog accordingly.
  • I have read the CONTRIBUTING document.
  • I have added tests to cover my changes.
  • All new and existing tests passed.

@alistairmaclean alistairmaclean force-pushed the fix/pydantic-creator-use-explicit-ns branch from 907e457 to 8214c73 Compare February 6, 2025 12:38
@alistairmaclean alistairmaclean force-pushed the fix/pydantic-creator-use-explicit-ns branch from 8214c73 to f47dec6 Compare February 6, 2025 13:07
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.

1 participant