tortoise/tortoise-orm

When querying data, for ORM entities, add fields that do not include

jiangzik opened this issue · 6 comments

For example, I want to query users, but the user password does not want to be included. I want all other fields. It is a bit cumbersome to just use only. Is there a way to exclude only the password field?

https://tortoise.github.io/examples/pydantic.html

from tortoise import Model
from tortoise.contrib.pydantic import pydantic_model_creator


class User(Model):
    ...
    class PydanticMeta:
        exclude = ["password"]

User_Pydantic = pydantic_model_creator(User)

In order to make the code more concise, I did not use pydantic, but directly returned the orm entity

Demo:

from tortoise import Model, fields, run_async
from tortoise.contrib.test import init_memory_sqlite


class User(Model):
    id = fields.IntField(pk=True)
    name = fields.CharField(max_length=10)
    password = fields.CharField(max_length=10)

    def model_dump(self) -> dict:
        data = dict(self)
        data.pop("password")
        return data


@init_memory_sqlite
async def main():
    user = await User.create(name="haha", password="123")
    print(user.model_dump())
    # {'id': 1, 'name': 'haha'}
    await User(name="hello", password="").save()
    users = await User.filter()
    print([i.model_dump() for i in users])
    # [{'name': 'haha', 'id': 1}, {'name': 'hello', 'id': 2}]


if __name__ == "__main__":
    run_async(main())

I understand that writing a method in the base class and letting other ORM entities inherit it was the earliest way to do this. In fact, from the query database, all fields are still queried. I want to fundamentally solve this problem.

How about this:

from typing import List

from tortoise import fields, Model, run_async
from tortoise.contrib.test import init_memory_sqlite


class User(Model):
    id = fields.IntField(primary_key=True)
    name = fields.CharField(max_length=20)
    password = fields.TextField()

    @classmethod
    def expose_fields(cls) -> List[str]:
        attr = "_expose_fields"
        if fs := getattr(cls, attr, None):
            return fs
        fields = [i for i in cls._meta.fields if i != "password"]
        setattr(cls, attr, fields)
        return fields


@init_memory_sqlite
async def run() -> None:
    await User(name="els", password="").save()
    users = await User.filter().values(*User.expose_fields())
    print(users)
    user = await User.get(id=1).only(*User.expose_fields())
    print({i: getattr(user, i) for i in User.expose_fields()})


if __name__ == "__main__":
    run_async(run())

Things are getting more troublesome, I'll try something else, anyway, thank you