Rakhmatulin
Rakhmatulin

Reputation: 21

How to Exclude a Model Field and Include a Computed Attribute in Tortoise ORM Pydantic Schema?

I'm working with Tortoise ORM in a FastAPI project and have a Channel model which optionally includes a password hash when channel is protected. I want to generate a list of channels for API responses, where I exclude the password_hash field and include a new computed boolean field, protected, to indicate whether a channel is password protected.

Here's the model definition:

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

class Channel(models.Model):
    id = fields.IntField(pk=True)
    name = fields.CharField(max_length=100)
    password_hash = fields.CharField(max_length=128, null=True)
    owner = fields.ForeignKeyField('models.User', related_name='channels')

ChannelSchema = pydantic_model_creator(Channel, name="Channel")

Current API response example:

[
  {
    "id": 1,
    "name": "my channel",
    "password_hash": null
  },
  {
    "id": 2,
    "name": "my protected channel",
    "password_hash": "$2b$..."
  }
]

Desired API response:

[
  {
    "id": 1,
    "name": "my channel",
    "protected": false
  },
  {
    "id": 2,
    "name": "my protected channel",
    "protected": true
  }
]

How can I correctly include the protected computed attribute in my Pydantic model while excluding the password_hash field?

I attempted to create a computed attribute password_protected and exclude password_hash like so:

class Channel(models.Model):
    ...
    
    def password_protected(self) -> bool:
        return bool(self.password_hash)

    class PydanticMeta:
        computed = ["password_protected"]
        # exclude = ["password_hash"]

However, excluding password_hash leads to an error:

AttributeError: 'ResponseChannelSchema' object has no attribute 'password_hash'

Upvotes: 1

Views: 425

Answers (1)

Rakhmatulin
Rakhmatulin

Reputation: 21

Here's how I solved this problem, using the serialize_password method for serialization, and serialization_alias.

class ResponseChannelSchema(ChannelSchema):
    password_hash: str | None = Field(None, serialization_alias="protected")

    @field_serializer('password_hash')
    def serialize_password(self, v: str) -> bool:
        return bool(v)

Upvotes: 1

Related Questions