tortoise / tortoise/tortoise-orm
FastAPI and Foreing Keys not working even after early init
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
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- 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