Can I return different schemas from a Pydantic model_validator? /u/_hemafencing_ Python Education

Summary of the problem: A well-established DB table expects a range of page views. However, when developing the API, I found it to be an abysmal user experience. I plan to create a min/max field for the user to supply input, then change it so it’s in a format the DB expects.

I cannot change anything about how the DB table is structured.

My current code:

class PageViewsRangeSchema(BaseWrite): """BaseWrite just provides config to exclude extra fields """ # These constraints are not needed since this is an internal Schema, but it is good practice to have them pages_viewed_range: Annotated[str, StringConstraints(strip_whitespace=True, min_length=3, max_length=64) ] = Field() class PageViewsSchemaWrite(BaseWrite): # PageViews is an enum that is discriminated against in_type: Literal[PageViews] = Field(exclude=True) minimum_views: NonNegativeInt = Field() maximum_views: NonNegativeInt = Field() u/model_validator(mode="after") def validate_page_viewed_range(self) -> PageViewsRangeSchema: if self.minimum_views > self.maximum_views: raise ValueError("Error here.") # Cannot use a model_serializer here because it is applied and shared # amongst all Schemas. The DB expects a range of min-max, but that is a bad user experience. return PageViewsRangeSchema(pages_viewed_range=f"{self.minimum_views}-{self.maximum_views}") 

In practice, this just works. Docs look fine, I can send a min/max value through the API request and that gets translated into a range for the DB. I know the model_validator is supposed to return Self, but I cannot get this to fit my use case. If I have an extra computed field property, it doesn’t get called to insert into the DB and complains that there are extra fields. At this point, I cannot change the existing behavior of how we write these models to the DB.

However, as I’m writing unit tests with pytest I am finding this doesn’t “just work.” No matter what I do, I cannot get the unit test to return an instance of PageViewsRangeSchema like it does when actually running the code.

An example of my tests is as follows:

schema = make_target_schema( schema=PageViewsSchemaWrite, in_type=PageViews, minimum_views=1, maximum_views=5) # me trying to see if it works by dumping... It doesn't dumped_schema = schema.model_dump(by_alias=True) assert schema.pages_viewed_range == "1-5" 

Where `make_target_schema` is just a fancy wrapper for creating the Schema defined above.

I am trying to figure out if this is an acceptable workflow, or if the behavior is “undefined” leading to the difference between running it live vs unit tests. Or, if there is something I may have overlooked and should be done a completely different way.

I have tried using a model_serializer to fit this case. However, the model_serializer is shared amongst all Schemas leading to many things breaking in the process.

I would have expected the serializer to not be shared, but that is not the case and I cannot get the `model_serializer` to work with this one Schema.

For this, I would expect a different schema to be returned after the model is validated. This is true in practice, but not so during unit testing.

submitted by /u/_hemafencing_
[link] [comments]

​r/learnpython Summary of the problem: A well-established DB table expects a range of page views. However, when developing the API, I found it to be an abysmal user experience. I plan to create a min/max field for the user to supply input, then change it so it’s in a format the DB expects. I cannot change anything about how the DB table is structured. My current code: class PageViewsRangeSchema(BaseWrite): “””BaseWrite just provides config to exclude extra fields “”” # These constraints are not needed since this is an internal Schema, but it is good practice to have them pages_viewed_range: Annotated[str, StringConstraints(strip_whitespace=True, min_length=3, max_length=64) ] = Field() class PageViewsSchemaWrite(BaseWrite): # PageViews is an enum that is discriminated against in_type: Literal[PageViews] = Field(exclude=True) minimum_views: NonNegativeInt = Field() maximum_views: NonNegativeInt = Field() u/model_validator(mode=”after”) def validate_page_viewed_range(self) -> PageViewsRangeSchema: if self.minimum_views > self.maximum_views: raise ValueError(“Error here.”) # Cannot use a model_serializer here because it is applied and shared # amongst all Schemas. The DB expects a range of min-max, but that is a bad user experience. return PageViewsRangeSchema(pages_viewed_range=f”{self.minimum_views}-{self.maximum_views}”) In practice, this just works. Docs look fine, I can send a min/max value through the API request and that gets translated into a range for the DB. I know the model_validator is supposed to return Self, but I cannot get this to fit my use case. If I have an extra computed field property, it doesn’t get called to insert into the DB and complains that there are extra fields. At this point, I cannot change the existing behavior of how we write these models to the DB. However, as I’m writing unit tests with pytest I am finding this doesn’t “just work.” No matter what I do, I cannot get the unit test to return an instance of PageViewsRangeSchema like it does when actually running the code. An example of my tests is as follows: schema = make_target_schema( schema=PageViewsSchemaWrite, in_type=PageViews, minimum_views=1, maximum_views=5) # me trying to see if it works by dumping… It doesn’t dumped_schema = schema.model_dump(by_alias=True) assert schema.pages_viewed_range == “1-5” Where `make_target_schema` is just a fancy wrapper for creating the Schema defined above. I am trying to figure out if this is an acceptable workflow, or if the behavior is “undefined” leading to the difference between running it live vs unit tests. Or, if there is something I may have overlooked and should be done a completely different way. I have tried using a model_serializer to fit this case. However, the model_serializer is shared amongst all Schemas leading to many things breaking in the process. I would have expected the serializer to not be shared, but that is not the case and I cannot get the `model_serializer` to work with this one Schema. For this, I would expect a different schema to be returned after the model is validated. This is true in practice, but not so during unit testing. submitted by /u/_hemafencing_ [link] [comments] 

Summary of the problem: A well-established DB table expects a range of page views. However, when developing the API, I found it to be an abysmal user experience. I plan to create a min/max field for the user to supply input, then change it so it’s in a format the DB expects.

I cannot change anything about how the DB table is structured.

My current code:

class PageViewsRangeSchema(BaseWrite): """BaseWrite just provides config to exclude extra fields """ # These constraints are not needed since this is an internal Schema, but it is good practice to have them pages_viewed_range: Annotated[str, StringConstraints(strip_whitespace=True, min_length=3, max_length=64) ] = Field() class PageViewsSchemaWrite(BaseWrite): # PageViews is an enum that is discriminated against in_type: Literal[PageViews] = Field(exclude=True) minimum_views: NonNegativeInt = Field() maximum_views: NonNegativeInt = Field() u/model_validator(mode="after") def validate_page_viewed_range(self) -> PageViewsRangeSchema: if self.minimum_views > self.maximum_views: raise ValueError("Error here.") # Cannot use a model_serializer here because it is applied and shared # amongst all Schemas. The DB expects a range of min-max, but that is a bad user experience. return PageViewsRangeSchema(pages_viewed_range=f"{self.minimum_views}-{self.maximum_views}") 

In practice, this just works. Docs look fine, I can send a min/max value through the API request and that gets translated into a range for the DB. I know the model_validator is supposed to return Self, but I cannot get this to fit my use case. If I have an extra computed field property, it doesn’t get called to insert into the DB and complains that there are extra fields. At this point, I cannot change the existing behavior of how we write these models to the DB.

However, as I’m writing unit tests with pytest I am finding this doesn’t “just work.” No matter what I do, I cannot get the unit test to return an instance of PageViewsRangeSchema like it does when actually running the code.

An example of my tests is as follows:

schema = make_target_schema( schema=PageViewsSchemaWrite, in_type=PageViews, minimum_views=1, maximum_views=5) # me trying to see if it works by dumping... It doesn't dumped_schema = schema.model_dump(by_alias=True) assert schema.pages_viewed_range == "1-5" 

Where `make_target_schema` is just a fancy wrapper for creating the Schema defined above.

I am trying to figure out if this is an acceptable workflow, or if the behavior is “undefined” leading to the difference between running it live vs unit tests. Or, if there is something I may have overlooked and should be done a completely different way.

I have tried using a model_serializer to fit this case. However, the model_serializer is shared amongst all Schemas leading to many things breaking in the process.

I would have expected the serializer to not be shared, but that is not the case and I cannot get the `model_serializer` to work with this one Schema.

For this, I would expect a different schema to be returned after the model is validated. This is true in practice, but not so during unit testing.

submitted by /u/_hemafencing_
[link] [comments] 

Leave a Reply

Your email address will not be published. Required fields are marked *