This issue was moved to a discussion.
You can continue the conversation there. Go to discussion →
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Use third party class as property in pydantic schema #68
Comments
Thanks for the report, sorry for the delay. I see/assume you are using MongoDB, right? I hope to check and debug it soon, but it might take a bit as I have to set up a stack with mongo (and I don't have a project generator with Mongo just yet). |
Thanks for your response. You are right, I'm using MongoDB. You just need to install the following pip packages:
And then put the first code snippet in a module called Note: I had to slightly modify my code snippets to make it possible to just copy-paste them. |
Excellent, I'll use that to debug/develop it. |
Was there a conclusion to this? Trying to parse mongos _id field is proving to be quite tricky unless i just delete it before returning the response |
@Charlie-iProov not yet, but it's on the backlog. |
Not sure this being a bug or a feature.
you're basically declaring that your response will be a class UserId(BaseModel):
object_id: ObjectId = None if I was on the other side, receiving this response, I would then have to expect this kind of {
"object_id": ObjectId("5cdc01a6d8893f59a36d9957")
} which would be pretty strange, since I couldn't have that Moreover, having to @app.post("/user", tags=["user"], response_model=UserId)
def create_user(user: User)
again, my problem would be what to send as an {
"object_id": ObjectId("5cdc01a6d8893f59a36d9957"),
"email" : "my@mail.com",
"password": "letmein",
"salt":"12345678"
} That's why defining custom |
@stefanondisponibile I'm currently working on this PR in Pydantic: pydantic/pydantic#520 It will allow you to declare |
That's great! |
About pydantic/pydantic#520, it was superseded by pydantic/pydantic#562. While reviewing it I tested with from bson import ObjectId
from pydantic import BaseModel
class ObjectIdStr(str):
@classmethod
def __get_validators__(cls):
yield cls.validate
@classmethod
def validate(cls, v):
if not isinstance(v, ObjectId):
raise ValueError("Not a valid ObjectId")
return str(v)
class UserId(BaseModel):
object_id: ObjectIdStr = None
class User(UserId):
email: str
salt: str
hashed_password: str
# Just for testing
user = User(object_id = ObjectId(), email="john.doe@example.com", salt="12345678", hashed_password="letmein")
print(user.json())
# Outputs:
# {"object_id": "5c7e424225e2971c8c548a86", "email": "john.doe@example.com", "salt": "12345678", "hashed_password": "letmein"} The trick is, there's no way to declare a JSON Schema for a BSON Then, if you need the |
That will do for now, thanks for you effort! I will also test my example with the changes to Pydantic you referenced when I get around to it. |
I'm using the solution proposed by @tiangolo up above, just I preferred doing this: class ObjectIdStr(str):
@classmethod
def __get_validators__(cls):
yield cls.validate
@classmethod
def validate(cls, v):
try:
ObjectId(str(v))
except InvalidId:
raise ValuerError("Not a valid ObjectId")
return str(v) this way I can either pass a valid ObjectId string or an ObjectId instance. This works pretty nicely also with What I'm striving to understand now, though, is why can't I get an ObjectId back from the from bson import ObjectId
from pydantic import BaseModel
from upabove import ObjectIdStr
class SomeItem(BaseModel):
some_id: ObjectIdStr
class Config:
json_encoders = {ObjectIdStr: lambda x: ObjectId(x)} Why wouldn't |
Okay, so pardon me if I don't make much sense. I face this Every time I hit the route which is supposed to fetch me data from Mongodb, I get Help |
Hi @senjenathaniel ! Are you sure your problem fits this issue? If you could give some more details, and an example of the code you're using, I think someone could give the proper advice :) |
@stefanondisponibile Have you solved this issue? I am also getting |
Actually, I found the answer here: pydantic/pydantic#1671 So it should be: class ObjectId(bson.ObjectId):
@classmethod
def __get_validators__(cls):
yield cls.validate
@classmethod
def validate(cls, value):
try:
return cls(value)
except bson.errors.InvalidId:
raise ValueError("Not a valid ObjectId")
class BaseModel(p.BaseModel):
class Config:
json_encoders = {ObjectId: str} |
+1
import typing as T
from datetime import datetime
from bson.errors import InvalidId
from bson.objectid import ObjectId as BsonObjectId
from pydantic import (
BaseModel as _BaseModel,
Field,
)
class ObjectId(BsonObjectId):
@classmethod
def __get_validators__(cls):
yield cls.validate
@classmethod
def validate(cls, v):
try:
return cls(v)
except InvalidId:
raise ValueError(f'{v} is not a valid ObjectId')
class BaseModel(_BaseModel):
class Config:
json_encoders = {ObjectId: str}
class BaseMongoCreateModel(BaseModel):
date_created: T.Optional[datetime]
date_updated: T.Optional[datetime]
class Config:
allow_population_by_field_name=True
orm_mode=True
class BaseMongoRetrieveModel(BaseMongoCreateModel):
id: T.Union[str, ObjectId] = Field(...)
class Config:
orm_mode = True
allow_population_by_field_name=True $ python
Python 3.8.12 | packaged by conda-forge | (default, Jan 30 2022, 23:53:36)
[GCC 9.4.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import pydantic; pydantic.__version__
'1.9.0'
>>> import fastapi; fastapi.__version__
'0.73.0' |
Solved reading the implementation of hbusul/kucukdev_api#18 import typing as T
from datetime import datetime
from bson.errors import InvalidId
from bson.objectid import ObjectId as BsonObjectId
from pydantic import (
BaseModel as _BaseModel,
Field,
)
class ObjectId(BsonObjectId):
@classmethod
def __get_validators__(cls):
yield cls.validate
@classmethod
def validate(cls, v):
try:
return cls(v)
except InvalidId:
raise ValueError(f'{v} is not a valid ObjectId')
+
+ @classmethod
+ def __modify_schema__(cls, field_schema):
+ field_schema.update(type='string')
class BaseModel(_BaseModel):
class Config:
json_encoders = {ObjectId: str}
class BaseMongoCreateModel(BaseModel):
date_created: T.Optional[datetime]
date_updated: T.Optional[datetime]
class Config:
allow_population_by_field_name=True
orm_mode=True
class BaseMongoRetrieveModel(BaseMongoCreateModel):
id: T.Union[str, ObjectId] = Field(...)
class Config:
orm_mode = True
allow_population_by_field_name=True |
I'm getting the same issues with trying to access geoalchemy2 data through shapely geometry types. @gustavorps' comment above works, but doesn't seem very "safe". Why are we having to monkey patch the type of the field? |
@gustavorps thanks for that code it is useful! Here is the solution I did to make it work for my arbitary type: tiangolo/sqlmodel#235 (comment) |
are you patching the geometry type to include more classmethods? I am interested to see how you have made it work as I am also facing this issue using sqlmodel + shapely
I had to add
as |
Assuming the original need was handled, this will be automatically closed now. But feel free to add more comments or create new issues or PRs. |
This issue was moved to a discussion.
You can continue the conversation there. Go to discussion →
Describe the bug
I have a pydantic schema that needs a third party class (bson.objectid.ObjectID) as a property. For this reason I created a custom validator and encoder as per pydantic documentation.
Code
As you can see at the bottom of the code, the serialization seems to work just fine. But when I use this schema as an argument (and/or response type) in API operations and then open the automatic documentation, I get presented with an error.
Code
Log
To Reproduce
Copy my code and follow the instrcutions given in the "Describe the bug" section.
Expected behavior
No error should occur and the documentation should be able to show the schema correctly.
Environment:
The text was updated successfully, but these errors were encountered: