tortoise / tortoise-orm

Familiar asyncio ORM for python, built with relations in mind
https://tortoise.github.io
Apache License 2.0
4.66k stars 389 forks source link

user not in response #444

Open jet10000 opened 4 years ago

jet10000 commented 4 years ago
class User(models.Model):
    name = fields.CharField(max_length=100)
    email = fields.CharField(max_length=50)
    recipes: fields.ReverseRelation["Recipe"]

class Recipe(models.Model):
    name = fields.CharField(max_length=100)
    content = fields.TextField()
    user = fields.ForeignKeyField("models.User")

Recipe_Pydantic = pydantic_model_creator(Recipe, name='Recipe')
RecipeIn_Pydantic = pydantic_model_creator(Recipe, name="RecipeIn", exclude_readonly=True)
@app.get("/recipes", response_model=List[Recipe_Pydantic])
async def get_recipes():
    return await Recipe_Pydantic.from_queryset(Recipe.all().prefetch_related('user'))
lsabi commented 4 years ago

I've encountered the same problem when using it with fastapi.

To me it looks like a pydantic problem: since we are not declaring the relation in the pydantic model, it gets excluded.

If I manage to find a solution, I'll post it.

long2ice commented 4 years ago

see https://tortoise-orm.readthedocs.io/en/latest/contrib/pydantic.html#relations-early-init

lsabi commented 3 years ago

I've seen that, but it doesn't work. When using fastapi, how's one supposed to do?

Taking from the example https://tortoise-orm.readthedocs.io/en/latest/examples/fastapi.html I have the models in a file together with the

User_Pydantic = pydantic_model_creator(Users, name="User")
UserIn_Pydantic = pydantic_model_creator(Users, name="UserIn", exclude_readonly=True)

pydantic delcarations.

In my endpoint, if I run await User_Pydantic.from_queryset_single(Users.get(id=user_id)prefetch_related("my_relation")), only returns the fields of the User model.

On the other hand, when I only use await Users.get(id=user_id)prefetch_related("my_relation") , I can see a field representing the relation. Basically, it is lost when running User_Pydantic.from_queryset_single.

long2ice commented 3 years ago

You should call init_models before get pydantic_model_creator

lsabi commented 3 years ago

Isn't init_models called by register_tortoise when the connection is initialized?

I'm not understanding where to place it. Before the database initialization and the routers registration does not work.

Is there a complete example with fastapi and complex relations?

long2ice commented 3 years ago

Yes, but it's called on app startup, your pydantic_model_creator is after models module import, which is before of register_tortoise

lsabi commented 3 years ago

I managed to make it work as you say.

Though to me it does not look great. I have to import within a function, which is not ideal in my opinion..

If there are better modules organizations, let me know.

In the meantime, thanks.

maou-shonen commented 3 years ago

I have the same problem Here is my solution

class User(models.Model):
    name = fields.CharField(max_length=100)
    email = fields.CharField(max_length=50)
    recipes: fields.ReverseRelation["Recipe"]

class Recipe(models.Model):
    name = fields.CharField(max_length=100)
    content = fields.TextField()
    user = fields.ForeignKeyField("models.User")

User_Pydantic = pydantic_model_creator(Users, name="User")
UserIn_Pydantic = pydantic_model_creator(Users, name="UserIn", exclude_readonly=True)

Recipe_Pydantic_Base = pydantic_model_creator(Recipe, name='Recipe')
RecipeIn_Pydantic = pydantic_model_creator(Recipe, name="RecipeIn", exclude_readonly=True)

class Recipe_Pydantic(Recipe_Pydantic_Base):
    user: User_Pydantic = Field(...)
lsabi commented 3 years ago

@mao-shonen it doesn't look very clean to me (it's a personal view).

I ended up splitting orm models and pydantic models into two separate folders, namely "orm" and "schema". Then, at initialization phase, right after the Fastapi instance creation, the models are initialized and then the connection is performed.

maou-shonen commented 3 years ago

I have re-read the document today I'm sorry, my previous answer was not good This is the correct one

https://tortoise-orm.readthedocs.io/en/latest/examples/pydantic.html#early-model-init

titogarrido commented 3 years ago

I have the same problem Here is my solution

class User(models.Model):
    name = fields.CharField(max_length=100)
    email = fields.CharField(max_length=50)
    recipes: fields.ReverseRelation["Recipe"]

class Recipe(models.Model):
    name = fields.CharField(max_length=100)
    content = fields.TextField()
    user = fields.ForeignKeyField("models.User")

User_Pydantic = pydantic_model_creator(Users, name="User")
UserIn_Pydantic = pydantic_model_creator(Users, name="UserIn", exclude_readonly=True)

Recipe_Pydantic_Base = pydantic_model_creator(Recipe, name='Recipe')
RecipeIn_Pydantic = pydantic_model_creator(Recipe, name="RecipeIn", exclude_readonly=True)

class Recipe_Pydantic(Recipe_Pydantic_Base):
    user: User_Pydantic = Field(...)

Hi! Where this "Field(...)" is imported from?

Thanks

lsabi commented 3 years ago

@titogarrido

It is imported from Pydantic

See https://pydantic-docs.helpmanual.io/usage/types/#constrained-types