tortoise / tortoise/tortoise-orm

FastAPI and Foreing Keys not working even after early init

Open
#604 2 comments 3 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Python
Stars
5.6k
Forks
516
Avg merge
2d 21h
Merged PRs (30d)
9

Description

Describe the bug
I am not able to see relationship fields on fastapi swagger.

To Reproduce
models.py

from tortoise import fields, Tortoise
from tortoise.models import Model
from tortoise.contrib.pydantic import pydantic_model_creator


class Users(Model):
    """
    The User model
    """

    id = fields.IntField(pk=True)
    external_id = fields.CharField(max_length=256, unique=True)
    email = fields.CharField(max_length=128, unique=True)
    username = fields.CharField(max_length=128, unique=True)
    first_name = fields.CharField(max_length=256, null=True)
    last_name = fields.CharField(max_length=256, null=True)
    password = fields.CharField(max_length=128, null=True)
    enabled = fields.BooleanField(default=True)
    projects: fields.ReverseRelation["Projects"]
    created_at = fields.DatetimeField(auto_now_add=True)
    modified_at = fields.DatetimeField(auto_now=True)


class Projects(Model):
    id = fields.IntField(pk=True)
    title = fields.CharField(max_length=256, unique=True)
    description = fields.TextField(),
    size = fields.CharField(max_length=128)
    user = fields.ForeignKeyField(
        "models.Users", related_name="projects"
    )
    cover = fields.CharField(max_length=256)
    vote_count = fields.IntField(default=0)
    level = fields.IntField(default=0)
    featured = fields.BooleanField(default=False)
    views = fields.IntField(default = 0)
    images: fields.ReverseRelation["Images"]
    votes: fields.ReverseRelation["Votes"]
    created_at = fields.DatetimeField(auto_now_add=True)
    modified_at = fields.DatetimeField(auto_now=True)

class Images(Model):
    id = fields.IntField(pk=True)
    image_path = fields.CharField(max_length=256)
    project: fields.ForeignKeyRelation[Projects] = fields.ForeignKeyField(
        "models.Projects", related_name="images"
    )
    created_at = fields.DatetimeField(auto_now_add=True)
    modified_at = fields.DatetimeField(auto_now=True)

class Votes(Model):
    id = fields.IntField(pk=True)
    username = fields.CharField(max_length=128)
    project: fields.ForeignKeyRelation[Projects] = fields.ForeignKeyField(
        "models.Projects", related_name="votes"
    )
    created_at = fields.DatetimeField(auto_now_add=True)
    modified_at = fields.DatetimeField(auto_now=True)

Tortoise.init_models(["__main__"], "models")

User_Pydantic = pydantic_model_creator(Users, name="User", exclude = ["password"])
UserIn_Pydantic = pydantic_model_creator(Users, name="UserIn", exclude= ["external_id"], exclude_readonly=True)
UserUpdate_Pydantic = pydantic_model_creator(Users, name="UserUpdate", exclude_readonly=True)
UserUpdatePass_Pydantic = pydantic_model_creator(Users, name="UserUpdatePass_Pydantic", exclude=["username", "email"], exclude_readonly=True)

Project_Pydantic = pydantic_model_creator(Projects, name="Projects", exclude=['images', 'votes'])
ProjectIn_Pydantic = pydantic_model_creator(Projects, name="ProjectsIn", exclude=['images', 'votes'], exclude_readonly=True)
ProjectUpdate_Pydantic = pydantic_model_creator(Projects, name="ProjectsUpdate", exclude=['images', 'votes'], exclude_readonly=True)

Images_Pydantic = pydantic_model_creator(Images, name="Images")
ImagesIn_Pydantic = pydantic_model_creator(Images, name="ImagesIn", exclude_readonly=True)

Votes_Pydantic = pydantic_model_creator(Votes, name="Votes")
VotesIn_Pydantic = pydantic_model_creator(Votes, name="VotesIn", exclude_readonly=True)

Expected behavior
Using schema_json(indent=4) I am able to see the relations:
To reproduce:

from tortoise.contrib.pydantic import pydantic_model_creator
from models import Users, Projects, Images, Votes
from tortoise import fields, Tortoise

Tortoise.init_models(["__main__"], "models")
Users_Pydantic = pydantic_model_creator(Users)
print(Users_Pydantic.schema_json(indent=4))

Result:

>>> print(Users_Pydantic.schema_json(indent=4))
{
    "title": "Users",
    "description": "The User model",
    "type": "object",
    "properties": {
        "id": {
            "title": "Id",
            "minimum": 1,
            "maximum": 2147483647,
            "type": "integer"
        },
        "external_id": {
            "title": "External Id",
            "maxLength": 256,
            "type": "string"
        },
        "email": {
            "title": "Email",
            "maxLength": 128,
            "type": "string"
        },
        "username": {
            "title": "Username",
            "maxLength": 128,
            "type": "string"
        },
        "first_name": {
            "title": "First Name",
            "maxLength": 256,
            "nullable": true,
            "type": "string"
        },
        "last_name": {
            "title": "Last Name",
            "maxLength": 256,
            "nullable": true,
            "type": "string"
        },
        "password": {
            "title": "Password",
            "maxLength": 128,
            "nullable": true,
            "type": "string"
        },
        "enabled": {
            "title": "Enabled",
            "default": true,
            "type": "boolean"
        },
        "created_at": {
            "title": "Created At",
            "readOnly": true,
            "type": "string",
            "format": "date-time"
        },
        "modified_at": {
            "title": "Modified At",
            "readOnly": true,
            "type": "string",
            "format": "date-time"
        },
        "projects": {
            "title": "Projects",
            "type": "array",
            "items": {
                "$ref": "#/definitions/models.Projects.cbyrw7"
            }
        }
    },
    "required": [
        "id",
        "external_id",
        "email",
        "username",
        "created_at",
        "modified_at",
        "projects"
    ],
    "additionalProperties": false,
    "definitions": {
        "models.Images.leaf": {
            "title": "Images",
            "type": "object",
            "properties": {
                "id": {
                    "title": "Id",
                    "minimum": 1,
                    "maximum": 2147483647,
                    "type": "integer"
                },
                "image_path": {
                    "title": "Image Path",
                    "maxLength": 256,
                    "type": "string"
                },
                "created_at": {
                    "title": "Created At",
                    "readOnly": true,
                    "type": "string",
                    "format": "date-time"
                },
                "modified_at": {
                    "title": "Modified At",
                    "readOnly": true,
                    "type": "string",
                    "format": "date-time"
                }
            },
            "required": [
                "id",
                "image_path",
                "created_at",
                "modified_at"
            ],
            "additionalProperties": false
        },
        "models.Votes.leaf": {
            "title": "Votes",
            "type": "object",
            "properties": {
                "id": {
                    "title": "Id",
                    "minimum": 1,
                    "maximum": 2147483647,
                    "type": "integer"
                },
                "username": {
                    "title": "Username",
                    "maxLength": 128,
                    "type": "string"
                },
                "created_at": {
                    "title": "Created At",
                    "readOnly": true,
                    "type": "string",
                    "format": "date-time"
                },
                "modified_at": {
                    "title": "Modified At",
                    "readOnly": true,
                    "type": "string",
                    "format": "date-time"
                }
            },
            "required": [
                "id",
                "username",
                "created_at",
                "modified_at"
            ],
            "additionalProperties": false
        },
        "models.Projects.cbyrw7": {
            "title": "Projects",
            "type": "object",
            "properties": {
                "id": {
                    "title": "Id",
                    "minimum": 1,
                    "maximum": 2147483647,
                    "type": "integer"
                },
                "title": {
                    "title": "Title",
                    "maxLength": 256,
                    "type": "string"
                },
                "size": {
                    "title": "Size",
                    "maxLength": 128,
                    "type": "string"
                },
                "cover": {
                    "title": "Cover",
                    "maxLength": 256,
                    "type": "string"
                },
                "vote_count": {
                    "title": "Vote Count",
                    "default": 0,
                    "minimum": -2147483648,
                    "maximum": 2147483647,
                    "type": "integer"
                },
                "level": {
                    "title": "Level",
                    "default": 0,
                    "minimum": -2147483648,
                    "maximum": 2147483647,
                    "type": "integer"
                },
                "featured": {
                    "title": "Featured",
                    "default": false,
                    "type": "boolean"
                },
                "views": {
                    "title": "Views",
                    "default": 0,
                    "minimum": -2147483648,
                    "maximum": 2147483647,
                    "type": "integer"
                },
                "created_at": {
                    "title": "Created At",
                    "readOnly": true,
                    "type": "string",
                    "format": "date-time"
                },
                "modified_at": {
                    "title": "Modified At",
                    "readOnly": true,
                    "type": "string",
                    "format": "date-time"
                },
                "images": {
                    "title": "Images",
                    "type": "array",
                    "items": {
                        "$ref": "#/definitions/models.Images.leaf"
                    }
                },
                "votes": {
                    "title": "Votes",
                    "type": "array",
                    "items": {
                        "$ref": "#/definitions/models.Votes.leaf"
                    }
                }
            },
            "required": [
                "id",
                "title",
                "size",
                "cover",
                "created_at",
                "modified_at",
                "images",
                "votes"
            ],
            "additionalProperties": false
        }
    }
}

But from swagget, using a GET method I am receiving:
get_users:

[
  {
    "id": 1,
    "external_id": "750ade6c-2f0b-4ced-b126-51975bfc4995",
    "email": "user@gmail.com",
    "username": "user",
    "first_name": "name",
    "last_name": "lastname",
    "enabled": true,
    "created_at": "2021-01-05T16:05:47.072786+00:00",
    "modified_at": "2021-01-05T16:05:47.072843+00:00"
  }
]

get_projects:

[
  {
    "id": 1,
    "title": "string",
    "size": "string",
    "cover": "string",
    "vote_count": 0,
    "level": 0,
    "featured": false,
    "views": 0,
    "created_at": "2021-01-05T16:50:59.062537+00:00",
    "modified_at": "2021-01-05T16:50:59.062589+00:00"
  }
]

Additional context
This is my get function:

@app.get("/users", response_model=List[User_Pydantic], tags=["user"])
async def get_users():
    return await User_Pydantic.from_queryset(Users.all())
@app.get("/projects", response_model=List[Project_Pydantic], tags=["projects"])
async def get_projects(current_user: dict = Depends(get_current_user)):
    print(current_user)
    return await Project_Pydantic.from_queryset(Projects.filter(user_id=current_user.id).all())

Any clue what I am doing wrong?

thanks!

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the models.py example, the pydantic_model_creator calls, and the FastAPI GET endpoints that produce the Swagger schemas. Compare schema_json(indent=4) with the generated Swagger response schemas; done means the relationship fields shown in the Pydantic schema also appear in the Swagger GET responses.

Written by the indexing model from the issue text.

Assessment

Tech stack
fastapi, python
Domain
api, backend
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
42/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.