✨ Add support for Pydantic v2 (while keeping support for v1 if v2 is not available), including initial work by AntonDeMeester (#722)
Co-authored-by: Mohamed Farahat <farahats9@yahoo.com> Co-authored-by: Stefan Borer <stefan.borer@gmail.com> Co-authored-by: Peter Landry <peter.landry@gmail.com> Co-authored-by: Anton De Meester <antondemeester+github@gmail.com>
This commit is contained in:
parent
5b733b348d
commit
fa2f178b8a
11
.github/workflows/test.yml
vendored
11
.github/workflows/test.yml
vendored
@ -27,6 +27,9 @@ jobs:
|
|||||||
- "3.10"
|
- "3.10"
|
||||||
- "3.11"
|
- "3.11"
|
||||||
- "3.12"
|
- "3.12"
|
||||||
|
pydantic-version:
|
||||||
|
- pydantic-v1
|
||||||
|
- pydantic-v2
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
@ -57,9 +60,15 @@ jobs:
|
|||||||
- name: Install Dependencies
|
- name: Install Dependencies
|
||||||
if: steps.cache.outputs.cache-hit != 'true'
|
if: steps.cache.outputs.cache-hit != 'true'
|
||||||
run: python -m poetry install
|
run: python -m poetry install
|
||||||
|
- name: Install Pydantic v1
|
||||||
|
if: matrix.pydantic-version == 'pydantic-v1'
|
||||||
|
run: pip install "pydantic>=1.10.0,<2.0.0"
|
||||||
|
- name: Install Pydantic v2
|
||||||
|
if: matrix.pydantic-version == 'pydantic-v2'
|
||||||
|
run: pip install "pydantic>=2.0.2,<3.0.0"
|
||||||
- name: Lint
|
- name: Lint
|
||||||
# Do not run on Python 3.7 as mypy behaves differently
|
# Do not run on Python 3.7 as mypy behaves differently
|
||||||
if: matrix.python-version != '3.7'
|
if: matrix.python-version != '3.7' && matrix.pydantic-version == 'pydantic-v2'
|
||||||
run: python -m poetry run bash scripts/lint.sh
|
run: python -m poetry run bash scripts/lint.sh
|
||||||
- run: mkdir coverage
|
- run: mkdir coverage
|
||||||
- name: Test
|
- name: Test
|
||||||
|
@ -175,15 +175,17 @@ Now we use the type annotation `HeroCreate` for the request JSON data in the `he
|
|||||||
# Code below omitted 👇
|
# Code below omitted 👇
|
||||||
```
|
```
|
||||||
|
|
||||||
Then we create a new `Hero` (this is the actual **table** model that saves things to the database) using `Hero.from_orm()`.
|
Then we create a new `Hero` (this is the actual **table** model that saves things to the database) using `Hero.model_validate()`.
|
||||||
|
|
||||||
The method `.from_orm()` reads data from another object with attributes and creates a new instance of this class, in this case `Hero`.
|
The method `.model_validate()` reads data from another object with attributes (or a dict) and creates a new instance of this class, in this case `Hero`.
|
||||||
|
|
||||||
The alternative is `Hero.parse_obj()` that reads data from a dictionary.
|
In this case, we have a `HeroCreate` instance in the `hero` variable. This is an object with attributes, so we use `.model_validate()` to read those attributes.
|
||||||
|
|
||||||
But as in this case, we have a `HeroCreate` instance in the `hero` variable. This is an object with attributes, so we use `.from_orm()` to read those attributes.
|
/// tip
|
||||||
|
In versions of **SQLModel** before `0.0.14` you would use the method `.from_orm()`, but it is now deprecated and you should use `.model_validate()` instead.
|
||||||
|
///
|
||||||
|
|
||||||
With this, we create a new `Hero` instance (the one for the database) and put it in the variable `db_hero` from the data in the `hero` variable that is the `HeroCreate` instance we received from the request.
|
We can now create a new `Hero` instance (the one for the database) and put it in the variable `db_hero` from the data in the `hero` variable that is the `HeroCreate` instance we received from the request.
|
||||||
|
|
||||||
```Python hl_lines="3"
|
```Python hl_lines="3"
|
||||||
# Code above omitted 👆
|
# Code above omitted 👆
|
||||||
|
@ -90,7 +90,7 @@ So, we need to read the hero from the database, with the **same logic** we used
|
|||||||
|
|
||||||
The `HeroUpdate` model has all the fields with **default values**, because they all have defaults, they are all optional, which is what we want.
|
The `HeroUpdate` model has all the fields with **default values**, because they all have defaults, they are all optional, which is what we want.
|
||||||
|
|
||||||
But that also means that if we just call `hero.dict()` we will get a dictionary that could potentially have several or all of those values with their defaults, for example:
|
But that also means that if we just call `hero.model_dump()` we will get a dictionary that could potentially have several or all of those values with their defaults, for example:
|
||||||
|
|
||||||
```Python
|
```Python
|
||||||
{
|
{
|
||||||
@ -102,7 +102,7 @@ But that also means that if we just call `hero.dict()` we will get a dictionary
|
|||||||
|
|
||||||
And then, if we update the hero in the database with this data, we would be removing any existing values, and that's probably **not what the client intended**.
|
And then, if we update the hero in the database with this data, we would be removing any existing values, and that's probably **not what the client intended**.
|
||||||
|
|
||||||
But fortunately Pydantic models (and so SQLModel models) have a parameter we can pass to the `.dict()` method for that: `exclude_unset=True`.
|
But fortunately Pydantic models (and so SQLModel models) have a parameter we can pass to the `.model_dump()` method for that: `exclude_unset=True`.
|
||||||
|
|
||||||
This tells Pydantic to **not include** the values that were **not sent** by the client. Saying it another way, it would **only** include the values that were **sent by the client**.
|
This tells Pydantic to **not include** the values that were **not sent** by the client. Saying it another way, it would **only** include the values that were **sent by the client**.
|
||||||
|
|
||||||
@ -112,7 +112,7 @@ So, if the client sent a JSON with no values:
|
|||||||
{}
|
{}
|
||||||
```
|
```
|
||||||
|
|
||||||
Then the dictionary we would get in Python using `hero.dict(exclude_unset=True)` would be:
|
Then the dictionary we would get in Python using `hero.model_dump(exclude_unset=True)` would be:
|
||||||
|
|
||||||
```Python
|
```Python
|
||||||
{}
|
{}
|
||||||
@ -126,7 +126,7 @@ But if the client sent a JSON with:
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Then the dictionary we would get in Python using `hero.dict(exclude_unset=True)` would be:
|
Then the dictionary we would get in Python using `hero.model_dump(exclude_unset=True)` would be:
|
||||||
|
|
||||||
```Python
|
```Python
|
||||||
{
|
{
|
||||||
@ -152,6 +152,9 @@ Then we use that to get the data that was actually sent by the client:
|
|||||||
|
|
||||||
///
|
///
|
||||||
|
|
||||||
|
/// tip
|
||||||
|
Before SQLModel 0.0.14, the method was called `hero.dict(exclude_unset=True)`, but it was renamed to `hero.model_dump(exclude_unset=True)` to be consistent with Pydantic v2.
|
||||||
|
|
||||||
## Update the Hero in the Database
|
## Update the Hero in the Database
|
||||||
|
|
||||||
Now that we have a **dictionary with the data sent by the client**, we can iterate for each one of the keys and the values, and then we set them in the database hero model `db_hero` using `setattr()`.
|
Now that we have a **dictionary with the data sent by the client**, we can iterate for each one of the keys and the values, and then we set them in the database hero model `db_hero` using `setattr()`.
|
||||||
@ -208,7 +211,7 @@ So, if the client wanted to intentionally remove the `age` of a hero, they could
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
And when getting the data with `hero.dict(exclude_unset=True)`, we would get:
|
And when getting the data with `hero.model_dump(exclude_unset=True)`, we would get:
|
||||||
|
|
||||||
```Python
|
```Python
|
||||||
{
|
{
|
||||||
@ -226,4 +229,4 @@ These are some of the advantages of Pydantic, that we can use with SQLModel.
|
|||||||
|
|
||||||
## Recap
|
## Recap
|
||||||
|
|
||||||
Using `.dict(exclude_unset=True)` in SQLModel models (and Pydantic models) we can easily update data **correctly**, even in the **edge cases**. 😎
|
Using `.model_dump(exclude_unset=True)` in SQLModel models (and Pydantic models) we can easily update data **correctly**, even in the **edge cases**. 😎
|
||||||
|
@ -82,8 +82,8 @@ There's a chance that you have multiple Python versions installed.
|
|||||||
|
|
||||||
You might want to try with the specific versions, for example with:
|
You might want to try with the specific versions, for example with:
|
||||||
|
|
||||||
* `python3.11`
|
|
||||||
* `python3.12`
|
* `python3.12`
|
||||||
|
* `python3.11`
|
||||||
* `python3.10`
|
* `python3.10`
|
||||||
* `python3.9`
|
* `python3.9`
|
||||||
|
|
||||||
|
@ -54,7 +54,7 @@ def on_startup():
|
|||||||
|
|
||||||
@app.post("/heroes/", response_model=HeroRead)
|
@app.post("/heroes/", response_model=HeroRead)
|
||||||
def create_hero(*, session: Session = Depends(get_session), hero: HeroCreate):
|
def create_hero(*, session: Session = Depends(get_session), hero: HeroCreate):
|
||||||
db_hero = Hero.from_orm(hero)
|
db_hero = Hero.model_validate(hero)
|
||||||
session.add(db_hero)
|
session.add(db_hero)
|
||||||
session.commit()
|
session.commit()
|
||||||
session.refresh(db_hero)
|
session.refresh(db_hero)
|
||||||
@ -87,7 +87,7 @@ def update_hero(
|
|||||||
db_hero = session.get(Hero, hero_id)
|
db_hero = session.get(Hero, hero_id)
|
||||||
if not db_hero:
|
if not db_hero:
|
||||||
raise HTTPException(status_code=404, detail="Hero not found")
|
raise HTTPException(status_code=404, detail="Hero not found")
|
||||||
hero_data = hero.dict(exclude_unset=True)
|
hero_data = hero.model_dump(exclude_unset=True)
|
||||||
for key, value in hero_data.items():
|
for key, value in hero_data.items():
|
||||||
setattr(db_hero, key, value)
|
setattr(db_hero, key, value)
|
||||||
session.add(db_hero)
|
session.add(db_hero)
|
||||||
|
@ -52,7 +52,7 @@ def on_startup():
|
|||||||
|
|
||||||
@app.post("/heroes/", response_model=HeroRead)
|
@app.post("/heroes/", response_model=HeroRead)
|
||||||
def create_hero(*, session: Session = Depends(get_session), hero: HeroCreate):
|
def create_hero(*, session: Session = Depends(get_session), hero: HeroCreate):
|
||||||
db_hero = Hero.from_orm(hero)
|
db_hero = Hero.model_validate(hero)
|
||||||
session.add(db_hero)
|
session.add(db_hero)
|
||||||
session.commit()
|
session.commit()
|
||||||
session.refresh(db_hero)
|
session.refresh(db_hero)
|
||||||
@ -85,7 +85,7 @@ def update_hero(
|
|||||||
db_hero = session.get(Hero, hero_id)
|
db_hero = session.get(Hero, hero_id)
|
||||||
if not db_hero:
|
if not db_hero:
|
||||||
raise HTTPException(status_code=404, detail="Hero not found")
|
raise HTTPException(status_code=404, detail="Hero not found")
|
||||||
hero_data = hero.dict(exclude_unset=True)
|
hero_data = hero.model_dump(exclude_unset=True)
|
||||||
for key, value in hero_data.items():
|
for key, value in hero_data.items():
|
||||||
setattr(db_hero, key, value)
|
setattr(db_hero, key, value)
|
||||||
session.add(db_hero)
|
session.add(db_hero)
|
||||||
|
@ -54,7 +54,7 @@ def on_startup():
|
|||||||
|
|
||||||
@app.post("/heroes/", response_model=HeroRead)
|
@app.post("/heroes/", response_model=HeroRead)
|
||||||
def create_hero(*, session: Session = Depends(get_session), hero: HeroCreate):
|
def create_hero(*, session: Session = Depends(get_session), hero: HeroCreate):
|
||||||
db_hero = Hero.from_orm(hero)
|
db_hero = Hero.model_validate(hero)
|
||||||
session.add(db_hero)
|
session.add(db_hero)
|
||||||
session.commit()
|
session.commit()
|
||||||
session.refresh(db_hero)
|
session.refresh(db_hero)
|
||||||
@ -87,7 +87,7 @@ def update_hero(
|
|||||||
db_hero = session.get(Hero, hero_id)
|
db_hero = session.get(Hero, hero_id)
|
||||||
if not db_hero:
|
if not db_hero:
|
||||||
raise HTTPException(status_code=404, detail="Hero not found")
|
raise HTTPException(status_code=404, detail="Hero not found")
|
||||||
hero_data = hero.dict(exclude_unset=True)
|
hero_data = hero.model_dump(exclude_unset=True)
|
||||||
for key, value in hero_data.items():
|
for key, value in hero_data.items():
|
||||||
setattr(db_hero, key, value)
|
setattr(db_hero, key, value)
|
||||||
session.add(db_hero)
|
session.add(db_hero)
|
||||||
|
@ -50,7 +50,7 @@ def on_startup():
|
|||||||
@app.post("/heroes/", response_model=HeroRead)
|
@app.post("/heroes/", response_model=HeroRead)
|
||||||
def create_hero(hero: HeroCreate):
|
def create_hero(hero: HeroCreate):
|
||||||
with Session(engine) as session:
|
with Session(engine) as session:
|
||||||
db_hero = Hero.from_orm(hero)
|
db_hero = Hero.model_validate(hero)
|
||||||
session.add(db_hero)
|
session.add(db_hero)
|
||||||
session.commit()
|
session.commit()
|
||||||
session.refresh(db_hero)
|
session.refresh(db_hero)
|
||||||
@ -79,7 +79,7 @@ def update_hero(hero_id: int, hero: HeroUpdate):
|
|||||||
db_hero = session.get(Hero, hero_id)
|
db_hero = session.get(Hero, hero_id)
|
||||||
if not db_hero:
|
if not db_hero:
|
||||||
raise HTTPException(status_code=404, detail="Hero not found")
|
raise HTTPException(status_code=404, detail="Hero not found")
|
||||||
hero_data = hero.dict(exclude_unset=True)
|
hero_data = hero.model_dump(exclude_unset=True)
|
||||||
for key, value in hero_data.items():
|
for key, value in hero_data.items():
|
||||||
setattr(db_hero, key, value)
|
setattr(db_hero, key, value)
|
||||||
session.add(db_hero)
|
session.add(db_hero)
|
||||||
|
@ -48,7 +48,7 @@ def on_startup():
|
|||||||
@app.post("/heroes/", response_model=HeroRead)
|
@app.post("/heroes/", response_model=HeroRead)
|
||||||
def create_hero(hero: HeroCreate):
|
def create_hero(hero: HeroCreate):
|
||||||
with Session(engine) as session:
|
with Session(engine) as session:
|
||||||
db_hero = Hero.from_orm(hero)
|
db_hero = Hero.model_validate(hero)
|
||||||
session.add(db_hero)
|
session.add(db_hero)
|
||||||
session.commit()
|
session.commit()
|
||||||
session.refresh(db_hero)
|
session.refresh(db_hero)
|
||||||
@ -77,7 +77,7 @@ def update_hero(hero_id: int, hero: HeroUpdate):
|
|||||||
db_hero = session.get(Hero, hero_id)
|
db_hero = session.get(Hero, hero_id)
|
||||||
if not db_hero:
|
if not db_hero:
|
||||||
raise HTTPException(status_code=404, detail="Hero not found")
|
raise HTTPException(status_code=404, detail="Hero not found")
|
||||||
hero_data = hero.dict(exclude_unset=True)
|
hero_data = hero.model_dump(exclude_unset=True)
|
||||||
for key, value in hero_data.items():
|
for key, value in hero_data.items():
|
||||||
setattr(db_hero, key, value)
|
setattr(db_hero, key, value)
|
||||||
session.add(db_hero)
|
session.add(db_hero)
|
||||||
|
@ -50,7 +50,7 @@ def on_startup():
|
|||||||
@app.post("/heroes/", response_model=HeroRead)
|
@app.post("/heroes/", response_model=HeroRead)
|
||||||
def create_hero(hero: HeroCreate):
|
def create_hero(hero: HeroCreate):
|
||||||
with Session(engine) as session:
|
with Session(engine) as session:
|
||||||
db_hero = Hero.from_orm(hero)
|
db_hero = Hero.model_validate(hero)
|
||||||
session.add(db_hero)
|
session.add(db_hero)
|
||||||
session.commit()
|
session.commit()
|
||||||
session.refresh(db_hero)
|
session.refresh(db_hero)
|
||||||
@ -79,7 +79,7 @@ def update_hero(hero_id: int, hero: HeroUpdate):
|
|||||||
db_hero = session.get(Hero, hero_id)
|
db_hero = session.get(Hero, hero_id)
|
||||||
if not db_hero:
|
if not db_hero:
|
||||||
raise HTTPException(status_code=404, detail="Hero not found")
|
raise HTTPException(status_code=404, detail="Hero not found")
|
||||||
hero_data = hero.dict(exclude_unset=True)
|
hero_data = hero.model_dump(exclude_unset=True)
|
||||||
for key, value in hero_data.items():
|
for key, value in hero_data.items():
|
||||||
setattr(db_hero, key, value)
|
setattr(db_hero, key, value)
|
||||||
session.add(db_hero)
|
session.add(db_hero)
|
||||||
|
@ -44,7 +44,7 @@ def on_startup():
|
|||||||
@app.post("/heroes/", response_model=HeroRead)
|
@app.post("/heroes/", response_model=HeroRead)
|
||||||
def create_hero(hero: HeroCreate):
|
def create_hero(hero: HeroCreate):
|
||||||
with Session(engine) as session:
|
with Session(engine) as session:
|
||||||
db_hero = Hero.from_orm(hero)
|
db_hero = Hero.model_validate(hero)
|
||||||
session.add(db_hero)
|
session.add(db_hero)
|
||||||
session.commit()
|
session.commit()
|
||||||
session.refresh(db_hero)
|
session.refresh(db_hero)
|
||||||
|
@ -42,7 +42,7 @@ def on_startup():
|
|||||||
@app.post("/heroes/", response_model=HeroRead)
|
@app.post("/heroes/", response_model=HeroRead)
|
||||||
def create_hero(hero: HeroCreate):
|
def create_hero(hero: HeroCreate):
|
||||||
with Session(engine) as session:
|
with Session(engine) as session:
|
||||||
db_hero = Hero.from_orm(hero)
|
db_hero = Hero.model_validate(hero)
|
||||||
session.add(db_hero)
|
session.add(db_hero)
|
||||||
session.commit()
|
session.commit()
|
||||||
session.refresh(db_hero)
|
session.refresh(db_hero)
|
||||||
|
@ -44,7 +44,7 @@ def on_startup():
|
|||||||
@app.post("/heroes/", response_model=HeroRead)
|
@app.post("/heroes/", response_model=HeroRead)
|
||||||
def create_hero(hero: HeroCreate):
|
def create_hero(hero: HeroCreate):
|
||||||
with Session(engine) as session:
|
with Session(engine) as session:
|
||||||
db_hero = Hero.from_orm(hero)
|
db_hero = Hero.model_validate(hero)
|
||||||
session.add(db_hero)
|
session.add(db_hero)
|
||||||
session.commit()
|
session.commit()
|
||||||
session.refresh(db_hero)
|
session.refresh(db_hero)
|
||||||
|
@ -46,7 +46,7 @@ def on_startup():
|
|||||||
@app.post("/heroes/", response_model=HeroRead)
|
@app.post("/heroes/", response_model=HeroRead)
|
||||||
def create_hero(hero: HeroCreate):
|
def create_hero(hero: HeroCreate):
|
||||||
with Session(engine) as session:
|
with Session(engine) as session:
|
||||||
db_hero = Hero.from_orm(hero)
|
db_hero = Hero.model_validate(hero)
|
||||||
session.add(db_hero)
|
session.add(db_hero)
|
||||||
session.commit()
|
session.commit()
|
||||||
session.refresh(db_hero)
|
session.refresh(db_hero)
|
||||||
|
@ -44,7 +44,7 @@ def on_startup():
|
|||||||
@app.post("/heroes/", response_model=HeroRead)
|
@app.post("/heroes/", response_model=HeroRead)
|
||||||
def create_hero(hero: HeroCreate):
|
def create_hero(hero: HeroCreate):
|
||||||
with Session(engine) as session:
|
with Session(engine) as session:
|
||||||
db_hero = Hero.from_orm(hero)
|
db_hero = Hero.model_validate(hero)
|
||||||
session.add(db_hero)
|
session.add(db_hero)
|
||||||
session.commit()
|
session.commit()
|
||||||
session.refresh(db_hero)
|
session.refresh(db_hero)
|
||||||
|
@ -46,7 +46,7 @@ def on_startup():
|
|||||||
@app.post("/heroes/", response_model=HeroRead)
|
@app.post("/heroes/", response_model=HeroRead)
|
||||||
def create_hero(hero: HeroCreate):
|
def create_hero(hero: HeroCreate):
|
||||||
with Session(engine) as session:
|
with Session(engine) as session:
|
||||||
db_hero = Hero.from_orm(hero)
|
db_hero = Hero.model_validate(hero)
|
||||||
session.add(db_hero)
|
session.add(db_hero)
|
||||||
session.commit()
|
session.commit()
|
||||||
session.refresh(db_hero)
|
session.refresh(db_hero)
|
||||||
|
@ -44,7 +44,7 @@ def on_startup():
|
|||||||
@app.post("/heroes/", response_model=HeroRead)
|
@app.post("/heroes/", response_model=HeroRead)
|
||||||
def create_hero(hero: HeroCreate):
|
def create_hero(hero: HeroCreate):
|
||||||
with Session(engine) as session:
|
with Session(engine) as session:
|
||||||
db_hero = Hero.from_orm(hero)
|
db_hero = Hero.model_validate(hero)
|
||||||
session.add(db_hero)
|
session.add(db_hero)
|
||||||
session.commit()
|
session.commit()
|
||||||
session.refresh(db_hero)
|
session.refresh(db_hero)
|
||||||
|
@ -42,7 +42,7 @@ def on_startup():
|
|||||||
@app.post("/heroes/", response_model=HeroRead)
|
@app.post("/heroes/", response_model=HeroRead)
|
||||||
def create_hero(hero: HeroCreate):
|
def create_hero(hero: HeroCreate):
|
||||||
with Session(engine) as session:
|
with Session(engine) as session:
|
||||||
db_hero = Hero.from_orm(hero)
|
db_hero = Hero.model_validate(hero)
|
||||||
session.add(db_hero)
|
session.add(db_hero)
|
||||||
session.commit()
|
session.commit()
|
||||||
session.refresh(db_hero)
|
session.refresh(db_hero)
|
||||||
|
@ -44,7 +44,7 @@ def on_startup():
|
|||||||
@app.post("/heroes/", response_model=HeroRead)
|
@app.post("/heroes/", response_model=HeroRead)
|
||||||
def create_hero(hero: HeroCreate):
|
def create_hero(hero: HeroCreate):
|
||||||
with Session(engine) as session:
|
with Session(engine) as session:
|
||||||
db_hero = Hero.from_orm(hero)
|
db_hero = Hero.model_validate(hero)
|
||||||
session.add(db_hero)
|
session.add(db_hero)
|
||||||
session.commit()
|
session.commit()
|
||||||
session.refresh(db_hero)
|
session.refresh(db_hero)
|
||||||
|
@ -44,7 +44,7 @@ def on_startup():
|
|||||||
@app.post("/heroes/", response_model=HeroRead)
|
@app.post("/heroes/", response_model=HeroRead)
|
||||||
def create_hero(hero: HeroCreate):
|
def create_hero(hero: HeroCreate):
|
||||||
with Session(engine) as session:
|
with Session(engine) as session:
|
||||||
db_hero = Hero.from_orm(hero)
|
db_hero = Hero.model_validate(hero)
|
||||||
session.add(db_hero)
|
session.add(db_hero)
|
||||||
session.commit()
|
session.commit()
|
||||||
session.refresh(db_hero)
|
session.refresh(db_hero)
|
||||||
|
@ -42,7 +42,7 @@ def on_startup():
|
|||||||
@app.post("/heroes/", response_model=HeroRead)
|
@app.post("/heroes/", response_model=HeroRead)
|
||||||
def create_hero(hero: HeroCreate):
|
def create_hero(hero: HeroCreate):
|
||||||
with Session(engine) as session:
|
with Session(engine) as session:
|
||||||
db_hero = Hero.from_orm(hero)
|
db_hero = Hero.model_validate(hero)
|
||||||
session.add(db_hero)
|
session.add(db_hero)
|
||||||
session.commit()
|
session.commit()
|
||||||
session.refresh(db_hero)
|
session.refresh(db_hero)
|
||||||
|
@ -44,7 +44,7 @@ def on_startup():
|
|||||||
@app.post("/heroes/", response_model=HeroRead)
|
@app.post("/heroes/", response_model=HeroRead)
|
||||||
def create_hero(hero: HeroCreate):
|
def create_hero(hero: HeroCreate):
|
||||||
with Session(engine) as session:
|
with Session(engine) as session:
|
||||||
db_hero = Hero.from_orm(hero)
|
db_hero = Hero.model_validate(hero)
|
||||||
session.add(db_hero)
|
session.add(db_hero)
|
||||||
session.commit()
|
session.commit()
|
||||||
session.refresh(db_hero)
|
session.refresh(db_hero)
|
||||||
|
@ -92,7 +92,7 @@ def on_startup():
|
|||||||
|
|
||||||
@app.post("/heroes/", response_model=HeroRead)
|
@app.post("/heroes/", response_model=HeroRead)
|
||||||
def create_hero(*, session: Session = Depends(get_session), hero: HeroCreate):
|
def create_hero(*, session: Session = Depends(get_session), hero: HeroCreate):
|
||||||
db_hero = Hero.from_orm(hero)
|
db_hero = Hero.model_validate(hero)
|
||||||
session.add(db_hero)
|
session.add(db_hero)
|
||||||
session.commit()
|
session.commit()
|
||||||
session.refresh(db_hero)
|
session.refresh(db_hero)
|
||||||
@ -125,7 +125,7 @@ def update_hero(
|
|||||||
db_hero = session.get(Hero, hero_id)
|
db_hero = session.get(Hero, hero_id)
|
||||||
if not db_hero:
|
if not db_hero:
|
||||||
raise HTTPException(status_code=404, detail="Hero not found")
|
raise HTTPException(status_code=404, detail="Hero not found")
|
||||||
hero_data = hero.dict(exclude_unset=True)
|
hero_data = hero.model_dump(exclude_unset=True)
|
||||||
for key, value in hero_data.items():
|
for key, value in hero_data.items():
|
||||||
setattr(db_hero, key, value)
|
setattr(db_hero, key, value)
|
||||||
session.add(db_hero)
|
session.add(db_hero)
|
||||||
@ -146,7 +146,7 @@ def delete_hero(*, session: Session = Depends(get_session), hero_id: int):
|
|||||||
|
|
||||||
@app.post("/teams/", response_model=TeamRead)
|
@app.post("/teams/", response_model=TeamRead)
|
||||||
def create_team(*, session: Session = Depends(get_session), team: TeamCreate):
|
def create_team(*, session: Session = Depends(get_session), team: TeamCreate):
|
||||||
db_team = Team.from_orm(team)
|
db_team = Team.model_validate(team)
|
||||||
session.add(db_team)
|
session.add(db_team)
|
||||||
session.commit()
|
session.commit()
|
||||||
session.refresh(db_team)
|
session.refresh(db_team)
|
||||||
@ -182,7 +182,7 @@ def update_team(
|
|||||||
db_team = session.get(Team, team_id)
|
db_team = session.get(Team, team_id)
|
||||||
if not db_team:
|
if not db_team:
|
||||||
raise HTTPException(status_code=404, detail="Team not found")
|
raise HTTPException(status_code=404, detail="Team not found")
|
||||||
team_data = team.dict(exclude_unset=True)
|
team_data = team.model_dump(exclude_unset=True)
|
||||||
for key, value in team_data.items():
|
for key, value in team_data.items():
|
||||||
setattr(db_team, key, value)
|
setattr(db_team, key, value)
|
||||||
session.add(db_team)
|
session.add(db_team)
|
||||||
|
@ -90,7 +90,7 @@ def on_startup():
|
|||||||
|
|
||||||
@app.post("/heroes/", response_model=HeroRead)
|
@app.post("/heroes/", response_model=HeroRead)
|
||||||
def create_hero(*, session: Session = Depends(get_session), hero: HeroCreate):
|
def create_hero(*, session: Session = Depends(get_session), hero: HeroCreate):
|
||||||
db_hero = Hero.from_orm(hero)
|
db_hero = Hero.model_validate(hero)
|
||||||
session.add(db_hero)
|
session.add(db_hero)
|
||||||
session.commit()
|
session.commit()
|
||||||
session.refresh(db_hero)
|
session.refresh(db_hero)
|
||||||
@ -123,7 +123,7 @@ def update_hero(
|
|||||||
db_hero = session.get(Hero, hero_id)
|
db_hero = session.get(Hero, hero_id)
|
||||||
if not db_hero:
|
if not db_hero:
|
||||||
raise HTTPException(status_code=404, detail="Hero not found")
|
raise HTTPException(status_code=404, detail="Hero not found")
|
||||||
hero_data = hero.dict(exclude_unset=True)
|
hero_data = hero.model_dump(exclude_unset=True)
|
||||||
for key, value in hero_data.items():
|
for key, value in hero_data.items():
|
||||||
setattr(db_hero, key, value)
|
setattr(db_hero, key, value)
|
||||||
session.add(db_hero)
|
session.add(db_hero)
|
||||||
@ -144,7 +144,7 @@ def delete_hero(*, session: Session = Depends(get_session), hero_id: int):
|
|||||||
|
|
||||||
@app.post("/teams/", response_model=TeamRead)
|
@app.post("/teams/", response_model=TeamRead)
|
||||||
def create_team(*, session: Session = Depends(get_session), team: TeamCreate):
|
def create_team(*, session: Session = Depends(get_session), team: TeamCreate):
|
||||||
db_team = Team.from_orm(team)
|
db_team = Team.model_validate(team)
|
||||||
session.add(db_team)
|
session.add(db_team)
|
||||||
session.commit()
|
session.commit()
|
||||||
session.refresh(db_team)
|
session.refresh(db_team)
|
||||||
@ -180,7 +180,7 @@ def update_team(
|
|||||||
db_team = session.get(Team, team_id)
|
db_team = session.get(Team, team_id)
|
||||||
if not db_team:
|
if not db_team:
|
||||||
raise HTTPException(status_code=404, detail="Team not found")
|
raise HTTPException(status_code=404, detail="Team not found")
|
||||||
team_data = team.dict(exclude_unset=True)
|
team_data = team.model_dump(exclude_unset=True)
|
||||||
for key, value in team_data.items():
|
for key, value in team_data.items():
|
||||||
setattr(db_team, key, value)
|
setattr(db_team, key, value)
|
||||||
session.add(db_team)
|
session.add(db_team)
|
||||||
|
@ -92,7 +92,7 @@ def on_startup():
|
|||||||
|
|
||||||
@app.post("/heroes/", response_model=HeroRead)
|
@app.post("/heroes/", response_model=HeroRead)
|
||||||
def create_hero(*, session: Session = Depends(get_session), hero: HeroCreate):
|
def create_hero(*, session: Session = Depends(get_session), hero: HeroCreate):
|
||||||
db_hero = Hero.from_orm(hero)
|
db_hero = Hero.model_validate(hero)
|
||||||
session.add(db_hero)
|
session.add(db_hero)
|
||||||
session.commit()
|
session.commit()
|
||||||
session.refresh(db_hero)
|
session.refresh(db_hero)
|
||||||
@ -125,7 +125,7 @@ def update_hero(
|
|||||||
db_hero = session.get(Hero, hero_id)
|
db_hero = session.get(Hero, hero_id)
|
||||||
if not db_hero:
|
if not db_hero:
|
||||||
raise HTTPException(status_code=404, detail="Hero not found")
|
raise HTTPException(status_code=404, detail="Hero not found")
|
||||||
hero_data = hero.dict(exclude_unset=True)
|
hero_data = hero.model_dump(exclude_unset=True)
|
||||||
for key, value in hero_data.items():
|
for key, value in hero_data.items():
|
||||||
setattr(db_hero, key, value)
|
setattr(db_hero, key, value)
|
||||||
session.add(db_hero)
|
session.add(db_hero)
|
||||||
@ -146,7 +146,7 @@ def delete_hero(*, session: Session = Depends(get_session), hero_id: int):
|
|||||||
|
|
||||||
@app.post("/teams/", response_model=TeamRead)
|
@app.post("/teams/", response_model=TeamRead)
|
||||||
def create_team(*, session: Session = Depends(get_session), team: TeamCreate):
|
def create_team(*, session: Session = Depends(get_session), team: TeamCreate):
|
||||||
db_team = Team.from_orm(team)
|
db_team = Team.model_validate(team)
|
||||||
session.add(db_team)
|
session.add(db_team)
|
||||||
session.commit()
|
session.commit()
|
||||||
session.refresh(db_team)
|
session.refresh(db_team)
|
||||||
@ -182,7 +182,7 @@ def update_team(
|
|||||||
db_team = session.get(Team, team_id)
|
db_team = session.get(Team, team_id)
|
||||||
if not db_team:
|
if not db_team:
|
||||||
raise HTTPException(status_code=404, detail="Team not found")
|
raise HTTPException(status_code=404, detail="Team not found")
|
||||||
team_data = team.dict(exclude_unset=True)
|
team_data = team.model_dump(exclude_unset=True)
|
||||||
for key, value in team_data.items():
|
for key, value in team_data.items():
|
||||||
setattr(db_team, key, value)
|
setattr(db_team, key, value)
|
||||||
session.add(db_team)
|
session.add(db_team)
|
||||||
|
@ -54,7 +54,7 @@ def on_startup():
|
|||||||
|
|
||||||
@app.post("/heroes/", response_model=HeroRead)
|
@app.post("/heroes/", response_model=HeroRead)
|
||||||
def create_hero(*, session: Session = Depends(get_session), hero: HeroCreate):
|
def create_hero(*, session: Session = Depends(get_session), hero: HeroCreate):
|
||||||
db_hero = Hero.from_orm(hero)
|
db_hero = Hero.model_validate(hero)
|
||||||
session.add(db_hero)
|
session.add(db_hero)
|
||||||
session.commit()
|
session.commit()
|
||||||
session.refresh(db_hero)
|
session.refresh(db_hero)
|
||||||
@ -87,7 +87,7 @@ def update_hero(
|
|||||||
db_hero = session.get(Hero, hero_id)
|
db_hero = session.get(Hero, hero_id)
|
||||||
if not db_hero:
|
if not db_hero:
|
||||||
raise HTTPException(status_code=404, detail="Hero not found")
|
raise HTTPException(status_code=404, detail="Hero not found")
|
||||||
hero_data = hero.dict(exclude_unset=True)
|
hero_data = hero.model_dump(exclude_unset=True)
|
||||||
for key, value in hero_data.items():
|
for key, value in hero_data.items():
|
||||||
setattr(db_hero, key, value)
|
setattr(db_hero, key, value)
|
||||||
session.add(db_hero)
|
session.add(db_hero)
|
||||||
|
@ -52,7 +52,7 @@ def on_startup():
|
|||||||
|
|
||||||
@app.post("/heroes/", response_model=HeroRead)
|
@app.post("/heroes/", response_model=HeroRead)
|
||||||
def create_hero(*, session: Session = Depends(get_session), hero: HeroCreate):
|
def create_hero(*, session: Session = Depends(get_session), hero: HeroCreate):
|
||||||
db_hero = Hero.from_orm(hero)
|
db_hero = Hero.model_validate(hero)
|
||||||
session.add(db_hero)
|
session.add(db_hero)
|
||||||
session.commit()
|
session.commit()
|
||||||
session.refresh(db_hero)
|
session.refresh(db_hero)
|
||||||
@ -85,7 +85,7 @@ def update_hero(
|
|||||||
db_hero = session.get(Hero, hero_id)
|
db_hero = session.get(Hero, hero_id)
|
||||||
if not db_hero:
|
if not db_hero:
|
||||||
raise HTTPException(status_code=404, detail="Hero not found")
|
raise HTTPException(status_code=404, detail="Hero not found")
|
||||||
hero_data = hero.dict(exclude_unset=True)
|
hero_data = hero.model_dump(exclude_unset=True)
|
||||||
for key, value in hero_data.items():
|
for key, value in hero_data.items():
|
||||||
setattr(db_hero, key, value)
|
setattr(db_hero, key, value)
|
||||||
session.add(db_hero)
|
session.add(db_hero)
|
||||||
|
@ -54,7 +54,7 @@ def on_startup():
|
|||||||
|
|
||||||
@app.post("/heroes/", response_model=HeroRead)
|
@app.post("/heroes/", response_model=HeroRead)
|
||||||
def create_hero(*, session: Session = Depends(get_session), hero: HeroCreate):
|
def create_hero(*, session: Session = Depends(get_session), hero: HeroCreate):
|
||||||
db_hero = Hero.from_orm(hero)
|
db_hero = Hero.model_validate(hero)
|
||||||
session.add(db_hero)
|
session.add(db_hero)
|
||||||
session.commit()
|
session.commit()
|
||||||
session.refresh(db_hero)
|
session.refresh(db_hero)
|
||||||
@ -87,7 +87,7 @@ def update_hero(
|
|||||||
db_hero = session.get(Hero, hero_id)
|
db_hero = session.get(Hero, hero_id)
|
||||||
if not db_hero:
|
if not db_hero:
|
||||||
raise HTTPException(status_code=404, detail="Hero not found")
|
raise HTTPException(status_code=404, detail="Hero not found")
|
||||||
hero_data = hero.dict(exclude_unset=True)
|
hero_data = hero.model_dump(exclude_unset=True)
|
||||||
for key, value in hero_data.items():
|
for key, value in hero_data.items():
|
||||||
setattr(db_hero, key, value)
|
setattr(db_hero, key, value)
|
||||||
session.add(db_hero)
|
session.add(db_hero)
|
||||||
|
@ -83,7 +83,7 @@ def on_startup():
|
|||||||
|
|
||||||
@app.post("/heroes/", response_model=HeroRead)
|
@app.post("/heroes/", response_model=HeroRead)
|
||||||
def create_hero(*, session: Session = Depends(get_session), hero: HeroCreate):
|
def create_hero(*, session: Session = Depends(get_session), hero: HeroCreate):
|
||||||
db_hero = Hero.from_orm(hero)
|
db_hero = Hero.model_validate(hero)
|
||||||
session.add(db_hero)
|
session.add(db_hero)
|
||||||
session.commit()
|
session.commit()
|
||||||
session.refresh(db_hero)
|
session.refresh(db_hero)
|
||||||
@ -116,7 +116,7 @@ def update_hero(
|
|||||||
db_hero = session.get(Hero, hero_id)
|
db_hero = session.get(Hero, hero_id)
|
||||||
if not db_hero:
|
if not db_hero:
|
||||||
raise HTTPException(status_code=404, detail="Hero not found")
|
raise HTTPException(status_code=404, detail="Hero not found")
|
||||||
hero_data = hero.dict(exclude_unset=True)
|
hero_data = hero.model_dump(exclude_unset=True)
|
||||||
for key, value in hero_data.items():
|
for key, value in hero_data.items():
|
||||||
setattr(db_hero, key, value)
|
setattr(db_hero, key, value)
|
||||||
session.add(db_hero)
|
session.add(db_hero)
|
||||||
@ -137,7 +137,7 @@ def delete_hero(*, session: Session = Depends(get_session), hero_id: int):
|
|||||||
|
|
||||||
@app.post("/teams/", response_model=TeamRead)
|
@app.post("/teams/", response_model=TeamRead)
|
||||||
def create_team(*, session: Session = Depends(get_session), team: TeamCreate):
|
def create_team(*, session: Session = Depends(get_session), team: TeamCreate):
|
||||||
db_team = Team.from_orm(team)
|
db_team = Team.model_validate(team)
|
||||||
session.add(db_team)
|
session.add(db_team)
|
||||||
session.commit()
|
session.commit()
|
||||||
session.refresh(db_team)
|
session.refresh(db_team)
|
||||||
@ -173,7 +173,7 @@ def update_team(
|
|||||||
db_team = session.get(Team, team_id)
|
db_team = session.get(Team, team_id)
|
||||||
if not db_team:
|
if not db_team:
|
||||||
raise HTTPException(status_code=404, detail="Team not found")
|
raise HTTPException(status_code=404, detail="Team not found")
|
||||||
team_data = team.dict(exclude_unset=True)
|
team_data = team.model_dump(exclude_unset=True)
|
||||||
for key, value in team_data.items():
|
for key, value in team_data.items():
|
||||||
setattr(db_team, key, value)
|
setattr(db_team, key, value)
|
||||||
session.add(db_team)
|
session.add(db_team)
|
||||||
|
@ -81,7 +81,7 @@ def on_startup():
|
|||||||
|
|
||||||
@app.post("/heroes/", response_model=HeroRead)
|
@app.post("/heroes/", response_model=HeroRead)
|
||||||
def create_hero(*, session: Session = Depends(get_session), hero: HeroCreate):
|
def create_hero(*, session: Session = Depends(get_session), hero: HeroCreate):
|
||||||
db_hero = Hero.from_orm(hero)
|
db_hero = Hero.model_validate(hero)
|
||||||
session.add(db_hero)
|
session.add(db_hero)
|
||||||
session.commit()
|
session.commit()
|
||||||
session.refresh(db_hero)
|
session.refresh(db_hero)
|
||||||
@ -114,7 +114,7 @@ def update_hero(
|
|||||||
db_hero = session.get(Hero, hero_id)
|
db_hero = session.get(Hero, hero_id)
|
||||||
if not db_hero:
|
if not db_hero:
|
||||||
raise HTTPException(status_code=404, detail="Hero not found")
|
raise HTTPException(status_code=404, detail="Hero not found")
|
||||||
hero_data = hero.dict(exclude_unset=True)
|
hero_data = hero.model_dump(exclude_unset=True)
|
||||||
for key, value in hero_data.items():
|
for key, value in hero_data.items():
|
||||||
setattr(db_hero, key, value)
|
setattr(db_hero, key, value)
|
||||||
session.add(db_hero)
|
session.add(db_hero)
|
||||||
@ -135,7 +135,7 @@ def delete_hero(*, session: Session = Depends(get_session), hero_id: int):
|
|||||||
|
|
||||||
@app.post("/teams/", response_model=TeamRead)
|
@app.post("/teams/", response_model=TeamRead)
|
||||||
def create_team(*, session: Session = Depends(get_session), team: TeamCreate):
|
def create_team(*, session: Session = Depends(get_session), team: TeamCreate):
|
||||||
db_team = Team.from_orm(team)
|
db_team = Team.model_validate(team)
|
||||||
session.add(db_team)
|
session.add(db_team)
|
||||||
session.commit()
|
session.commit()
|
||||||
session.refresh(db_team)
|
session.refresh(db_team)
|
||||||
@ -171,7 +171,7 @@ def update_team(
|
|||||||
db_team = session.get(Team, team_id)
|
db_team = session.get(Team, team_id)
|
||||||
if not db_team:
|
if not db_team:
|
||||||
raise HTTPException(status_code=404, detail="Team not found")
|
raise HTTPException(status_code=404, detail="Team not found")
|
||||||
team_data = team.dict(exclude_unset=True)
|
team_data = team.model_dump(exclude_unset=True)
|
||||||
for key, value in team_data.items():
|
for key, value in team_data.items():
|
||||||
setattr(db_team, key, value)
|
setattr(db_team, key, value)
|
||||||
session.add(db_team)
|
session.add(db_team)
|
||||||
|
@ -83,7 +83,7 @@ def on_startup():
|
|||||||
|
|
||||||
@app.post("/heroes/", response_model=HeroRead)
|
@app.post("/heroes/", response_model=HeroRead)
|
||||||
def create_hero(*, session: Session = Depends(get_session), hero: HeroCreate):
|
def create_hero(*, session: Session = Depends(get_session), hero: HeroCreate):
|
||||||
db_hero = Hero.from_orm(hero)
|
db_hero = Hero.model_validate(hero)
|
||||||
session.add(db_hero)
|
session.add(db_hero)
|
||||||
session.commit()
|
session.commit()
|
||||||
session.refresh(db_hero)
|
session.refresh(db_hero)
|
||||||
@ -116,7 +116,7 @@ def update_hero(
|
|||||||
db_hero = session.get(Hero, hero_id)
|
db_hero = session.get(Hero, hero_id)
|
||||||
if not db_hero:
|
if not db_hero:
|
||||||
raise HTTPException(status_code=404, detail="Hero not found")
|
raise HTTPException(status_code=404, detail="Hero not found")
|
||||||
hero_data = hero.dict(exclude_unset=True)
|
hero_data = hero.model_dump(exclude_unset=True)
|
||||||
for key, value in hero_data.items():
|
for key, value in hero_data.items():
|
||||||
setattr(db_hero, key, value)
|
setattr(db_hero, key, value)
|
||||||
session.add(db_hero)
|
session.add(db_hero)
|
||||||
@ -137,7 +137,7 @@ def delete_hero(*, session: Session = Depends(get_session), hero_id: int):
|
|||||||
|
|
||||||
@app.post("/teams/", response_model=TeamRead)
|
@app.post("/teams/", response_model=TeamRead)
|
||||||
def create_team(*, session: Session = Depends(get_session), team: TeamCreate):
|
def create_team(*, session: Session = Depends(get_session), team: TeamCreate):
|
||||||
db_team = Team.from_orm(team)
|
db_team = Team.model_validate(team)
|
||||||
session.add(db_team)
|
session.add(db_team)
|
||||||
session.commit()
|
session.commit()
|
||||||
session.refresh(db_team)
|
session.refresh(db_team)
|
||||||
@ -173,7 +173,7 @@ def update_team(
|
|||||||
db_team = session.get(Team, team_id)
|
db_team = session.get(Team, team_id)
|
||||||
if not db_team:
|
if not db_team:
|
||||||
raise HTTPException(status_code=404, detail="Team not found")
|
raise HTTPException(status_code=404, detail="Team not found")
|
||||||
team_data = team.dict(exclude_unset=True)
|
team_data = team.model_dump(exclude_unset=True)
|
||||||
for key, value in team_data.items():
|
for key, value in team_data.items():
|
||||||
setattr(db_team, key, value)
|
setattr(db_team, key, value)
|
||||||
session.add(db_team)
|
session.add(db_team)
|
||||||
|
@ -50,7 +50,7 @@ def on_startup():
|
|||||||
@app.post("/heroes/", response_model=HeroRead)
|
@app.post("/heroes/", response_model=HeroRead)
|
||||||
def create_hero(hero: HeroCreate):
|
def create_hero(hero: HeroCreate):
|
||||||
with Session(engine) as session:
|
with Session(engine) as session:
|
||||||
db_hero = Hero.from_orm(hero)
|
db_hero = Hero.model_validate(hero)
|
||||||
session.add(db_hero)
|
session.add(db_hero)
|
||||||
session.commit()
|
session.commit()
|
||||||
session.refresh(db_hero)
|
session.refresh(db_hero)
|
||||||
@ -79,7 +79,7 @@ def update_hero(hero_id: int, hero: HeroUpdate):
|
|||||||
db_hero = session.get(Hero, hero_id)
|
db_hero = session.get(Hero, hero_id)
|
||||||
if not db_hero:
|
if not db_hero:
|
||||||
raise HTTPException(status_code=404, detail="Hero not found")
|
raise HTTPException(status_code=404, detail="Hero not found")
|
||||||
hero_data = hero.dict(exclude_unset=True)
|
hero_data = hero.model_dump(exclude_unset=True)
|
||||||
for key, value in hero_data.items():
|
for key, value in hero_data.items():
|
||||||
setattr(db_hero, key, value)
|
setattr(db_hero, key, value)
|
||||||
session.add(db_hero)
|
session.add(db_hero)
|
||||||
|
@ -48,7 +48,7 @@ def on_startup():
|
|||||||
@app.post("/heroes/", response_model=HeroRead)
|
@app.post("/heroes/", response_model=HeroRead)
|
||||||
def create_hero(hero: HeroCreate):
|
def create_hero(hero: HeroCreate):
|
||||||
with Session(engine) as session:
|
with Session(engine) as session:
|
||||||
db_hero = Hero.from_orm(hero)
|
db_hero = Hero.model_validate(hero)
|
||||||
session.add(db_hero)
|
session.add(db_hero)
|
||||||
session.commit()
|
session.commit()
|
||||||
session.refresh(db_hero)
|
session.refresh(db_hero)
|
||||||
@ -77,7 +77,7 @@ def update_hero(hero_id: int, hero: HeroUpdate):
|
|||||||
db_hero = session.get(Hero, hero_id)
|
db_hero = session.get(Hero, hero_id)
|
||||||
if not db_hero:
|
if not db_hero:
|
||||||
raise HTTPException(status_code=404, detail="Hero not found")
|
raise HTTPException(status_code=404, detail="Hero not found")
|
||||||
hero_data = hero.dict(exclude_unset=True)
|
hero_data = hero.model_dump(exclude_unset=True)
|
||||||
for key, value in hero_data.items():
|
for key, value in hero_data.items():
|
||||||
setattr(db_hero, key, value)
|
setattr(db_hero, key, value)
|
||||||
session.add(db_hero)
|
session.add(db_hero)
|
||||||
|
@ -50,7 +50,7 @@ def on_startup():
|
|||||||
@app.post("/heroes/", response_model=HeroRead)
|
@app.post("/heroes/", response_model=HeroRead)
|
||||||
def create_hero(hero: HeroCreate):
|
def create_hero(hero: HeroCreate):
|
||||||
with Session(engine) as session:
|
with Session(engine) as session:
|
||||||
db_hero = Hero.from_orm(hero)
|
db_hero = Hero.model_validate(hero)
|
||||||
session.add(db_hero)
|
session.add(db_hero)
|
||||||
session.commit()
|
session.commit()
|
||||||
session.refresh(db_hero)
|
session.refresh(db_hero)
|
||||||
@ -79,7 +79,7 @@ def update_hero(hero_id: int, hero: HeroUpdate):
|
|||||||
db_hero = session.get(Hero, hero_id)
|
db_hero = session.get(Hero, hero_id)
|
||||||
if not db_hero:
|
if not db_hero:
|
||||||
raise HTTPException(status_code=404, detail="Hero not found")
|
raise HTTPException(status_code=404, detail="Hero not found")
|
||||||
hero_data = hero.dict(exclude_unset=True)
|
hero_data = hero.model_dump(exclude_unset=True)
|
||||||
for key, value in hero_data.items():
|
for key, value in hero_data.items():
|
||||||
setattr(db_hero, key, value)
|
setattr(db_hero, key, value)
|
||||||
session.add(db_hero)
|
session.add(db_hero)
|
||||||
|
@ -34,7 +34,7 @@ classifiers = [
|
|||||||
[tool.poetry.dependencies]
|
[tool.poetry.dependencies]
|
||||||
python = "^3.7"
|
python = "^3.7"
|
||||||
SQLAlchemy = ">=2.0.0,<2.1.0"
|
SQLAlchemy = ">=2.0.0,<2.1.0"
|
||||||
pydantic = "^1.9.0"
|
pydantic = ">=1.10.13,<3.0.0"
|
||||||
|
|
||||||
[tool.poetry.group.dev.dependencies]
|
[tool.poetry.group.dev.dependencies]
|
||||||
pytest = "^7.0.1"
|
pytest = "^7.0.1"
|
||||||
@ -50,6 +50,8 @@ fastapi = "^0.103.2"
|
|||||||
ruff = "^0.1.2"
|
ruff = "^0.1.2"
|
||||||
# For FastAPI tests
|
# For FastAPI tests
|
||||||
httpx = "0.24.1"
|
httpx = "0.24.1"
|
||||||
|
# TODO: upgrade when deprecating Python 3.7
|
||||||
|
dirty-equals = "^0.6.0"
|
||||||
typer-cli = "^0.0.13"
|
typer-cli = "^0.0.13"
|
||||||
mkdocs-markdownextradata-plugin = ">=0.1.7,<0.3.0"
|
mkdocs-markdownextradata-plugin = ">=0.1.7,<0.3.0"
|
||||||
|
|
||||||
|
554
sqlmodel/_compat.py
Normal file
554
sqlmodel/_compat.py
Normal file
@ -0,0 +1,554 @@
|
|||||||
|
import types
|
||||||
|
from contextlib import contextmanager
|
||||||
|
from contextvars import ContextVar
|
||||||
|
from dataclasses import dataclass
|
||||||
|
from typing import (
|
||||||
|
TYPE_CHECKING,
|
||||||
|
AbstractSet,
|
||||||
|
Any,
|
||||||
|
Dict,
|
||||||
|
ForwardRef,
|
||||||
|
Generator,
|
||||||
|
Mapping,
|
||||||
|
Optional,
|
||||||
|
Set,
|
||||||
|
Type,
|
||||||
|
TypeVar,
|
||||||
|
Union,
|
||||||
|
)
|
||||||
|
|
||||||
|
from pydantic import VERSION as PYDANTIC_VERSION
|
||||||
|
from pydantic.fields import FieldInfo
|
||||||
|
from typing_extensions import get_args, get_origin
|
||||||
|
|
||||||
|
IS_PYDANTIC_V2 = PYDANTIC_VERSION.startswith("2.")
|
||||||
|
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from .main import RelationshipInfo, SQLModel
|
||||||
|
|
||||||
|
UnionType = getattr(types, "UnionType", Union)
|
||||||
|
NoneType = type(None)
|
||||||
|
T = TypeVar("T")
|
||||||
|
InstanceOrType = Union[T, Type[T]]
|
||||||
|
_TSQLModel = TypeVar("_TSQLModel", bound="SQLModel")
|
||||||
|
|
||||||
|
|
||||||
|
class FakeMetadata:
|
||||||
|
max_length: Optional[int] = None
|
||||||
|
max_digits: Optional[int] = None
|
||||||
|
decimal_places: Optional[int] = None
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class ObjectWithUpdateWrapper:
|
||||||
|
obj: Any
|
||||||
|
update: Dict[str, Any]
|
||||||
|
|
||||||
|
def __getattribute__(self, __name: str) -> Any:
|
||||||
|
if __name in self.update:
|
||||||
|
return self.update[__name]
|
||||||
|
return getattr(self.obj, __name)
|
||||||
|
|
||||||
|
|
||||||
|
def _is_union_type(t: Any) -> bool:
|
||||||
|
return t is UnionType or t is Union
|
||||||
|
|
||||||
|
|
||||||
|
finish_init: ContextVar[bool] = ContextVar("finish_init", default=True)
|
||||||
|
|
||||||
|
|
||||||
|
@contextmanager
|
||||||
|
def partial_init() -> Generator[None, None, None]:
|
||||||
|
token = finish_init.set(False)
|
||||||
|
yield
|
||||||
|
finish_init.reset(token)
|
||||||
|
|
||||||
|
|
||||||
|
if IS_PYDANTIC_V2:
|
||||||
|
from pydantic import ConfigDict as BaseConfig
|
||||||
|
from pydantic._internal._fields import PydanticMetadata
|
||||||
|
from pydantic._internal._model_construction import ModelMetaclass
|
||||||
|
from pydantic._internal._repr import Representation as Representation
|
||||||
|
from pydantic_core import PydanticUndefined as Undefined
|
||||||
|
from pydantic_core import PydanticUndefinedType as UndefinedType
|
||||||
|
|
||||||
|
# Dummy for types, to make it importable
|
||||||
|
class ModelField:
|
||||||
|
pass
|
||||||
|
|
||||||
|
class SQLModelConfig(BaseConfig, total=False):
|
||||||
|
table: Optional[bool]
|
||||||
|
registry: Optional[Any]
|
||||||
|
|
||||||
|
def get_config_value(
|
||||||
|
*, model: InstanceOrType["SQLModel"], parameter: str, default: Any = None
|
||||||
|
) -> Any:
|
||||||
|
return model.model_config.get(parameter, default)
|
||||||
|
|
||||||
|
def set_config_value(
|
||||||
|
*,
|
||||||
|
model: InstanceOrType["SQLModel"],
|
||||||
|
parameter: str,
|
||||||
|
value: Any,
|
||||||
|
) -> None:
|
||||||
|
model.model_config[parameter] = value # type: ignore[literal-required]
|
||||||
|
|
||||||
|
def get_model_fields(model: InstanceOrType["SQLModel"]) -> Dict[str, "FieldInfo"]:
|
||||||
|
return model.model_fields
|
||||||
|
|
||||||
|
def set_fields_set(
|
||||||
|
new_object: InstanceOrType["SQLModel"], fields: Set["FieldInfo"]
|
||||||
|
) -> None:
|
||||||
|
object.__setattr__(new_object, "__pydantic_fields_set__", fields)
|
||||||
|
|
||||||
|
def get_annotations(class_dict: Dict[str, Any]) -> Dict[str, Any]:
|
||||||
|
return class_dict.get("__annotations__", {})
|
||||||
|
|
||||||
|
def is_table_model_class(cls: Type[Any]) -> bool:
|
||||||
|
config = getattr(cls, "model_config", {})
|
||||||
|
if config:
|
||||||
|
return config.get("table", False) or False
|
||||||
|
return False
|
||||||
|
|
||||||
|
def get_relationship_to(
|
||||||
|
name: str,
|
||||||
|
rel_info: "RelationshipInfo",
|
||||||
|
annotation: Any,
|
||||||
|
) -> Any:
|
||||||
|
origin = get_origin(annotation)
|
||||||
|
use_annotation = annotation
|
||||||
|
# Direct relationships (e.g. 'Team' or Team) have None as an origin
|
||||||
|
if origin is None:
|
||||||
|
if isinstance(use_annotation, ForwardRef):
|
||||||
|
use_annotation = use_annotation.__forward_arg__
|
||||||
|
else:
|
||||||
|
return use_annotation
|
||||||
|
# If Union (e.g. Optional), get the real field
|
||||||
|
elif _is_union_type(origin):
|
||||||
|
use_annotation = get_args(annotation)
|
||||||
|
if len(use_annotation) > 2:
|
||||||
|
raise ValueError(
|
||||||
|
"Cannot have a (non-optional) union as a SQLAlchemy field"
|
||||||
|
)
|
||||||
|
arg1, arg2 = use_annotation
|
||||||
|
if arg1 is NoneType and arg2 is not NoneType:
|
||||||
|
use_annotation = arg2
|
||||||
|
elif arg2 is NoneType and arg1 is not NoneType:
|
||||||
|
use_annotation = arg1
|
||||||
|
else:
|
||||||
|
raise ValueError(
|
||||||
|
"Cannot have a Union of None and None as a SQLAlchemy field"
|
||||||
|
)
|
||||||
|
|
||||||
|
# If a list, then also get the real field
|
||||||
|
elif origin is list:
|
||||||
|
use_annotation = get_args(annotation)[0]
|
||||||
|
|
||||||
|
return get_relationship_to(
|
||||||
|
name=name, rel_info=rel_info, annotation=use_annotation
|
||||||
|
)
|
||||||
|
|
||||||
|
def is_field_noneable(field: "FieldInfo") -> bool:
|
||||||
|
if getattr(field, "nullable", Undefined) is not Undefined:
|
||||||
|
return field.nullable # type: ignore
|
||||||
|
origin = get_origin(field.annotation)
|
||||||
|
if origin is not None and _is_union_type(origin):
|
||||||
|
args = get_args(field.annotation)
|
||||||
|
if any(arg is NoneType for arg in args):
|
||||||
|
return True
|
||||||
|
if not field.is_required():
|
||||||
|
if field.default is Undefined:
|
||||||
|
return False
|
||||||
|
if field.annotation is None or field.annotation is NoneType: # type: ignore[comparison-overlap]
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
return False
|
||||||
|
|
||||||
|
def get_type_from_field(field: Any) -> Any:
|
||||||
|
type_: Any = field.annotation
|
||||||
|
# Resolve Optional fields
|
||||||
|
if type_ is None:
|
||||||
|
raise ValueError("Missing field type")
|
||||||
|
origin = get_origin(type_)
|
||||||
|
if origin is None:
|
||||||
|
return type_
|
||||||
|
if _is_union_type(origin):
|
||||||
|
bases = get_args(type_)
|
||||||
|
if len(bases) > 2:
|
||||||
|
raise ValueError(
|
||||||
|
"Cannot have a (non-optional) union as a SQLAlchemy field"
|
||||||
|
)
|
||||||
|
# Non optional unions are not allowed
|
||||||
|
if bases[0] is not NoneType and bases[1] is not NoneType:
|
||||||
|
raise ValueError(
|
||||||
|
"Cannot have a (non-optional) union as a SQLlchemy field"
|
||||||
|
)
|
||||||
|
# Optional unions are allowed
|
||||||
|
return bases[0] if bases[0] is not NoneType else bases[1]
|
||||||
|
return origin
|
||||||
|
|
||||||
|
def get_field_metadata(field: Any) -> Any:
|
||||||
|
for meta in field.metadata:
|
||||||
|
if isinstance(meta, PydanticMetadata):
|
||||||
|
return meta
|
||||||
|
return FakeMetadata()
|
||||||
|
|
||||||
|
def post_init_field_info(field_info: FieldInfo) -> None:
|
||||||
|
return None
|
||||||
|
|
||||||
|
# Dummy to make it importable
|
||||||
|
def _calculate_keys(
|
||||||
|
self: "SQLModel",
|
||||||
|
include: Optional[Mapping[Union[int, str], Any]],
|
||||||
|
exclude: Optional[Mapping[Union[int, str], Any]],
|
||||||
|
exclude_unset: bool,
|
||||||
|
update: Optional[Dict[str, Any]] = None,
|
||||||
|
) -> Optional[AbstractSet[str]]: # pragma: no cover
|
||||||
|
return None
|
||||||
|
|
||||||
|
def sqlmodel_table_construct(
|
||||||
|
*,
|
||||||
|
self_instance: _TSQLModel,
|
||||||
|
values: Dict[str, Any],
|
||||||
|
_fields_set: Union[Set[str], None] = None,
|
||||||
|
) -> _TSQLModel:
|
||||||
|
# Copy from Pydantic's BaseModel.construct()
|
||||||
|
# Ref: https://github.com/pydantic/pydantic/blob/v2.5.2/pydantic/main.py#L198
|
||||||
|
# Modified to not include everything, only the model fields, and to
|
||||||
|
# set relationships
|
||||||
|
# SQLModel override to get class SQLAlchemy __dict__ attributes and
|
||||||
|
# set them back in after creating the object
|
||||||
|
# new_obj = cls.__new__(cls)
|
||||||
|
cls = type(self_instance)
|
||||||
|
old_dict = self_instance.__dict__.copy()
|
||||||
|
# End SQLModel override
|
||||||
|
|
||||||
|
fields_values: Dict[str, Any] = {}
|
||||||
|
defaults: Dict[
|
||||||
|
str, Any
|
||||||
|
] = {} # keeping this separate from `fields_values` helps us compute `_fields_set`
|
||||||
|
for name, field in cls.model_fields.items():
|
||||||
|
if field.alias and field.alias in values:
|
||||||
|
fields_values[name] = values.pop(field.alias)
|
||||||
|
elif name in values:
|
||||||
|
fields_values[name] = values.pop(name)
|
||||||
|
elif not field.is_required():
|
||||||
|
defaults[name] = field.get_default(call_default_factory=True)
|
||||||
|
if _fields_set is None:
|
||||||
|
_fields_set = set(fields_values.keys())
|
||||||
|
fields_values.update(defaults)
|
||||||
|
|
||||||
|
_extra: Union[Dict[str, Any], None] = None
|
||||||
|
if cls.model_config.get("extra") == "allow":
|
||||||
|
_extra = {}
|
||||||
|
for k, v in values.items():
|
||||||
|
_extra[k] = v
|
||||||
|
# SQLModel override, do not include everything, only the model fields
|
||||||
|
# else:
|
||||||
|
# fields_values.update(values)
|
||||||
|
# End SQLModel override
|
||||||
|
# SQLModel override
|
||||||
|
# Do not set __dict__, instead use setattr to trigger SQLAlchemy
|
||||||
|
# object.__setattr__(new_obj, "__dict__", fields_values)
|
||||||
|
# instrumentation
|
||||||
|
for key, value in {**old_dict, **fields_values}.items():
|
||||||
|
setattr(self_instance, key, value)
|
||||||
|
# End SQLModel override
|
||||||
|
object.__setattr__(self_instance, "__pydantic_fields_set__", _fields_set)
|
||||||
|
if not cls.__pydantic_root_model__:
|
||||||
|
object.__setattr__(self_instance, "__pydantic_extra__", _extra)
|
||||||
|
|
||||||
|
if cls.__pydantic_post_init__:
|
||||||
|
self_instance.model_post_init(None)
|
||||||
|
elif not cls.__pydantic_root_model__:
|
||||||
|
# Note: if there are any private attributes, cls.__pydantic_post_init__ would exist
|
||||||
|
# Since it doesn't, that means that `__pydantic_private__` should be set to None
|
||||||
|
object.__setattr__(self_instance, "__pydantic_private__", None)
|
||||||
|
# SQLModel override, set relationships
|
||||||
|
# Get and set any relationship objects
|
||||||
|
for key in self_instance.__sqlmodel_relationships__:
|
||||||
|
value = values.get(key, Undefined)
|
||||||
|
if value is not Undefined:
|
||||||
|
setattr(self_instance, key, value)
|
||||||
|
# End SQLModel override
|
||||||
|
return self_instance
|
||||||
|
|
||||||
|
def sqlmodel_validate(
|
||||||
|
cls: Type[_TSQLModel],
|
||||||
|
obj: Any,
|
||||||
|
*,
|
||||||
|
strict: Union[bool, None] = None,
|
||||||
|
from_attributes: Union[bool, None] = None,
|
||||||
|
context: Union[Dict[str, Any], None] = None,
|
||||||
|
update: Union[Dict[str, Any], None] = None,
|
||||||
|
) -> _TSQLModel:
|
||||||
|
if not is_table_model_class(cls):
|
||||||
|
new_obj: _TSQLModel = cls.__new__(cls)
|
||||||
|
else:
|
||||||
|
# If table, create the new instance normally to make SQLAlchemy create
|
||||||
|
# the _sa_instance_state attribute
|
||||||
|
# The wrapper of this function should use with _partial_init()
|
||||||
|
with partial_init():
|
||||||
|
new_obj = cls()
|
||||||
|
# SQLModel Override to get class SQLAlchemy __dict__ attributes and
|
||||||
|
# set them back in after creating the object
|
||||||
|
old_dict = new_obj.__dict__.copy()
|
||||||
|
use_obj = obj
|
||||||
|
if isinstance(obj, dict) and update:
|
||||||
|
use_obj = {**obj, **update}
|
||||||
|
elif update:
|
||||||
|
use_obj = ObjectWithUpdateWrapper(obj=obj, update=update)
|
||||||
|
cls.__pydantic_validator__.validate_python(
|
||||||
|
use_obj,
|
||||||
|
strict=strict,
|
||||||
|
from_attributes=from_attributes,
|
||||||
|
context=context,
|
||||||
|
self_instance=new_obj,
|
||||||
|
)
|
||||||
|
# Capture fields set to restore it later
|
||||||
|
fields_set = new_obj.__pydantic_fields_set__.copy()
|
||||||
|
if not is_table_model_class(cls):
|
||||||
|
# If not table, normal Pydantic code, set __dict__
|
||||||
|
new_obj.__dict__ = {**old_dict, **new_obj.__dict__}
|
||||||
|
else:
|
||||||
|
# Do not set __dict__, instead use setattr to trigger SQLAlchemy
|
||||||
|
# instrumentation
|
||||||
|
for key, value in {**old_dict, **new_obj.__dict__}.items():
|
||||||
|
setattr(new_obj, key, value)
|
||||||
|
# Restore fields set
|
||||||
|
object.__setattr__(new_obj, "__pydantic_fields_set__", fields_set)
|
||||||
|
# Get and set any relationship objects
|
||||||
|
if is_table_model_class(cls):
|
||||||
|
for key in new_obj.__sqlmodel_relationships__:
|
||||||
|
value = getattr(use_obj, key, Undefined)
|
||||||
|
if value is not Undefined:
|
||||||
|
setattr(new_obj, key, value)
|
||||||
|
return new_obj
|
||||||
|
|
||||||
|
def sqlmodel_init(*, self: "SQLModel", data: Dict[str, Any]) -> None:
|
||||||
|
old_dict = self.__dict__.copy()
|
||||||
|
if not is_table_model_class(self.__class__):
|
||||||
|
self.__pydantic_validator__.validate_python(
|
||||||
|
data,
|
||||||
|
self_instance=self,
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
sqlmodel_table_construct(
|
||||||
|
self_instance=self,
|
||||||
|
values=data,
|
||||||
|
)
|
||||||
|
object.__setattr__(
|
||||||
|
self,
|
||||||
|
"__dict__",
|
||||||
|
{**old_dict, **self.__dict__},
|
||||||
|
)
|
||||||
|
|
||||||
|
else:
|
||||||
|
from pydantic import BaseConfig as BaseConfig # type: ignore[assignment]
|
||||||
|
from pydantic.errors import ConfigError
|
||||||
|
from pydantic.fields import ( # type: ignore[attr-defined, no-redef]
|
||||||
|
SHAPE_SINGLETON,
|
||||||
|
ModelField,
|
||||||
|
)
|
||||||
|
from pydantic.fields import ( # type: ignore[attr-defined, no-redef]
|
||||||
|
Undefined as Undefined, # noqa
|
||||||
|
)
|
||||||
|
from pydantic.fields import ( # type: ignore[attr-defined, no-redef]
|
||||||
|
UndefinedType as UndefinedType,
|
||||||
|
)
|
||||||
|
from pydantic.main import ( # type: ignore[no-redef]
|
||||||
|
ModelMetaclass as ModelMetaclass,
|
||||||
|
)
|
||||||
|
from pydantic.main import validate_model
|
||||||
|
from pydantic.typing import resolve_annotations
|
||||||
|
from pydantic.utils import ROOT_KEY, ValueItems
|
||||||
|
from pydantic.utils import ( # type: ignore[no-redef]
|
||||||
|
Representation as Representation,
|
||||||
|
)
|
||||||
|
|
||||||
|
class SQLModelConfig(BaseConfig): # type: ignore[no-redef]
|
||||||
|
table: Optional[bool] = None # type: ignore[misc]
|
||||||
|
registry: Optional[Any] = None # type: ignore[misc]
|
||||||
|
|
||||||
|
def get_config_value(
|
||||||
|
*, model: InstanceOrType["SQLModel"], parameter: str, default: Any = None
|
||||||
|
) -> Any:
|
||||||
|
return getattr(model.__config__, parameter, default) # type: ignore[union-attr]
|
||||||
|
|
||||||
|
def set_config_value(
|
||||||
|
*,
|
||||||
|
model: InstanceOrType["SQLModel"],
|
||||||
|
parameter: str,
|
||||||
|
value: Any,
|
||||||
|
) -> None:
|
||||||
|
setattr(model.__config__, parameter, value) # type: ignore
|
||||||
|
|
||||||
|
def get_model_fields(model: InstanceOrType["SQLModel"]) -> Dict[str, "FieldInfo"]:
|
||||||
|
return model.__fields__ # type: ignore
|
||||||
|
|
||||||
|
def set_fields_set(
|
||||||
|
new_object: InstanceOrType["SQLModel"], fields: Set["FieldInfo"]
|
||||||
|
) -> None:
|
||||||
|
object.__setattr__(new_object, "__fields_set__", fields)
|
||||||
|
|
||||||
|
def get_annotations(class_dict: Dict[str, Any]) -> Dict[str, Any]:
|
||||||
|
return resolve_annotations( # type: ignore[no-any-return]
|
||||||
|
class_dict.get("__annotations__", {}),
|
||||||
|
class_dict.get("__module__", None),
|
||||||
|
)
|
||||||
|
|
||||||
|
def is_table_model_class(cls: Type[Any]) -> bool:
|
||||||
|
config = getattr(cls, "__config__", None)
|
||||||
|
if config:
|
||||||
|
return getattr(config, "table", False)
|
||||||
|
return False
|
||||||
|
|
||||||
|
def get_relationship_to(
|
||||||
|
name: str,
|
||||||
|
rel_info: "RelationshipInfo",
|
||||||
|
annotation: Any,
|
||||||
|
) -> Any:
|
||||||
|
temp_field = ModelField.infer( # type: ignore[attr-defined]
|
||||||
|
name=name,
|
||||||
|
value=rel_info,
|
||||||
|
annotation=annotation,
|
||||||
|
class_validators=None,
|
||||||
|
config=SQLModelConfig,
|
||||||
|
)
|
||||||
|
relationship_to = temp_field.type_
|
||||||
|
if isinstance(temp_field.type_, ForwardRef):
|
||||||
|
relationship_to = temp_field.type_.__forward_arg__
|
||||||
|
return relationship_to
|
||||||
|
|
||||||
|
def is_field_noneable(field: "FieldInfo") -> bool:
|
||||||
|
if not field.required: # type: ignore[attr-defined]
|
||||||
|
# Taken from [Pydantic](https://github.com/samuelcolvin/pydantic/blob/v1.8.2/pydantic/fields.py#L946-L947)
|
||||||
|
return field.allow_none and ( # type: ignore[attr-defined]
|
||||||
|
field.shape != SHAPE_SINGLETON or not field.sub_fields # type: ignore[attr-defined]
|
||||||
|
)
|
||||||
|
return field.allow_none # type: ignore[no-any-return, attr-defined]
|
||||||
|
|
||||||
|
def get_type_from_field(field: Any) -> Any:
|
||||||
|
if isinstance(field.type_, type) and field.shape == SHAPE_SINGLETON:
|
||||||
|
return field.type_
|
||||||
|
raise ValueError(f"The field {field.name} has no matching SQLAlchemy type")
|
||||||
|
|
||||||
|
def get_field_metadata(field: Any) -> Any:
|
||||||
|
metadata = FakeMetadata()
|
||||||
|
metadata.max_length = field.field_info.max_length
|
||||||
|
metadata.max_digits = getattr(field.type_, "max_digits", None)
|
||||||
|
metadata.decimal_places = getattr(field.type_, "decimal_places", None)
|
||||||
|
return metadata
|
||||||
|
|
||||||
|
def post_init_field_info(field_info: FieldInfo) -> None:
|
||||||
|
field_info._validate() # type: ignore[attr-defined]
|
||||||
|
|
||||||
|
def _calculate_keys(
|
||||||
|
self: "SQLModel",
|
||||||
|
include: Optional[Mapping[Union[int, str], Any]],
|
||||||
|
exclude: Optional[Mapping[Union[int, str], Any]],
|
||||||
|
exclude_unset: bool,
|
||||||
|
update: Optional[Dict[str, Any]] = None,
|
||||||
|
) -> Optional[AbstractSet[str]]:
|
||||||
|
if include is None and exclude is None and not exclude_unset:
|
||||||
|
# Original in Pydantic:
|
||||||
|
# return None
|
||||||
|
# Updated to not return SQLAlchemy attributes
|
||||||
|
# Do not include relationships as that would easily lead to infinite
|
||||||
|
# recursion, or traversing the whole database
|
||||||
|
return (
|
||||||
|
self.__fields__.keys() # noqa
|
||||||
|
) # | self.__sqlmodel_relationships__.keys()
|
||||||
|
|
||||||
|
keys: AbstractSet[str]
|
||||||
|
if exclude_unset:
|
||||||
|
keys = self.__fields_set__.copy() # noqa
|
||||||
|
else:
|
||||||
|
# Original in Pydantic:
|
||||||
|
# keys = self.__dict__.keys()
|
||||||
|
# Updated to not return SQLAlchemy attributes
|
||||||
|
# Do not include relationships as that would easily lead to infinite
|
||||||
|
# recursion, or traversing the whole database
|
||||||
|
keys = (
|
||||||
|
self.__fields__.keys() # noqa
|
||||||
|
) # | self.__sqlmodel_relationships__.keys()
|
||||||
|
if include is not None:
|
||||||
|
keys &= include.keys()
|
||||||
|
|
||||||
|
if update:
|
||||||
|
keys -= update.keys()
|
||||||
|
|
||||||
|
if exclude:
|
||||||
|
keys -= {k for k, v in exclude.items() if ValueItems.is_true(v)}
|
||||||
|
|
||||||
|
return keys
|
||||||
|
|
||||||
|
def sqlmodel_validate(
|
||||||
|
cls: Type[_TSQLModel],
|
||||||
|
obj: Any,
|
||||||
|
*,
|
||||||
|
strict: Union[bool, None] = None,
|
||||||
|
from_attributes: Union[bool, None] = None,
|
||||||
|
context: Union[Dict[str, Any], None] = None,
|
||||||
|
update: Union[Dict[str, Any], None] = None,
|
||||||
|
) -> _TSQLModel:
|
||||||
|
# This was SQLModel's original from_orm() for Pydantic v1
|
||||||
|
# Duplicated from Pydantic
|
||||||
|
if not cls.__config__.orm_mode: # type: ignore[attr-defined] # noqa
|
||||||
|
raise ConfigError(
|
||||||
|
"You must have the config attribute orm_mode=True to use from_orm"
|
||||||
|
)
|
||||||
|
if not isinstance(obj, Mapping):
|
||||||
|
obj = (
|
||||||
|
{ROOT_KEY: obj}
|
||||||
|
if cls.__custom_root_type__ # type: ignore[attr-defined] # noqa
|
||||||
|
else cls._decompose_class(obj) # type: ignore[attr-defined] # noqa
|
||||||
|
)
|
||||||
|
# SQLModel, support update dict
|
||||||
|
if update is not None:
|
||||||
|
obj = {**obj, **update}
|
||||||
|
# End SQLModel support dict
|
||||||
|
if not getattr(cls.__config__, "table", False): # noqa
|
||||||
|
# If not table, normal Pydantic code
|
||||||
|
m: _TSQLModel = cls.__new__(cls)
|
||||||
|
else:
|
||||||
|
# If table, create the new instance normally to make SQLAlchemy create
|
||||||
|
# the _sa_instance_state attribute
|
||||||
|
m = cls()
|
||||||
|
values, fields_set, validation_error = validate_model(cls, obj)
|
||||||
|
if validation_error:
|
||||||
|
raise validation_error
|
||||||
|
# Updated to trigger SQLAlchemy internal handling
|
||||||
|
if not getattr(cls.__config__, "table", False): # noqa
|
||||||
|
object.__setattr__(m, "__dict__", values)
|
||||||
|
else:
|
||||||
|
for key, value in values.items():
|
||||||
|
setattr(m, key, value)
|
||||||
|
# Continue with standard Pydantic logic
|
||||||
|
object.__setattr__(m, "__fields_set__", fields_set)
|
||||||
|
m._init_private_attributes() # type: ignore[attr-defined] # noqa
|
||||||
|
return m
|
||||||
|
|
||||||
|
def sqlmodel_init(*, self: "SQLModel", data: Dict[str, Any]) -> None:
|
||||||
|
values, fields_set, validation_error = validate_model(self.__class__, data)
|
||||||
|
# Only raise errors if not a SQLModel model
|
||||||
|
if (
|
||||||
|
not is_table_model_class(self.__class__) # noqa
|
||||||
|
and validation_error
|
||||||
|
):
|
||||||
|
raise validation_error
|
||||||
|
if not is_table_model_class(self.__class__):
|
||||||
|
object.__setattr__(self, "__dict__", values)
|
||||||
|
else:
|
||||||
|
# Do not set values as in Pydantic, pass them through setattr, so
|
||||||
|
# SQLAlchemy can handle them
|
||||||
|
for key, value in values.items():
|
||||||
|
setattr(self, key, value)
|
||||||
|
object.__setattr__(self, "__fields_set__", fields_set)
|
||||||
|
non_pydantic_keys = data.keys() - values.keys()
|
||||||
|
|
||||||
|
if is_table_model_class(self.__class__):
|
||||||
|
for key in non_pydantic_keys:
|
||||||
|
if key in self.__sqlmodel_relationships__:
|
||||||
|
setattr(self, key, data[key])
|
517
sqlmodel/main.py
517
sqlmodel/main.py
@ -11,7 +11,6 @@ from typing import (
|
|||||||
Callable,
|
Callable,
|
||||||
ClassVar,
|
ClassVar,
|
||||||
Dict,
|
Dict,
|
||||||
ForwardRef,
|
|
||||||
List,
|
List,
|
||||||
Mapping,
|
Mapping,
|
||||||
Optional,
|
Optional,
|
||||||
@ -25,13 +24,8 @@ from typing import (
|
|||||||
overload,
|
overload,
|
||||||
)
|
)
|
||||||
|
|
||||||
from pydantic import BaseConfig, BaseModel
|
from pydantic import BaseModel
|
||||||
from pydantic.errors import ConfigError, DictError
|
|
||||||
from pydantic.fields import SHAPE_SINGLETON, ModelField, Undefined, UndefinedType
|
|
||||||
from pydantic.fields import FieldInfo as PydanticFieldInfo
|
from pydantic.fields import FieldInfo as PydanticFieldInfo
|
||||||
from pydantic.main import ModelMetaclass, validate_model
|
|
||||||
from pydantic.typing import NoArgAnyCallable, resolve_annotations
|
|
||||||
from pydantic.utils import ROOT_KEY, Representation
|
|
||||||
from sqlalchemy import (
|
from sqlalchemy import (
|
||||||
Boolean,
|
Boolean,
|
||||||
Column,
|
Column,
|
||||||
@ -57,11 +51,38 @@ from sqlalchemy.orm.decl_api import DeclarativeMeta
|
|||||||
from sqlalchemy.orm.instrumentation import is_instrumented
|
from sqlalchemy.orm.instrumentation import is_instrumented
|
||||||
from sqlalchemy.sql.schema import MetaData
|
from sqlalchemy.sql.schema import MetaData
|
||||||
from sqlalchemy.sql.sqltypes import LargeBinary, Time
|
from sqlalchemy.sql.sqltypes import LargeBinary, Time
|
||||||
from typing_extensions import get_origin
|
from typing_extensions import Literal, deprecated, get_origin
|
||||||
|
|
||||||
|
from ._compat import ( # type: ignore[attr-defined]
|
||||||
|
IS_PYDANTIC_V2,
|
||||||
|
BaseConfig,
|
||||||
|
ModelField,
|
||||||
|
ModelMetaclass,
|
||||||
|
Representation,
|
||||||
|
SQLModelConfig,
|
||||||
|
Undefined,
|
||||||
|
UndefinedType,
|
||||||
|
_calculate_keys,
|
||||||
|
finish_init,
|
||||||
|
get_annotations,
|
||||||
|
get_config_value,
|
||||||
|
get_field_metadata,
|
||||||
|
get_model_fields,
|
||||||
|
get_relationship_to,
|
||||||
|
get_type_from_field,
|
||||||
|
is_field_noneable,
|
||||||
|
is_table_model_class,
|
||||||
|
post_init_field_info,
|
||||||
|
set_config_value,
|
||||||
|
set_fields_set,
|
||||||
|
sqlmodel_init,
|
||||||
|
sqlmodel_validate,
|
||||||
|
)
|
||||||
from .sql.sqltypes import GUID, AutoString
|
from .sql.sqltypes import GUID, AutoString
|
||||||
|
|
||||||
_T = TypeVar("_T")
|
_T = TypeVar("_T")
|
||||||
|
NoArgAnyCallable = Callable[[], Any]
|
||||||
|
IncEx = Union[Set[int], Set[str], Dict[int, Any], Dict[str, Any], None]
|
||||||
|
|
||||||
|
|
||||||
def __dataclass_transform__(
|
def __dataclass_transform__(
|
||||||
@ -321,7 +342,7 @@ def Field(
|
|||||||
sa_column_kwargs=sa_column_kwargs,
|
sa_column_kwargs=sa_column_kwargs,
|
||||||
**current_schema_extra,
|
**current_schema_extra,
|
||||||
)
|
)
|
||||||
field_info._validate()
|
post_init_field_info(field_info)
|
||||||
return field_info
|
return field_info
|
||||||
|
|
||||||
|
|
||||||
@ -341,7 +362,7 @@ def Relationship(
|
|||||||
*,
|
*,
|
||||||
back_populates: Optional[str] = None,
|
back_populates: Optional[str] = None,
|
||||||
link_model: Optional[Any] = None,
|
link_model: Optional[Any] = None,
|
||||||
sa_relationship: Optional[RelationshipProperty] = None, # type: ignore
|
sa_relationship: Optional[RelationshipProperty[Any]] = None,
|
||||||
) -> Any:
|
) -> Any:
|
||||||
...
|
...
|
||||||
|
|
||||||
@ -350,7 +371,7 @@ def Relationship(
|
|||||||
*,
|
*,
|
||||||
back_populates: Optional[str] = None,
|
back_populates: Optional[str] = None,
|
||||||
link_model: Optional[Any] = None,
|
link_model: Optional[Any] = None,
|
||||||
sa_relationship: Optional[RelationshipProperty] = None, # type: ignore
|
sa_relationship: Optional[RelationshipProperty[Any]] = None,
|
||||||
sa_relationship_args: Optional[Sequence[Any]] = None,
|
sa_relationship_args: Optional[Sequence[Any]] = None,
|
||||||
sa_relationship_kwargs: Optional[Mapping[str, Any]] = None,
|
sa_relationship_kwargs: Optional[Mapping[str, Any]] = None,
|
||||||
) -> Any:
|
) -> Any:
|
||||||
@ -367,18 +388,20 @@ def Relationship(
|
|||||||
@__dataclass_transform__(kw_only_default=True, field_descriptors=(Field, FieldInfo))
|
@__dataclass_transform__(kw_only_default=True, field_descriptors=(Field, FieldInfo))
|
||||||
class SQLModelMetaclass(ModelMetaclass, DeclarativeMeta):
|
class SQLModelMetaclass(ModelMetaclass, DeclarativeMeta):
|
||||||
__sqlmodel_relationships__: Dict[str, RelationshipInfo]
|
__sqlmodel_relationships__: Dict[str, RelationshipInfo]
|
||||||
__config__: Type[BaseConfig]
|
model_config: SQLModelConfig
|
||||||
__fields__: Dict[str, ModelField]
|
model_fields: Dict[str, FieldInfo]
|
||||||
|
__config__: Type[SQLModelConfig]
|
||||||
|
__fields__: Dict[str, ModelField] # type: ignore[assignment]
|
||||||
|
|
||||||
# Replicate SQLAlchemy
|
# Replicate SQLAlchemy
|
||||||
def __setattr__(cls, name: str, value: Any) -> None:
|
def __setattr__(cls, name: str, value: Any) -> None:
|
||||||
if getattr(cls.__config__, "table", False):
|
if is_table_model_class(cls):
|
||||||
DeclarativeMeta.__setattr__(cls, name, value)
|
DeclarativeMeta.__setattr__(cls, name, value)
|
||||||
else:
|
else:
|
||||||
super().__setattr__(name, value)
|
super().__setattr__(name, value)
|
||||||
|
|
||||||
def __delattr__(cls, name: str) -> None:
|
def __delattr__(cls, name: str) -> None:
|
||||||
if getattr(cls.__config__, "table", False):
|
if is_table_model_class(cls):
|
||||||
DeclarativeMeta.__delattr__(cls, name)
|
DeclarativeMeta.__delattr__(cls, name)
|
||||||
else:
|
else:
|
||||||
super().__delattr__(name)
|
super().__delattr__(name)
|
||||||
@ -393,9 +416,7 @@ class SQLModelMetaclass(ModelMetaclass, DeclarativeMeta):
|
|||||||
) -> Any:
|
) -> Any:
|
||||||
relationships: Dict[str, RelationshipInfo] = {}
|
relationships: Dict[str, RelationshipInfo] = {}
|
||||||
dict_for_pydantic = {}
|
dict_for_pydantic = {}
|
||||||
original_annotations = resolve_annotations(
|
original_annotations = get_annotations(class_dict)
|
||||||
class_dict.get("__annotations__", {}), class_dict.get("__module__", None)
|
|
||||||
)
|
|
||||||
pydantic_annotations = {}
|
pydantic_annotations = {}
|
||||||
relationship_annotations = {}
|
relationship_annotations = {}
|
||||||
for k, v in class_dict.items():
|
for k, v in class_dict.items():
|
||||||
@ -424,10 +445,8 @@ class SQLModelMetaclass(ModelMetaclass, DeclarativeMeta):
|
|||||||
key.startswith("__") and key.endswith("__")
|
key.startswith("__") and key.endswith("__")
|
||||||
) # skip dunder methods and attributes
|
) # skip dunder methods and attributes
|
||||||
}
|
}
|
||||||
pydantic_kwargs = kwargs.copy()
|
|
||||||
config_kwargs = {
|
config_kwargs = {
|
||||||
key: pydantic_kwargs.pop(key)
|
key: kwargs[key] for key in kwargs.keys() & allowed_config_kwargs
|
||||||
for key in pydantic_kwargs.keys() & allowed_config_kwargs
|
|
||||||
}
|
}
|
||||||
new_cls = super().__new__(cls, name, bases, dict_used, **config_kwargs)
|
new_cls = super().__new__(cls, name, bases, dict_used, **config_kwargs)
|
||||||
new_cls.__annotations__ = {
|
new_cls.__annotations__ = {
|
||||||
@ -437,7 +456,9 @@ class SQLModelMetaclass(ModelMetaclass, DeclarativeMeta):
|
|||||||
}
|
}
|
||||||
|
|
||||||
def get_config(name: str) -> Any:
|
def get_config(name: str) -> Any:
|
||||||
config_class_value = getattr(new_cls.__config__, name, Undefined)
|
config_class_value = get_config_value(
|
||||||
|
model=new_cls, parameter=name, default=Undefined
|
||||||
|
)
|
||||||
if config_class_value is not Undefined:
|
if config_class_value is not Undefined:
|
||||||
return config_class_value
|
return config_class_value
|
||||||
kwarg_value = kwargs.get(name, Undefined)
|
kwarg_value = kwargs.get(name, Undefined)
|
||||||
@ -448,22 +469,27 @@ class SQLModelMetaclass(ModelMetaclass, DeclarativeMeta):
|
|||||||
config_table = get_config("table")
|
config_table = get_config("table")
|
||||||
if config_table is True:
|
if config_table is True:
|
||||||
# If it was passed by kwargs, ensure it's also set in config
|
# If it was passed by kwargs, ensure it's also set in config
|
||||||
new_cls.__config__.table = config_table
|
set_config_value(model=new_cls, parameter="table", value=config_table)
|
||||||
for k, v in new_cls.__fields__.items():
|
for k, v in get_model_fields(new_cls).items():
|
||||||
col = get_column_from_field(v)
|
col = get_column_from_field(v)
|
||||||
setattr(new_cls, k, col)
|
setattr(new_cls, k, col)
|
||||||
# Set a config flag to tell FastAPI that this should be read with a field
|
# Set a config flag to tell FastAPI that this should be read with a field
|
||||||
# in orm_mode instead of preemptively converting it to a dict.
|
# in orm_mode instead of preemptively converting it to a dict.
|
||||||
# This could be done by reading new_cls.__config__.table in FastAPI, but
|
# This could be done by reading new_cls.model_config['table'] in FastAPI, but
|
||||||
# that's very specific about SQLModel, so let's have another config that
|
# that's very specific about SQLModel, so let's have another config that
|
||||||
# other future tools based on Pydantic can use.
|
# other future tools based on Pydantic can use.
|
||||||
new_cls.__config__.read_with_orm_mode = True
|
set_config_value(
|
||||||
|
model=new_cls, parameter="read_from_attributes", value=True
|
||||||
|
)
|
||||||
|
# For compatibility with older versions
|
||||||
|
# TODO: remove this in the future
|
||||||
|
set_config_value(model=new_cls, parameter="read_with_orm_mode", value=True)
|
||||||
|
|
||||||
config_registry = get_config("registry")
|
config_registry = get_config("registry")
|
||||||
if config_registry is not Undefined:
|
if config_registry is not Undefined:
|
||||||
config_registry = cast(registry, config_registry)
|
config_registry = cast(registry, config_registry)
|
||||||
# If it was passed by kwargs, ensure it's also set in config
|
# If it was passed by kwargs, ensure it's also set in config
|
||||||
new_cls.__config__.registry = config_table
|
set_config_value(model=new_cls, parameter="registry", value=config_table)
|
||||||
setattr(new_cls, "_sa_registry", config_registry) # noqa: B010
|
setattr(new_cls, "_sa_registry", config_registry) # noqa: B010
|
||||||
setattr(new_cls, "metadata", config_registry.metadata) # noqa: B010
|
setattr(new_cls, "metadata", config_registry.metadata) # noqa: B010
|
||||||
setattr(new_cls, "__abstract__", True) # noqa: B010
|
setattr(new_cls, "__abstract__", True) # noqa: B010
|
||||||
@ -477,13 +503,8 @@ class SQLModelMetaclass(ModelMetaclass, DeclarativeMeta):
|
|||||||
# this allows FastAPI cloning a SQLModel for the response_model without
|
# this allows FastAPI cloning a SQLModel for the response_model without
|
||||||
# trying to create a new SQLAlchemy, for a new table, with the same name, that
|
# trying to create a new SQLAlchemy, for a new table, with the same name, that
|
||||||
# triggers an error
|
# triggers an error
|
||||||
base_is_table = False
|
base_is_table = any(is_table_model_class(base) for base in bases)
|
||||||
for base in bases:
|
if is_table_model_class(cls) and not base_is_table:
|
||||||
config = getattr(base, "__config__") # noqa: B009
|
|
||||||
if config and getattr(config, "table", False):
|
|
||||||
base_is_table = True
|
|
||||||
break
|
|
||||||
if getattr(cls.__config__, "table", False) and not base_is_table:
|
|
||||||
for rel_name, rel_info in cls.__sqlmodel_relationships__.items():
|
for rel_name, rel_info in cls.__sqlmodel_relationships__.items():
|
||||||
if rel_info.sa_relationship:
|
if rel_info.sa_relationship:
|
||||||
# There's a SQLAlchemy relationship declared, that takes precedence
|
# There's a SQLAlchemy relationship declared, that takes precedence
|
||||||
@ -500,16 +521,9 @@ class SQLModelMetaclass(ModelMetaclass, DeclarativeMeta):
|
|||||||
# handled well by SQLAlchemy without Mapped, so, wrap the
|
# handled well by SQLAlchemy without Mapped, so, wrap the
|
||||||
# annotations in Mapped here
|
# annotations in Mapped here
|
||||||
cls.__annotations__[rel_name] = Mapped[ann] # type: ignore[valid-type]
|
cls.__annotations__[rel_name] = Mapped[ann] # type: ignore[valid-type]
|
||||||
temp_field = ModelField.infer(
|
relationship_to = get_relationship_to(
|
||||||
name=rel_name,
|
name=rel_name, rel_info=rel_info, annotation=ann
|
||||||
value=rel_info,
|
|
||||||
annotation=ann,
|
|
||||||
class_validators=None,
|
|
||||||
config=BaseConfig,
|
|
||||||
)
|
)
|
||||||
relationship_to = temp_field.type_
|
|
||||||
if isinstance(temp_field.type_, ForwardRef):
|
|
||||||
relationship_to = temp_field.type_.__forward_arg__
|
|
||||||
rel_kwargs: Dict[str, Any] = {}
|
rel_kwargs: Dict[str, Any] = {}
|
||||||
if rel_info.back_populates:
|
if rel_info.back_populates:
|
||||||
rel_kwargs["back_populates"] = rel_info.back_populates
|
rel_kwargs["back_populates"] = rel_info.back_populates
|
||||||
@ -537,77 +551,89 @@ class SQLModelMetaclass(ModelMetaclass, DeclarativeMeta):
|
|||||||
ModelMetaclass.__init__(cls, classname, bases, dict_, **kw)
|
ModelMetaclass.__init__(cls, classname, bases, dict_, **kw)
|
||||||
|
|
||||||
|
|
||||||
def get_sqlalchemy_type(field: ModelField) -> Any:
|
def get_sqlalchemy_type(field: Any) -> Any:
|
||||||
sa_type = getattr(field.field_info, "sa_type", Undefined) # noqa: B009
|
if IS_PYDANTIC_V2:
|
||||||
|
field_info = field
|
||||||
|
else:
|
||||||
|
field_info = field.field_info
|
||||||
|
sa_type = getattr(field_info, "sa_type", Undefined) # noqa: B009
|
||||||
if sa_type is not Undefined:
|
if sa_type is not Undefined:
|
||||||
return sa_type
|
return sa_type
|
||||||
if isinstance(field.type_, type) and field.shape == SHAPE_SINGLETON:
|
|
||||||
# Check enums first as an enum can also be a str, needed by Pydantic/FastAPI
|
type_ = get_type_from_field(field)
|
||||||
if issubclass(field.type_, Enum):
|
metadata = get_field_metadata(field)
|
||||||
return sa_Enum(field.type_)
|
|
||||||
if issubclass(field.type_, str):
|
# Check enums first as an enum can also be a str, needed by Pydantic/FastAPI
|
||||||
if field.field_info.max_length:
|
if issubclass(type_, Enum):
|
||||||
return AutoString(length=field.field_info.max_length)
|
return sa_Enum(type_)
|
||||||
return AutoString
|
if issubclass(type_, str):
|
||||||
if issubclass(field.type_, float):
|
max_length = getattr(metadata, "max_length", None)
|
||||||
return Float
|
if max_length:
|
||||||
if issubclass(field.type_, bool):
|
return AutoString(length=max_length)
|
||||||
return Boolean
|
return AutoString
|
||||||
if issubclass(field.type_, int):
|
if issubclass(type_, float):
|
||||||
return Integer
|
return Float
|
||||||
if issubclass(field.type_, datetime):
|
if issubclass(type_, bool):
|
||||||
return DateTime
|
return Boolean
|
||||||
if issubclass(field.type_, date):
|
if issubclass(type_, int):
|
||||||
return Date
|
return Integer
|
||||||
if issubclass(field.type_, timedelta):
|
if issubclass(type_, datetime):
|
||||||
return Interval
|
return DateTime
|
||||||
if issubclass(field.type_, time):
|
if issubclass(type_, date):
|
||||||
return Time
|
return Date
|
||||||
if issubclass(field.type_, bytes):
|
if issubclass(type_, timedelta):
|
||||||
return LargeBinary
|
return Interval
|
||||||
if issubclass(field.type_, Decimal):
|
if issubclass(type_, time):
|
||||||
return Numeric(
|
return Time
|
||||||
precision=getattr(field.type_, "max_digits", None),
|
if issubclass(type_, bytes):
|
||||||
scale=getattr(field.type_, "decimal_places", None),
|
return LargeBinary
|
||||||
)
|
if issubclass(type_, Decimal):
|
||||||
if issubclass(field.type_, ipaddress.IPv4Address):
|
return Numeric(
|
||||||
return AutoString
|
precision=getattr(metadata, "max_digits", None),
|
||||||
if issubclass(field.type_, ipaddress.IPv4Network):
|
scale=getattr(metadata, "decimal_places", None),
|
||||||
return AutoString
|
)
|
||||||
if issubclass(field.type_, ipaddress.IPv6Address):
|
if issubclass(type_, ipaddress.IPv4Address):
|
||||||
return AutoString
|
return AutoString
|
||||||
if issubclass(field.type_, ipaddress.IPv6Network):
|
if issubclass(type_, ipaddress.IPv4Network):
|
||||||
return AutoString
|
return AutoString
|
||||||
if issubclass(field.type_, Path):
|
if issubclass(type_, ipaddress.IPv6Address):
|
||||||
return AutoString
|
return AutoString
|
||||||
if issubclass(field.type_, uuid.UUID):
|
if issubclass(type_, ipaddress.IPv6Network):
|
||||||
return GUID
|
return AutoString
|
||||||
raise ValueError(f"The field {field.name} has no matching SQLAlchemy type")
|
if issubclass(type_, Path):
|
||||||
|
return AutoString
|
||||||
|
if issubclass(type_, uuid.UUID):
|
||||||
|
return GUID
|
||||||
|
raise ValueError(f"{type_} has no matching SQLAlchemy type")
|
||||||
|
|
||||||
|
|
||||||
def get_column_from_field(field: ModelField) -> Column: # type: ignore
|
def get_column_from_field(field: Any) -> Column: # type: ignore
|
||||||
sa_column = getattr(field.field_info, "sa_column", Undefined)
|
if IS_PYDANTIC_V2:
|
||||||
|
field_info = field
|
||||||
|
else:
|
||||||
|
field_info = field.field_info
|
||||||
|
sa_column = getattr(field_info, "sa_column", Undefined)
|
||||||
if isinstance(sa_column, Column):
|
if isinstance(sa_column, Column):
|
||||||
return sa_column
|
return sa_column
|
||||||
sa_type = get_sqlalchemy_type(field)
|
sa_type = get_sqlalchemy_type(field)
|
||||||
primary_key = getattr(field.field_info, "primary_key", Undefined)
|
primary_key = getattr(field_info, "primary_key", Undefined)
|
||||||
if primary_key is Undefined:
|
if primary_key is Undefined:
|
||||||
primary_key = False
|
primary_key = False
|
||||||
index = getattr(field.field_info, "index", Undefined)
|
index = getattr(field_info, "index", Undefined)
|
||||||
if index is Undefined:
|
if index is Undefined:
|
||||||
index = False
|
index = False
|
||||||
nullable = not primary_key and _is_field_noneable(field)
|
nullable = not primary_key and is_field_noneable(field)
|
||||||
# Override derived nullability if the nullable property is set explicitly
|
# Override derived nullability if the nullable property is set explicitly
|
||||||
# on the field
|
# on the field
|
||||||
field_nullable = getattr(field.field_info, "nullable", Undefined) # noqa: B009
|
field_nullable = getattr(field_info, "nullable", Undefined) # noqa: B009
|
||||||
if field_nullable != Undefined:
|
if field_nullable is not Undefined:
|
||||||
assert not isinstance(field_nullable, UndefinedType)
|
assert not isinstance(field_nullable, UndefinedType)
|
||||||
nullable = field_nullable
|
nullable = field_nullable
|
||||||
args = []
|
args = []
|
||||||
foreign_key = getattr(field.field_info, "foreign_key", Undefined)
|
foreign_key = getattr(field_info, "foreign_key", Undefined)
|
||||||
if foreign_key is Undefined:
|
if foreign_key is Undefined:
|
||||||
foreign_key = None
|
foreign_key = None
|
||||||
unique = getattr(field.field_info, "unique", Undefined)
|
unique = getattr(field_info, "unique", Undefined)
|
||||||
if unique is Undefined:
|
if unique is Undefined:
|
||||||
unique = False
|
unique = False
|
||||||
if foreign_key:
|
if foreign_key:
|
||||||
@ -620,16 +646,16 @@ def get_column_from_field(field: ModelField) -> Column: # type: ignore
|
|||||||
"unique": unique,
|
"unique": unique,
|
||||||
}
|
}
|
||||||
sa_default = Undefined
|
sa_default = Undefined
|
||||||
if field.field_info.default_factory:
|
if field_info.default_factory:
|
||||||
sa_default = field.field_info.default_factory
|
sa_default = field_info.default_factory
|
||||||
elif field.field_info.default is not Undefined:
|
elif field_info.default is not Undefined:
|
||||||
sa_default = field.field_info.default
|
sa_default = field_info.default
|
||||||
if sa_default is not Undefined:
|
if sa_default is not Undefined:
|
||||||
kwargs["default"] = sa_default
|
kwargs["default"] = sa_default
|
||||||
sa_column_args = getattr(field.field_info, "sa_column_args", Undefined)
|
sa_column_args = getattr(field_info, "sa_column_args", Undefined)
|
||||||
if sa_column_args is not Undefined:
|
if sa_column_args is not Undefined:
|
||||||
args.extend(list(cast(Sequence[Any], sa_column_args)))
|
args.extend(list(cast(Sequence[Any], sa_column_args)))
|
||||||
sa_column_kwargs = getattr(field.field_info, "sa_column_kwargs", Undefined)
|
sa_column_kwargs = getattr(field_info, "sa_column_kwargs", Undefined)
|
||||||
if sa_column_kwargs is not Undefined:
|
if sa_column_kwargs is not Undefined:
|
||||||
kwargs.update(cast(Dict[Any, Any], sa_column_kwargs))
|
kwargs.update(cast(Dict[Any, Any], sa_column_kwargs))
|
||||||
return Column(sa_type, *args, **kwargs) # type: ignore
|
return Column(sa_type, *args, **kwargs) # type: ignore
|
||||||
@ -639,13 +665,6 @@ class_registry = weakref.WeakValueDictionary() # type: ignore
|
|||||||
|
|
||||||
default_registry = registry()
|
default_registry = registry()
|
||||||
|
|
||||||
|
|
||||||
def _value_items_is_true(v: Any) -> bool:
|
|
||||||
# Re-implement Pydantic's ValueItems.is_true() as it hasn't been released as of
|
|
||||||
# the current latest, Pydantic 1.8.2
|
|
||||||
return v is True or v is ...
|
|
||||||
|
|
||||||
|
|
||||||
_TSQLModel = TypeVar("_TSQLModel", bound="SQLModel")
|
_TSQLModel = TypeVar("_TSQLModel", bound="SQLModel")
|
||||||
|
|
||||||
|
|
||||||
@ -653,13 +672,17 @@ class SQLModel(BaseModel, metaclass=SQLModelMetaclass, registry=default_registry
|
|||||||
# SQLAlchemy needs to set weakref(s), Pydantic will set the other slots values
|
# SQLAlchemy needs to set weakref(s), Pydantic will set the other slots values
|
||||||
__slots__ = ("__weakref__",)
|
__slots__ = ("__weakref__",)
|
||||||
__tablename__: ClassVar[Union[str, Callable[..., str]]]
|
__tablename__: ClassVar[Union[str, Callable[..., str]]]
|
||||||
__sqlmodel_relationships__: ClassVar[Dict[str, RelationshipProperty]] # type: ignore
|
__sqlmodel_relationships__: ClassVar[Dict[str, RelationshipProperty[Any]]]
|
||||||
__name__: ClassVar[str]
|
__name__: ClassVar[str]
|
||||||
metadata: ClassVar[MetaData]
|
metadata: ClassVar[MetaData]
|
||||||
__allow_unmapped__ = True # https://docs.sqlalchemy.org/en/20/changelog/migration_20.html#migration-20-step-six
|
__allow_unmapped__ = True # https://docs.sqlalchemy.org/en/20/changelog/migration_20.html#migration-20-step-six
|
||||||
|
|
||||||
class Config:
|
if IS_PYDANTIC_V2:
|
||||||
orm_mode = True
|
model_config = SQLModelConfig(from_attributes=True)
|
||||||
|
else:
|
||||||
|
|
||||||
|
class Config:
|
||||||
|
orm_mode = True
|
||||||
|
|
||||||
def __new__(cls, *args: Any, **kwargs: Any) -> Any:
|
def __new__(cls, *args: Any, **kwargs: Any) -> Any:
|
||||||
new_object = super().__new__(cls)
|
new_object = super().__new__(cls)
|
||||||
@ -668,31 +691,28 @@ class SQLModel(BaseModel, metaclass=SQLModelMetaclass, registry=default_registry
|
|||||||
# Set __fields_set__ here, that would have been set when calling __init__
|
# Set __fields_set__ here, that would have been set when calling __init__
|
||||||
# in the Pydantic model so that when SQLAlchemy sets attributes that are
|
# in the Pydantic model so that when SQLAlchemy sets attributes that are
|
||||||
# added (e.g. when querying from DB) to the __fields_set__, this already exists
|
# added (e.g. when querying from DB) to the __fields_set__, this already exists
|
||||||
object.__setattr__(new_object, "__fields_set__", set())
|
set_fields_set(new_object, set())
|
||||||
return new_object
|
return new_object
|
||||||
|
|
||||||
def __init__(__pydantic_self__, **data: Any) -> None:
|
def __init__(__pydantic_self__, **data: Any) -> None:
|
||||||
# Uses something other than `self` the first arg to allow "self" as a
|
# Uses something other than `self` the first arg to allow "self" as a
|
||||||
# settable attribute
|
# settable attribute
|
||||||
values, fields_set, validation_error = validate_model(
|
|
||||||
__pydantic_self__.__class__, data
|
# SQLAlchemy does very dark black magic and modifies the __init__ method in
|
||||||
)
|
# sqlalchemy.orm.instrumentation._generate_init()
|
||||||
# Only raise errors if not a SQLModel model
|
# so, to make SQLAlchemy work, it's needed to explicitly call __init__ to
|
||||||
if (
|
# trigger all the SQLAlchemy logic, it doesn't work using cls.__new__, setting
|
||||||
not getattr(__pydantic_self__.__config__, "table", False)
|
# attributes obj.__dict__, etc. The __init__ method has to be called. But
|
||||||
and validation_error
|
# there are cases where calling all the default logic is not ideal, e.g.
|
||||||
):
|
# when calling Model.model_validate(), as the validation is done outside
|
||||||
raise validation_error
|
# of instance creation.
|
||||||
# Do not set values as in Pydantic, pass them through setattr, so SQLAlchemy
|
# At the same time, __init__ is what users would normally call, by creating
|
||||||
# can handle them
|
# a new instance, which should have validation and all the default logic.
|
||||||
# object.__setattr__(__pydantic_self__, '__dict__', values)
|
# So, to be able to set up the internal SQLAlchemy logic alone without
|
||||||
for key, value in values.items():
|
# executing the rest, and support things like Model.model_validate(), we
|
||||||
setattr(__pydantic_self__, key, value)
|
# use a contextvar to know if we should execute everything.
|
||||||
object.__setattr__(__pydantic_self__, "__fields_set__", fields_set)
|
if finish_init.get():
|
||||||
non_pydantic_keys = data.keys() - values.keys()
|
sqlmodel_init(self=__pydantic_self__, data=data)
|
||||||
for key in non_pydantic_keys:
|
|
||||||
if key in __pydantic_self__.__sqlmodel_relationships__:
|
|
||||||
setattr(__pydantic_self__, key, data[key])
|
|
||||||
|
|
||||||
def __setattr__(self, name: str, value: Any) -> None:
|
def __setattr__(self, name: str, value: Any) -> None:
|
||||||
if name in {"_sa_instance_state"}:
|
if name in {"_sa_instance_state"}:
|
||||||
@ -700,59 +720,13 @@ class SQLModel(BaseModel, metaclass=SQLModelMetaclass, registry=default_registry
|
|||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
# Set in SQLAlchemy, before Pydantic to trigger events and updates
|
# Set in SQLAlchemy, before Pydantic to trigger events and updates
|
||||||
if getattr(self.__config__, "table", False) and is_instrumented(self, name): # type: ignore
|
if is_table_model_class(self.__class__) and is_instrumented(self, name): # type: ignore[no-untyped-call]
|
||||||
set_attribute(self, name, value)
|
set_attribute(self, name, value)
|
||||||
# Set in Pydantic model to trigger possible validation changes, only for
|
# Set in Pydantic model to trigger possible validation changes, only for
|
||||||
# non relationship values
|
# non relationship values
|
||||||
if name not in self.__sqlmodel_relationships__:
|
if name not in self.__sqlmodel_relationships__:
|
||||||
super().__setattr__(name, value)
|
super().__setattr__(name, value)
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def from_orm(
|
|
||||||
cls: Type[_TSQLModel], obj: Any, update: Optional[Dict[str, Any]] = None
|
|
||||||
) -> _TSQLModel:
|
|
||||||
# Duplicated from Pydantic
|
|
||||||
if not cls.__config__.orm_mode:
|
|
||||||
raise ConfigError(
|
|
||||||
"You must have the config attribute orm_mode=True to use from_orm"
|
|
||||||
)
|
|
||||||
obj = {ROOT_KEY: obj} if cls.__custom_root_type__ else cls._decompose_class(obj)
|
|
||||||
# SQLModel, support update dict
|
|
||||||
if update is not None:
|
|
||||||
obj = {**obj, **update}
|
|
||||||
# End SQLModel support dict
|
|
||||||
if not getattr(cls.__config__, "table", False):
|
|
||||||
# If not table, normal Pydantic code
|
|
||||||
m: _TSQLModel = cls.__new__(cls)
|
|
||||||
else:
|
|
||||||
# If table, create the new instance normally to make SQLAlchemy create
|
|
||||||
# the _sa_instance_state attribute
|
|
||||||
m = cls()
|
|
||||||
values, fields_set, validation_error = validate_model(cls, obj)
|
|
||||||
if validation_error:
|
|
||||||
raise validation_error
|
|
||||||
# Updated to trigger SQLAlchemy internal handling
|
|
||||||
if not getattr(cls.__config__, "table", False):
|
|
||||||
object.__setattr__(m, "__dict__", values)
|
|
||||||
else:
|
|
||||||
for key, value in values.items():
|
|
||||||
setattr(m, key, value)
|
|
||||||
# Continue with standard Pydantic logic
|
|
||||||
object.__setattr__(m, "__fields_set__", fields_set)
|
|
||||||
m._init_private_attributes()
|
|
||||||
return m
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def parse_obj(
|
|
||||||
cls: Type[_TSQLModel], obj: Any, update: Optional[Dict[str, Any]] = None
|
|
||||||
) -> _TSQLModel:
|
|
||||||
obj = cls._enforce_dict_if_root(obj)
|
|
||||||
# SQLModel, support update dict
|
|
||||||
if update is not None:
|
|
||||||
obj = {**obj, **update}
|
|
||||||
# End SQLModel support dict
|
|
||||||
return super().parse_obj(obj)
|
|
||||||
|
|
||||||
def __repr_args__(self) -> Sequence[Tuple[Optional[str], Any]]:
|
def __repr_args__(self) -> Sequence[Tuple[Optional[str], Any]]:
|
||||||
# Don't show SQLAlchemy private attributes
|
# Don't show SQLAlchemy private attributes
|
||||||
return [
|
return [
|
||||||
@ -761,33 +735,126 @@ class SQLModel(BaseModel, metaclass=SQLModelMetaclass, registry=default_registry
|
|||||||
if not (isinstance(k, str) and k.startswith("_sa_"))
|
if not (isinstance(k, str) and k.startswith("_sa_"))
|
||||||
]
|
]
|
||||||
|
|
||||||
# From Pydantic, override to enforce validation with dict
|
@declared_attr # type: ignore
|
||||||
@classmethod
|
def __tablename__(cls) -> str:
|
||||||
def validate(cls: Type[_TSQLModel], value: Any) -> _TSQLModel:
|
return cls.__name__.lower()
|
||||||
if isinstance(value, cls):
|
|
||||||
return value.copy() if cls.__config__.copy_on_model_validation else value
|
|
||||||
|
|
||||||
value = cls._enforce_dict_if_root(value)
|
@classmethod
|
||||||
if isinstance(value, dict):
|
def model_validate(
|
||||||
values, fields_set, validation_error = validate_model(cls, value)
|
cls: Type[_TSQLModel],
|
||||||
if validation_error:
|
obj: Any,
|
||||||
raise validation_error
|
*,
|
||||||
model = cls(**value)
|
strict: Union[bool, None] = None,
|
||||||
# Reset fields set, this would have been done in Pydantic in __init__
|
from_attributes: Union[bool, None] = None,
|
||||||
object.__setattr__(model, "__fields_set__", fields_set)
|
context: Union[Dict[str, Any], None] = None,
|
||||||
return model
|
update: Union[Dict[str, Any], None] = None,
|
||||||
elif cls.__config__.orm_mode:
|
) -> _TSQLModel:
|
||||||
return cls.from_orm(value)
|
return sqlmodel_validate(
|
||||||
elif cls.__custom_root_type__:
|
cls=cls,
|
||||||
return cls.parse_obj(value)
|
obj=obj,
|
||||||
|
strict=strict,
|
||||||
|
from_attributes=from_attributes,
|
||||||
|
context=context,
|
||||||
|
update=update,
|
||||||
|
)
|
||||||
|
|
||||||
|
# TODO: remove when deprecating Pydantic v1, only for compatibility
|
||||||
|
def model_dump(
|
||||||
|
self,
|
||||||
|
*,
|
||||||
|
mode: Union[Literal["json", "python"], str] = "python",
|
||||||
|
include: IncEx = None,
|
||||||
|
exclude: IncEx = None,
|
||||||
|
by_alias: bool = False,
|
||||||
|
exclude_unset: bool = False,
|
||||||
|
exclude_defaults: bool = False,
|
||||||
|
exclude_none: bool = False,
|
||||||
|
round_trip: bool = False,
|
||||||
|
warnings: bool = True,
|
||||||
|
) -> Dict[str, Any]:
|
||||||
|
if IS_PYDANTIC_V2:
|
||||||
|
return super().model_dump(
|
||||||
|
mode=mode,
|
||||||
|
include=include,
|
||||||
|
exclude=exclude,
|
||||||
|
by_alias=by_alias,
|
||||||
|
exclude_unset=exclude_unset,
|
||||||
|
exclude_defaults=exclude_defaults,
|
||||||
|
exclude_none=exclude_none,
|
||||||
|
round_trip=round_trip,
|
||||||
|
warnings=warnings,
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
try:
|
return super().dict(
|
||||||
value_as_dict = dict(value)
|
include=include,
|
||||||
except (TypeError, ValueError) as e:
|
exclude=exclude,
|
||||||
raise DictError() from e
|
by_alias=by_alias,
|
||||||
return cls(**value_as_dict)
|
exclude_unset=exclude_unset,
|
||||||
|
exclude_defaults=exclude_defaults,
|
||||||
|
exclude_none=exclude_none,
|
||||||
|
)
|
||||||
|
|
||||||
|
@deprecated(
|
||||||
|
"""
|
||||||
|
🚨 `obj.dict()` was deprecated in SQLModel 0.0.14, you should
|
||||||
|
instead use `obj.model_dump()`.
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
def dict(
|
||||||
|
self,
|
||||||
|
*,
|
||||||
|
include: IncEx = None,
|
||||||
|
exclude: IncEx = None,
|
||||||
|
by_alias: bool = False,
|
||||||
|
exclude_unset: bool = False,
|
||||||
|
exclude_defaults: bool = False,
|
||||||
|
exclude_none: bool = False,
|
||||||
|
) -> Dict[str, Any]:
|
||||||
|
return self.model_dump(
|
||||||
|
include=include,
|
||||||
|
exclude=exclude,
|
||||||
|
by_alias=by_alias,
|
||||||
|
exclude_unset=exclude_unset,
|
||||||
|
exclude_defaults=exclude_defaults,
|
||||||
|
exclude_none=exclude_none,
|
||||||
|
)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
@deprecated(
|
||||||
|
"""
|
||||||
|
🚨 `obj.from_orm(data)` was deprecated in SQLModel 0.0.14, you should
|
||||||
|
instead use `obj.model_validate(data)`.
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
def from_orm(
|
||||||
|
cls: Type[_TSQLModel], obj: Any, update: Optional[Dict[str, Any]] = None
|
||||||
|
) -> _TSQLModel:
|
||||||
|
return cls.model_validate(obj, update=update)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
@deprecated(
|
||||||
|
"""
|
||||||
|
🚨 `obj.parse_obj(data)` was deprecated in SQLModel 0.0.14, you should
|
||||||
|
instead use `obj.model_validate(data)`.
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
def parse_obj(
|
||||||
|
cls: Type[_TSQLModel], obj: Any, update: Optional[Dict[str, Any]] = None
|
||||||
|
) -> _TSQLModel:
|
||||||
|
if not IS_PYDANTIC_V2:
|
||||||
|
obj = cls._enforce_dict_if_root(obj) # type: ignore[attr-defined] # noqa
|
||||||
|
return cls.model_validate(obj, update=update)
|
||||||
|
|
||||||
# From Pydantic, override to only show keys from fields, omit SQLAlchemy attributes
|
# From Pydantic, override to only show keys from fields, omit SQLAlchemy attributes
|
||||||
|
@deprecated(
|
||||||
|
"""
|
||||||
|
🚨 You should not access `obj._calculate_keys()` directly.
|
||||||
|
|
||||||
|
It is only useful for Pydantic v1.X, you should probably upgrade to
|
||||||
|
Pydantic v2.X.
|
||||||
|
""",
|
||||||
|
category=None,
|
||||||
|
)
|
||||||
def _calculate_keys(
|
def _calculate_keys(
|
||||||
self,
|
self,
|
||||||
include: Optional[Mapping[Union[int, str], Any]],
|
include: Optional[Mapping[Union[int, str], Any]],
|
||||||
@ -795,44 +862,10 @@ class SQLModel(BaseModel, metaclass=SQLModelMetaclass, registry=default_registry
|
|||||||
exclude_unset: bool,
|
exclude_unset: bool,
|
||||||
update: Optional[Dict[str, Any]] = None,
|
update: Optional[Dict[str, Any]] = None,
|
||||||
) -> Optional[AbstractSet[str]]:
|
) -> Optional[AbstractSet[str]]:
|
||||||
if include is None and exclude is None and not exclude_unset:
|
return _calculate_keys(
|
||||||
# Original in Pydantic:
|
self,
|
||||||
# return None
|
include=include,
|
||||||
# Updated to not return SQLAlchemy attributes
|
exclude=exclude,
|
||||||
# Do not include relationships as that would easily lead to infinite
|
exclude_unset=exclude_unset,
|
||||||
# recursion, or traversing the whole database
|
update=update,
|
||||||
return self.__fields__.keys() # | self.__sqlmodel_relationships__.keys()
|
|
||||||
|
|
||||||
keys: AbstractSet[str]
|
|
||||||
if exclude_unset:
|
|
||||||
keys = self.__fields_set__.copy()
|
|
||||||
else:
|
|
||||||
# Original in Pydantic:
|
|
||||||
# keys = self.__dict__.keys()
|
|
||||||
# Updated to not return SQLAlchemy attributes
|
|
||||||
# Do not include relationships as that would easily lead to infinite
|
|
||||||
# recursion, or traversing the whole database
|
|
||||||
keys = self.__fields__.keys() # | self.__sqlmodel_relationships__.keys()
|
|
||||||
if include is not None:
|
|
||||||
keys &= include.keys()
|
|
||||||
|
|
||||||
if update:
|
|
||||||
keys -= update.keys()
|
|
||||||
|
|
||||||
if exclude:
|
|
||||||
keys -= {k for k, v in exclude.items() if _value_items_is_true(v)}
|
|
||||||
|
|
||||||
return keys
|
|
||||||
|
|
||||||
@declared_attr # type: ignore
|
|
||||||
def __tablename__(cls) -> str:
|
|
||||||
return cls.__name__.lower()
|
|
||||||
|
|
||||||
|
|
||||||
def _is_field_noneable(field: ModelField) -> bool:
|
|
||||||
if not field.required:
|
|
||||||
# Taken from [Pydantic](https://github.com/samuelcolvin/pydantic/blob/v1.8.2/pydantic/fields.py#L946-L947)
|
|
||||||
return field.allow_none and (
|
|
||||||
field.shape != SHAPE_SINGLETON or not field.sub_fields
|
|
||||||
)
|
)
|
||||||
return False
|
|
||||||
|
@ -7,6 +7,7 @@ from typing import Any, Callable, Dict, List, Union
|
|||||||
import pytest
|
import pytest
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
from sqlmodel import SQLModel
|
from sqlmodel import SQLModel
|
||||||
|
from sqlmodel._compat import IS_PYDANTIC_V2
|
||||||
from sqlmodel.main import default_registry
|
from sqlmodel.main import default_registry
|
||||||
|
|
||||||
top_level_path = Path(__file__).resolve().parent.parent
|
top_level_path = Path(__file__).resolve().parent.parent
|
||||||
@ -56,12 +57,12 @@ def get_testing_print_function(
|
|||||||
data = []
|
data = []
|
||||||
for arg in args:
|
for arg in args:
|
||||||
if isinstance(arg, BaseModel):
|
if isinstance(arg, BaseModel):
|
||||||
data.append(arg.dict())
|
data.append(arg.model_dump())
|
||||||
elif isinstance(arg, list):
|
elif isinstance(arg, list):
|
||||||
new_list = []
|
new_list = []
|
||||||
for item in arg:
|
for item in arg:
|
||||||
if isinstance(item, BaseModel):
|
if isinstance(item, BaseModel):
|
||||||
new_list.append(item.dict())
|
new_list.append(item.model_dump())
|
||||||
data.append(new_list)
|
data.append(new_list)
|
||||||
else:
|
else:
|
||||||
data.append(arg)
|
data.append(arg)
|
||||||
@ -70,6 +71,9 @@ def get_testing_print_function(
|
|||||||
return new_print
|
return new_print
|
||||||
|
|
||||||
|
|
||||||
|
needs_pydanticv2 = pytest.mark.skipif(not IS_PYDANTIC_V2, reason="requires Pydantic v2")
|
||||||
|
needs_pydanticv1 = pytest.mark.skipif(IS_PYDANTIC_V2, reason="requires Pydantic v1")
|
||||||
|
|
||||||
needs_py39 = pytest.mark.skipif(sys.version_info < (3, 9), reason="requires python3.9+")
|
needs_py39 = pytest.mark.skipif(sys.version_info < (3, 9), reason="requires python3.9+")
|
||||||
needs_py310 = pytest.mark.skipif(
|
needs_py310 = pytest.mark.skipif(
|
||||||
sys.version_info < (3, 10), reason="requires python3.10+"
|
sys.version_info < (3, 10), reason="requires python3.10+"
|
||||||
|
30
tests/test_deprecations.py
Normal file
30
tests/test_deprecations.py
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
import pytest
|
||||||
|
from sqlmodel import SQLModel
|
||||||
|
|
||||||
|
|
||||||
|
class Item(SQLModel):
|
||||||
|
name: str
|
||||||
|
|
||||||
|
|
||||||
|
class SubItem(Item):
|
||||||
|
password: str
|
||||||
|
|
||||||
|
|
||||||
|
def test_deprecated_from_orm_inheritance():
|
||||||
|
new_item = SubItem(name="Hello", password="secret")
|
||||||
|
with pytest.warns(DeprecationWarning):
|
||||||
|
item = Item.from_orm(new_item)
|
||||||
|
assert item.name == "Hello"
|
||||||
|
assert not hasattr(item, "password")
|
||||||
|
|
||||||
|
|
||||||
|
def test_deprecated_parse_obj():
|
||||||
|
with pytest.warns(DeprecationWarning):
|
||||||
|
item = Item.parse_obj({"name": "Hello"})
|
||||||
|
assert item.name == "Hello"
|
||||||
|
|
||||||
|
|
||||||
|
def test_deprecated_dict():
|
||||||
|
with pytest.warns(DeprecationWarning):
|
||||||
|
data = Item(name="Hello").dict()
|
||||||
|
assert data == {"name": "Hello"}
|
@ -5,6 +5,8 @@ from sqlalchemy import create_mock_engine
|
|||||||
from sqlalchemy.sql.type_api import TypeEngine
|
from sqlalchemy.sql.type_api import TypeEngine
|
||||||
from sqlmodel import Field, SQLModel
|
from sqlmodel import Field, SQLModel
|
||||||
|
|
||||||
|
from .conftest import needs_pydanticv1, needs_pydanticv2
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Tests related to Enums
|
Tests related to Enums
|
||||||
|
|
||||||
@ -72,7 +74,8 @@ def test_sqlite_ddl_sql(capsys):
|
|||||||
assert "CREATE TYPE" not in captured.out
|
assert "CREATE TYPE" not in captured.out
|
||||||
|
|
||||||
|
|
||||||
def test_json_schema_flat_model():
|
@needs_pydanticv1
|
||||||
|
def test_json_schema_flat_model_pydantic_v1():
|
||||||
assert FlatModel.schema() == {
|
assert FlatModel.schema() == {
|
||||||
"title": "FlatModel",
|
"title": "FlatModel",
|
||||||
"type": "object",
|
"type": "object",
|
||||||
@ -92,7 +95,8 @@ def test_json_schema_flat_model():
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def test_json_schema_inherit_model():
|
@needs_pydanticv1
|
||||||
|
def test_json_schema_inherit_model_pydantic_v1():
|
||||||
assert InheritModel.schema() == {
|
assert InheritModel.schema() == {
|
||||||
"title": "InheritModel",
|
"title": "InheritModel",
|
||||||
"type": "object",
|
"type": "object",
|
||||||
@ -110,3 +114,35 @@ def test_json_schema_inherit_model():
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@needs_pydanticv2
|
||||||
|
def test_json_schema_flat_model_pydantic_v2():
|
||||||
|
assert FlatModel.model_json_schema() == {
|
||||||
|
"title": "FlatModel",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"id": {"title": "Id", "type": "string", "format": "uuid"},
|
||||||
|
"enum_field": {"$ref": "#/$defs/MyEnum1"},
|
||||||
|
},
|
||||||
|
"required": ["id", "enum_field"],
|
||||||
|
"$defs": {
|
||||||
|
"MyEnum1": {"enum": ["A", "B"], "title": "MyEnum1", "type": "string"}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@needs_pydanticv2
|
||||||
|
def test_json_schema_inherit_model_pydantic_v2():
|
||||||
|
assert InheritModel.model_json_schema() == {
|
||||||
|
"title": "InheritModel",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"id": {"title": "Id", "type": "string", "format": "uuid"},
|
||||||
|
"enum_field": {"$ref": "#/$defs/MyEnum2"},
|
||||||
|
},
|
||||||
|
"required": ["id", "enum_field"],
|
||||||
|
"$defs": {
|
||||||
|
"MyEnum2": {"enum": ["C", "D"], "title": "MyEnum2", "type": "string"}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
@ -6,7 +6,7 @@ from sqlmodel import Field, Relationship, SQLModel
|
|||||||
|
|
||||||
|
|
||||||
def test_sa_relationship_no_args() -> None:
|
def test_sa_relationship_no_args() -> None:
|
||||||
with pytest.raises(RuntimeError):
|
with pytest.raises(RuntimeError): # pragma: no cover
|
||||||
|
|
||||||
class Team(SQLModel, table=True):
|
class Team(SQLModel, table=True):
|
||||||
id: Optional[int] = Field(default=None, primary_key=True)
|
id: Optional[int] = Field(default=None, primary_key=True)
|
||||||
@ -30,7 +30,7 @@ def test_sa_relationship_no_args() -> None:
|
|||||||
|
|
||||||
|
|
||||||
def test_sa_relationship_no_kwargs() -> None:
|
def test_sa_relationship_no_kwargs() -> None:
|
||||||
with pytest.raises(RuntimeError):
|
with pytest.raises(RuntimeError): # pragma: no cover
|
||||||
|
|
||||||
class Team(SQLModel, table=True):
|
class Team(SQLModel, table=True):
|
||||||
id: Optional[int] = Field(default=None, primary_key=True)
|
id: Optional[int] = Field(default=None, primary_key=True)
|
||||||
|
@ -1,19 +1,16 @@
|
|||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
from sqlalchemy import create_engine, select
|
import pytest
|
||||||
from sqlalchemy.orm import Session
|
from pydantic import ValidationError
|
||||||
from sqlmodel import Field, SQLModel
|
from sqlmodel import Field, Session, SQLModel, create_engine, select
|
||||||
|
|
||||||
|
|
||||||
def test_allow_instantiation_without_arguments(clear_sqlmodel):
|
def test_allow_instantiation_without_arguments(clear_sqlmodel):
|
||||||
class Item(SQLModel):
|
class Item(SQLModel, table=True):
|
||||||
id: Optional[int] = Field(default=None, primary_key=True)
|
id: Optional[int] = Field(default=None, primary_key=True)
|
||||||
name: str
|
name: str
|
||||||
description: Optional[str] = None
|
description: Optional[str] = None
|
||||||
|
|
||||||
class Config:
|
|
||||||
table = True
|
|
||||||
|
|
||||||
engine = create_engine("sqlite:///:memory:")
|
engine = create_engine("sqlite:///:memory:")
|
||||||
SQLModel.metadata.create_all(engine)
|
SQLModel.metadata.create_all(engine)
|
||||||
with Session(engine) as db:
|
with Session(engine) as db:
|
||||||
@ -21,7 +18,18 @@ def test_allow_instantiation_without_arguments(clear_sqlmodel):
|
|||||||
item.name = "Rick"
|
item.name = "Rick"
|
||||||
db.add(item)
|
db.add(item)
|
||||||
db.commit()
|
db.commit()
|
||||||
result = db.execute(select(Item)).scalars().all()
|
statement = select(Item)
|
||||||
|
result = db.exec(statement).all()
|
||||||
assert len(result) == 1
|
assert len(result) == 1
|
||||||
assert isinstance(item.id, int)
|
assert isinstance(item.id, int)
|
||||||
SQLModel.metadata.clear()
|
SQLModel.metadata.clear()
|
||||||
|
|
||||||
|
|
||||||
|
def test_not_allow_instantiation_without_arguments_if_not_table():
|
||||||
|
class Item(SQLModel):
|
||||||
|
id: Optional[int] = Field(default=None, primary_key=True)
|
||||||
|
name: str
|
||||||
|
description: Optional[str] = None
|
||||||
|
|
||||||
|
with pytest.raises(ValidationError):
|
||||||
|
Item()
|
||||||
|
@ -91,7 +91,6 @@ def test_should_raise_exception_when_try_to_duplicate_row_if_unique_constraint_i
|
|||||||
with Session(engine) as session:
|
with Session(engine) as session:
|
||||||
session.add(hero_2)
|
session.add(hero_2)
|
||||||
session.commit()
|
session.commit()
|
||||||
session.refresh(hero_2)
|
|
||||||
|
|
||||||
|
|
||||||
def test_sa_relationship_property(clear_sqlmodel):
|
def test_sa_relationship_property(clear_sqlmodel):
|
||||||
|
@ -1,17 +1,18 @@
|
|||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
from pydantic import BaseModel
|
||||||
from sqlmodel import Field, SQLModel
|
from sqlmodel import Field, SQLModel
|
||||||
|
|
||||||
|
|
||||||
def test_missing_sql_type():
|
def test_missing_sql_type():
|
||||||
class CustomType:
|
class CustomType(BaseModel):
|
||||||
@classmethod
|
@classmethod
|
||||||
def __get_validators__(cls):
|
def __get_validators__(cls):
|
||||||
yield cls.validate
|
yield cls.validate
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def validate(cls, v):
|
def validate(cls, v): # pragma: no cover
|
||||||
return v
|
return v
|
||||||
|
|
||||||
with pytest.raises(ValueError):
|
with pytest.raises(ValueError):
|
||||||
|
@ -58,7 +58,7 @@ def test_nullable_fields(clear_sqlmodel, caplog):
|
|||||||
][0]
|
][0]
|
||||||
assert "primary_key INTEGER NOT NULL," in create_table_log
|
assert "primary_key INTEGER NOT NULL," in create_table_log
|
||||||
assert "required_value VARCHAR NOT NULL," in create_table_log
|
assert "required_value VARCHAR NOT NULL," in create_table_log
|
||||||
assert "optional_default_ellipsis VARCHAR NOT NULL," in create_table_log
|
assert "optional_default_ellipsis VARCHAR," in create_table_log
|
||||||
assert "optional_default_none VARCHAR," in create_table_log
|
assert "optional_default_none VARCHAR," in create_table_log
|
||||||
assert "optional_non_nullable VARCHAR NOT NULL," in create_table_log
|
assert "optional_non_nullable VARCHAR NOT NULL," in create_table_log
|
||||||
assert "optional_nullable VARCHAR," in create_table_log
|
assert "optional_nullable VARCHAR," in create_table_log
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
|
import pytest
|
||||||
from sqlmodel import Field, Session, SQLModel, create_engine
|
from sqlmodel import Field, Session, SQLModel, create_engine
|
||||||
|
|
||||||
|
|
||||||
@ -21,6 +22,7 @@ def test_query(clear_sqlmodel):
|
|||||||
session.refresh(hero_1)
|
session.refresh(hero_1)
|
||||||
|
|
||||||
with Session(engine) as session:
|
with Session(engine) as session:
|
||||||
query_hero = session.query(Hero).first()
|
with pytest.warns(DeprecationWarning):
|
||||||
|
query_hero = session.query(Hero).first()
|
||||||
assert query_hero
|
assert query_hero
|
||||||
assert query_hero.name == hero_1.name
|
assert query_hero.name == hero_1.name
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
from dirty_equals import IsDict
|
||||||
from fastapi.testclient import TestClient
|
from fastapi.testclient import TestClient
|
||||||
from sqlmodel import create_engine
|
from sqlmodel import create_engine
|
||||||
from sqlmodel.pool import StaticPool
|
from sqlmodel.pool import StaticPool
|
||||||
@ -284,7 +285,16 @@ def test_tutorial(clear_sqlmodel):
|
|||||||
"properties": {
|
"properties": {
|
||||||
"name": {"title": "Name", "type": "string"},
|
"name": {"title": "Name", "type": "string"},
|
||||||
"secret_name": {"title": "Secret Name", "type": "string"},
|
"secret_name": {"title": "Secret Name", "type": "string"},
|
||||||
"age": {"title": "Age", "type": "integer"},
|
"age": IsDict(
|
||||||
|
{
|
||||||
|
"title": "Age",
|
||||||
|
"anyOf": [{"type": "integer"}, {"type": "null"}],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
| IsDict(
|
||||||
|
# TODO: remove when deprecating Pydantic v1
|
||||||
|
{"title": "Age", "type": "integer"}
|
||||||
|
),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"HeroRead": {
|
"HeroRead": {
|
||||||
@ -294,7 +304,16 @@ def test_tutorial(clear_sqlmodel):
|
|||||||
"properties": {
|
"properties": {
|
||||||
"name": {"title": "Name", "type": "string"},
|
"name": {"title": "Name", "type": "string"},
|
||||||
"secret_name": {"title": "Secret Name", "type": "string"},
|
"secret_name": {"title": "Secret Name", "type": "string"},
|
||||||
"age": {"title": "Age", "type": "integer"},
|
"age": IsDict(
|
||||||
|
{
|
||||||
|
"title": "Age",
|
||||||
|
"anyOf": [{"type": "integer"}, {"type": "null"}],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
| IsDict(
|
||||||
|
# TODO: remove when deprecating Pydantic v1
|
||||||
|
{"title": "Age", "type": "integer"}
|
||||||
|
),
|
||||||
"id": {"title": "Id", "type": "integer"},
|
"id": {"title": "Id", "type": "integer"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -302,9 +321,36 @@ def test_tutorial(clear_sqlmodel):
|
|||||||
"title": "HeroUpdate",
|
"title": "HeroUpdate",
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
"name": {"title": "Name", "type": "string"},
|
"name": IsDict(
|
||||||
"secret_name": {"title": "Secret Name", "type": "string"},
|
{
|
||||||
"age": {"title": "Age", "type": "integer"},
|
"title": "Name",
|
||||||
|
"anyOf": [{"type": "string"}, {"type": "null"}],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
| IsDict(
|
||||||
|
# TODO: remove when deprecating Pydantic v1
|
||||||
|
{"title": "Name", "type": "string"}
|
||||||
|
),
|
||||||
|
"secret_name": IsDict(
|
||||||
|
{
|
||||||
|
"title": "Secret Name",
|
||||||
|
"anyOf": [{"type": "string"}, {"type": "null"}],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
| IsDict(
|
||||||
|
# TODO: remove when deprecating Pydantic v1
|
||||||
|
{"title": "Secret Name", "type": "string"}
|
||||||
|
),
|
||||||
|
"age": IsDict(
|
||||||
|
{
|
||||||
|
"title": "Age",
|
||||||
|
"anyOf": [{"type": "integer"}, {"type": "null"}],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
| IsDict(
|
||||||
|
# TODO: remove when deprecating Pydantic v1
|
||||||
|
{"title": "Age", "type": "integer"}
|
||||||
|
),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"ValidationError": {
|
"ValidationError": {
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
from dirty_equals import IsDict
|
||||||
from fastapi.testclient import TestClient
|
from fastapi.testclient import TestClient
|
||||||
from sqlmodel import create_engine
|
from sqlmodel import create_engine
|
||||||
from sqlmodel.pool import StaticPool
|
from sqlmodel.pool import StaticPool
|
||||||
@ -287,7 +288,16 @@ def test_tutorial(clear_sqlmodel):
|
|||||||
"properties": {
|
"properties": {
|
||||||
"name": {"title": "Name", "type": "string"},
|
"name": {"title": "Name", "type": "string"},
|
||||||
"secret_name": {"title": "Secret Name", "type": "string"},
|
"secret_name": {"title": "Secret Name", "type": "string"},
|
||||||
"age": {"title": "Age", "type": "integer"},
|
"age": IsDict(
|
||||||
|
{
|
||||||
|
"title": "Age",
|
||||||
|
"anyOf": [{"type": "integer"}, {"type": "null"}],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
| IsDict(
|
||||||
|
# TODO: remove when deprecating Pydantic v1
|
||||||
|
{"title": "Age", "type": "integer"}
|
||||||
|
),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"HeroRead": {
|
"HeroRead": {
|
||||||
@ -297,7 +307,16 @@ def test_tutorial(clear_sqlmodel):
|
|||||||
"properties": {
|
"properties": {
|
||||||
"name": {"title": "Name", "type": "string"},
|
"name": {"title": "Name", "type": "string"},
|
||||||
"secret_name": {"title": "Secret Name", "type": "string"},
|
"secret_name": {"title": "Secret Name", "type": "string"},
|
||||||
"age": {"title": "Age", "type": "integer"},
|
"age": IsDict(
|
||||||
|
{
|
||||||
|
"title": "Age",
|
||||||
|
"anyOf": [{"type": "integer"}, {"type": "null"}],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
| IsDict(
|
||||||
|
# TODO: remove when deprecating Pydantic v1
|
||||||
|
{"title": "Age", "type": "integer"}
|
||||||
|
),
|
||||||
"id": {"title": "Id", "type": "integer"},
|
"id": {"title": "Id", "type": "integer"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -305,9 +324,36 @@ def test_tutorial(clear_sqlmodel):
|
|||||||
"title": "HeroUpdate",
|
"title": "HeroUpdate",
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
"name": {"title": "Name", "type": "string"},
|
"name": IsDict(
|
||||||
"secret_name": {"title": "Secret Name", "type": "string"},
|
{
|
||||||
"age": {"title": "Age", "type": "integer"},
|
"title": "Name",
|
||||||
|
"anyOf": [{"type": "string"}, {"type": "null"}],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
| IsDict(
|
||||||
|
# TODO: remove when deprecating Pydantic v1
|
||||||
|
{"title": "Name", "type": "string"}
|
||||||
|
),
|
||||||
|
"secret_name": IsDict(
|
||||||
|
{
|
||||||
|
"title": "Secret Name",
|
||||||
|
"anyOf": [{"type": "string"}, {"type": "null"}],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
| IsDict(
|
||||||
|
# TODO: remove when deprecating Pydantic v1
|
||||||
|
{"title": "Secret Name", "type": "string"}
|
||||||
|
),
|
||||||
|
"age": IsDict(
|
||||||
|
{
|
||||||
|
"title": "Age",
|
||||||
|
"anyOf": [{"type": "integer"}, {"type": "null"}],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
| IsDict(
|
||||||
|
# TODO: remove when deprecating Pydantic v1
|
||||||
|
{"title": "Age", "type": "integer"}
|
||||||
|
),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"ValidationError": {
|
"ValidationError": {
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
from dirty_equals import IsDict
|
||||||
from fastapi.testclient import TestClient
|
from fastapi.testclient import TestClient
|
||||||
from sqlmodel import create_engine
|
from sqlmodel import create_engine
|
||||||
from sqlmodel.pool import StaticPool
|
from sqlmodel.pool import StaticPool
|
||||||
@ -287,7 +288,16 @@ def test_tutorial(clear_sqlmodel):
|
|||||||
"properties": {
|
"properties": {
|
||||||
"name": {"title": "Name", "type": "string"},
|
"name": {"title": "Name", "type": "string"},
|
||||||
"secret_name": {"title": "Secret Name", "type": "string"},
|
"secret_name": {"title": "Secret Name", "type": "string"},
|
||||||
"age": {"title": "Age", "type": "integer"},
|
"age": IsDict(
|
||||||
|
{
|
||||||
|
"title": "Age",
|
||||||
|
"anyOf": [{"type": "integer"}, {"type": "null"}],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
| IsDict(
|
||||||
|
# TODO: remove when deprecating Pydantic v1
|
||||||
|
{"title": "Age", "type": "integer"}
|
||||||
|
),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"HeroRead": {
|
"HeroRead": {
|
||||||
@ -297,7 +307,16 @@ def test_tutorial(clear_sqlmodel):
|
|||||||
"properties": {
|
"properties": {
|
||||||
"name": {"title": "Name", "type": "string"},
|
"name": {"title": "Name", "type": "string"},
|
||||||
"secret_name": {"title": "Secret Name", "type": "string"},
|
"secret_name": {"title": "Secret Name", "type": "string"},
|
||||||
"age": {"title": "Age", "type": "integer"},
|
"age": IsDict(
|
||||||
|
{
|
||||||
|
"title": "Age",
|
||||||
|
"anyOf": [{"type": "integer"}, {"type": "null"}],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
| IsDict(
|
||||||
|
# TODO: remove when deprecating Pydantic v1
|
||||||
|
{"title": "Age", "type": "integer"}
|
||||||
|
),
|
||||||
"id": {"title": "Id", "type": "integer"},
|
"id": {"title": "Id", "type": "integer"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -305,9 +324,36 @@ def test_tutorial(clear_sqlmodel):
|
|||||||
"title": "HeroUpdate",
|
"title": "HeroUpdate",
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
"name": {"title": "Name", "type": "string"},
|
"name": IsDict(
|
||||||
"secret_name": {"title": "Secret Name", "type": "string"},
|
{
|
||||||
"age": {"title": "Age", "type": "integer"},
|
"title": "Name",
|
||||||
|
"anyOf": [{"type": "string"}, {"type": "null"}],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
| IsDict(
|
||||||
|
# TODO: remove when deprecating Pydantic v1
|
||||||
|
{"title": "Name", "type": "string"}
|
||||||
|
),
|
||||||
|
"secret_name": IsDict(
|
||||||
|
{
|
||||||
|
"title": "Secret Name",
|
||||||
|
"anyOf": [{"type": "string"}, {"type": "null"}],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
| IsDict(
|
||||||
|
# TODO: remove when deprecating Pydantic v1
|
||||||
|
{"title": "Secret Name", "type": "string"}
|
||||||
|
),
|
||||||
|
"age": IsDict(
|
||||||
|
{
|
||||||
|
"title": "Age",
|
||||||
|
"anyOf": [{"type": "integer"}, {"type": "null"}],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
| IsDict(
|
||||||
|
# TODO: remove when deprecating Pydantic v1
|
||||||
|
{"title": "Age", "type": "integer"}
|
||||||
|
),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"ValidationError": {
|
"ValidationError": {
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
from dirty_equals import IsDict
|
||||||
from fastapi.testclient import TestClient
|
from fastapi.testclient import TestClient
|
||||||
from sqlmodel import create_engine
|
from sqlmodel import create_engine
|
||||||
from sqlmodel.pool import StaticPool
|
from sqlmodel.pool import StaticPool
|
||||||
@ -217,7 +218,16 @@ def test_tutorial(clear_sqlmodel):
|
|||||||
"properties": {
|
"properties": {
|
||||||
"name": {"title": "Name", "type": "string"},
|
"name": {"title": "Name", "type": "string"},
|
||||||
"secret_name": {"title": "Secret Name", "type": "string"},
|
"secret_name": {"title": "Secret Name", "type": "string"},
|
||||||
"age": {"title": "Age", "type": "integer"},
|
"age": IsDict(
|
||||||
|
{
|
||||||
|
"title": "Age",
|
||||||
|
"anyOf": [{"type": "integer"}, {"type": "null"}],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
| IsDict(
|
||||||
|
# TODO: remove when deprecating Pydantic v1
|
||||||
|
{"title": "Age", "type": "integer"}
|
||||||
|
),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"HeroRead": {
|
"HeroRead": {
|
||||||
@ -227,7 +237,16 @@ def test_tutorial(clear_sqlmodel):
|
|||||||
"properties": {
|
"properties": {
|
||||||
"name": {"title": "Name", "type": "string"},
|
"name": {"title": "Name", "type": "string"},
|
||||||
"secret_name": {"title": "Secret Name", "type": "string"},
|
"secret_name": {"title": "Secret Name", "type": "string"},
|
||||||
"age": {"title": "Age", "type": "integer"},
|
"age": IsDict(
|
||||||
|
{
|
||||||
|
"title": "Age",
|
||||||
|
"anyOf": [{"type": "integer"}, {"type": "null"}],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
| IsDict(
|
||||||
|
# TODO: remove when deprecating Pydantic v1
|
||||||
|
{"title": "Age", "type": "integer"}
|
||||||
|
),
|
||||||
"id": {"title": "Id", "type": "integer"},
|
"id": {"title": "Id", "type": "integer"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
from dirty_equals import IsDict
|
||||||
from fastapi.testclient import TestClient
|
from fastapi.testclient import TestClient
|
||||||
from sqlmodel import create_engine
|
from sqlmodel import create_engine
|
||||||
from sqlmodel.pool import StaticPool
|
from sqlmodel.pool import StaticPool
|
||||||
@ -220,7 +221,16 @@ def test_tutorial(clear_sqlmodel):
|
|||||||
"properties": {
|
"properties": {
|
||||||
"name": {"title": "Name", "type": "string"},
|
"name": {"title": "Name", "type": "string"},
|
||||||
"secret_name": {"title": "Secret Name", "type": "string"},
|
"secret_name": {"title": "Secret Name", "type": "string"},
|
||||||
"age": {"title": "Age", "type": "integer"},
|
"age": IsDict(
|
||||||
|
{
|
||||||
|
"title": "Age",
|
||||||
|
"anyOf": [{"type": "integer"}, {"type": "null"}],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
| IsDict(
|
||||||
|
# TODO: remove when deprecating Pydantic v1
|
||||||
|
{"title": "Age", "type": "integer"}
|
||||||
|
),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"HeroRead": {
|
"HeroRead": {
|
||||||
@ -230,7 +240,16 @@ def test_tutorial(clear_sqlmodel):
|
|||||||
"properties": {
|
"properties": {
|
||||||
"name": {"title": "Name", "type": "string"},
|
"name": {"title": "Name", "type": "string"},
|
||||||
"secret_name": {"title": "Secret Name", "type": "string"},
|
"secret_name": {"title": "Secret Name", "type": "string"},
|
||||||
"age": {"title": "Age", "type": "integer"},
|
"age": IsDict(
|
||||||
|
{
|
||||||
|
"title": "Age",
|
||||||
|
"anyOf": [{"type": "integer"}, {"type": "null"}],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
| IsDict(
|
||||||
|
# TODO: remove when deprecating Pydantic v1
|
||||||
|
{"title": "Age", "type": "integer"}
|
||||||
|
),
|
||||||
"id": {"title": "Id", "type": "integer"},
|
"id": {"title": "Id", "type": "integer"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
from dirty_equals import IsDict
|
||||||
from fastapi.testclient import TestClient
|
from fastapi.testclient import TestClient
|
||||||
from sqlmodel import create_engine
|
from sqlmodel import create_engine
|
||||||
from sqlmodel.pool import StaticPool
|
from sqlmodel.pool import StaticPool
|
||||||
@ -220,7 +221,16 @@ def test_tutorial(clear_sqlmodel):
|
|||||||
"properties": {
|
"properties": {
|
||||||
"name": {"title": "Name", "type": "string"},
|
"name": {"title": "Name", "type": "string"},
|
||||||
"secret_name": {"title": "Secret Name", "type": "string"},
|
"secret_name": {"title": "Secret Name", "type": "string"},
|
||||||
"age": {"title": "Age", "type": "integer"},
|
"age": IsDict(
|
||||||
|
{
|
||||||
|
"title": "Age",
|
||||||
|
"anyOf": [{"type": "integer"}, {"type": "null"}],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
| IsDict(
|
||||||
|
# TODO: remove when deprecating Pydantic v1
|
||||||
|
{"title": "Age", "type": "integer"}
|
||||||
|
),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"HeroRead": {
|
"HeroRead": {
|
||||||
@ -230,7 +240,16 @@ def test_tutorial(clear_sqlmodel):
|
|||||||
"properties": {
|
"properties": {
|
||||||
"name": {"title": "Name", "type": "string"},
|
"name": {"title": "Name", "type": "string"},
|
||||||
"secret_name": {"title": "Secret Name", "type": "string"},
|
"secret_name": {"title": "Secret Name", "type": "string"},
|
||||||
"age": {"title": "Age", "type": "integer"},
|
"age": IsDict(
|
||||||
|
{
|
||||||
|
"title": "Age",
|
||||||
|
"anyOf": [{"type": "integer"}, {"type": "null"}],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
| IsDict(
|
||||||
|
# TODO: remove when deprecating Pydantic v1
|
||||||
|
{"title": "Age", "type": "integer"}
|
||||||
|
),
|
||||||
"id": {"title": "Id", "type": "integer"},
|
"id": {"title": "Id", "type": "integer"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
from dirty_equals import IsDict
|
||||||
from fastapi.testclient import TestClient
|
from fastapi.testclient import TestClient
|
||||||
from sqlalchemy import inspect
|
from sqlalchemy import inspect
|
||||||
from sqlalchemy.engine.reflection import Inspector
|
from sqlalchemy.engine.reflection import Inspector
|
||||||
@ -53,11 +54,10 @@ def test_tutorial(clear_sqlmodel):
|
|||||||
assert data[1]["id"] != hero2_data["id"]
|
assert data[1]["id"] != hero2_data["id"]
|
||||||
|
|
||||||
response = client.get("/openapi.json")
|
response = client.get("/openapi.json")
|
||||||
data = response.json()
|
|
||||||
|
|
||||||
assert response.status_code == 200, response.text
|
assert response.status_code == 200, response.text
|
||||||
|
|
||||||
assert data == {
|
assert response.json() == {
|
||||||
"openapi": "3.1.0",
|
"openapi": "3.1.0",
|
||||||
"info": {"title": "FastAPI", "version": "0.1.0"},
|
"info": {"title": "FastAPI", "version": "0.1.0"},
|
||||||
"paths": {
|
"paths": {
|
||||||
@ -142,7 +142,16 @@ def test_tutorial(clear_sqlmodel):
|
|||||||
"properties": {
|
"properties": {
|
||||||
"name": {"title": "Name", "type": "string"},
|
"name": {"title": "Name", "type": "string"},
|
||||||
"secret_name": {"title": "Secret Name", "type": "string"},
|
"secret_name": {"title": "Secret Name", "type": "string"},
|
||||||
"age": {"title": "Age", "type": "integer"},
|
"age": IsDict(
|
||||||
|
{
|
||||||
|
"title": "Age",
|
||||||
|
"anyOf": [{"type": "integer"}, {"type": "null"}],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
| IsDict(
|
||||||
|
# TODO: remove when deprecating Pydantic v1
|
||||||
|
{"title": "Age", "type": "integer"}
|
||||||
|
),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"HeroRead": {
|
"HeroRead": {
|
||||||
@ -153,7 +162,16 @@ def test_tutorial(clear_sqlmodel):
|
|||||||
"id": {"title": "Id", "type": "integer"},
|
"id": {"title": "Id", "type": "integer"},
|
||||||
"name": {"title": "Name", "type": "string"},
|
"name": {"title": "Name", "type": "string"},
|
||||||
"secret_name": {"title": "Secret Name", "type": "string"},
|
"secret_name": {"title": "Secret Name", "type": "string"},
|
||||||
"age": {"title": "Age", "type": "integer"},
|
"age": IsDict(
|
||||||
|
{
|
||||||
|
"title": "Age",
|
||||||
|
"anyOf": [{"type": "integer"}, {"type": "null"}],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
| IsDict(
|
||||||
|
# TODO: remove when deprecating Pydantic v1
|
||||||
|
{"title": "Age", "type": "integer"}
|
||||||
|
),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"ValidationError": {
|
"ValidationError": {
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
from dirty_equals import IsDict
|
||||||
from fastapi.testclient import TestClient
|
from fastapi.testclient import TestClient
|
||||||
from sqlalchemy import inspect
|
from sqlalchemy import inspect
|
||||||
from sqlalchemy.engine.reflection import Inspector
|
from sqlalchemy.engine.reflection import Inspector
|
||||||
@ -56,11 +57,9 @@ def test_tutorial(clear_sqlmodel):
|
|||||||
assert data[1]["id"] != hero2_data["id"]
|
assert data[1]["id"] != hero2_data["id"]
|
||||||
|
|
||||||
response = client.get("/openapi.json")
|
response = client.get("/openapi.json")
|
||||||
data = response.json()
|
|
||||||
|
|
||||||
assert response.status_code == 200, response.text
|
assert response.status_code == 200, response.text
|
||||||
|
|
||||||
assert data == {
|
assert response.json() == {
|
||||||
"openapi": "3.1.0",
|
"openapi": "3.1.0",
|
||||||
"info": {"title": "FastAPI", "version": "0.1.0"},
|
"info": {"title": "FastAPI", "version": "0.1.0"},
|
||||||
"paths": {
|
"paths": {
|
||||||
@ -145,7 +144,16 @@ def test_tutorial(clear_sqlmodel):
|
|||||||
"properties": {
|
"properties": {
|
||||||
"name": {"title": "Name", "type": "string"},
|
"name": {"title": "Name", "type": "string"},
|
||||||
"secret_name": {"title": "Secret Name", "type": "string"},
|
"secret_name": {"title": "Secret Name", "type": "string"},
|
||||||
"age": {"title": "Age", "type": "integer"},
|
"age": IsDict(
|
||||||
|
{
|
||||||
|
"title": "Age",
|
||||||
|
"anyOf": [{"type": "integer"}, {"type": "null"}],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
| IsDict(
|
||||||
|
# TODO: remove when deprecating Pydantic v1
|
||||||
|
{"title": "Age", "type": "integer"}
|
||||||
|
),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"HeroRead": {
|
"HeroRead": {
|
||||||
@ -156,7 +164,16 @@ def test_tutorial(clear_sqlmodel):
|
|||||||
"id": {"title": "Id", "type": "integer"},
|
"id": {"title": "Id", "type": "integer"},
|
||||||
"name": {"title": "Name", "type": "string"},
|
"name": {"title": "Name", "type": "string"},
|
||||||
"secret_name": {"title": "Secret Name", "type": "string"},
|
"secret_name": {"title": "Secret Name", "type": "string"},
|
||||||
"age": {"title": "Age", "type": "integer"},
|
"age": IsDict(
|
||||||
|
{
|
||||||
|
"title": "Age",
|
||||||
|
"anyOf": [{"type": "integer"}, {"type": "null"}],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
| IsDict(
|
||||||
|
# TODO: remove when deprecating Pydantic v1
|
||||||
|
{"title": "Age", "type": "integer"}
|
||||||
|
),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"ValidationError": {
|
"ValidationError": {
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
from dirty_equals import IsDict
|
||||||
from fastapi.testclient import TestClient
|
from fastapi.testclient import TestClient
|
||||||
from sqlalchemy import inspect
|
from sqlalchemy import inspect
|
||||||
from sqlalchemy.engine.reflection import Inspector
|
from sqlalchemy.engine.reflection import Inspector
|
||||||
@ -56,11 +57,10 @@ def test_tutorial(clear_sqlmodel):
|
|||||||
assert data[1]["id"] != hero2_data["id"]
|
assert data[1]["id"] != hero2_data["id"]
|
||||||
|
|
||||||
response = client.get("/openapi.json")
|
response = client.get("/openapi.json")
|
||||||
data = response.json()
|
|
||||||
|
|
||||||
assert response.status_code == 200, response.text
|
assert response.status_code == 200, response.text
|
||||||
|
|
||||||
assert data == {
|
assert response.json() == {
|
||||||
"openapi": "3.1.0",
|
"openapi": "3.1.0",
|
||||||
"info": {"title": "FastAPI", "version": "0.1.0"},
|
"info": {"title": "FastAPI", "version": "0.1.0"},
|
||||||
"paths": {
|
"paths": {
|
||||||
@ -145,7 +145,16 @@ def test_tutorial(clear_sqlmodel):
|
|||||||
"properties": {
|
"properties": {
|
||||||
"name": {"title": "Name", "type": "string"},
|
"name": {"title": "Name", "type": "string"},
|
||||||
"secret_name": {"title": "Secret Name", "type": "string"},
|
"secret_name": {"title": "Secret Name", "type": "string"},
|
||||||
"age": {"title": "Age", "type": "integer"},
|
"age": IsDict(
|
||||||
|
{
|
||||||
|
"title": "Age",
|
||||||
|
"anyOf": [{"type": "integer"}, {"type": "null"}],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
| IsDict(
|
||||||
|
# TODO: remove when deprecating Pydantic v1
|
||||||
|
{"title": "Age", "type": "integer"}
|
||||||
|
),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"HeroRead": {
|
"HeroRead": {
|
||||||
@ -156,7 +165,16 @@ def test_tutorial(clear_sqlmodel):
|
|||||||
"id": {"title": "Id", "type": "integer"},
|
"id": {"title": "Id", "type": "integer"},
|
||||||
"name": {"title": "Name", "type": "string"},
|
"name": {"title": "Name", "type": "string"},
|
||||||
"secret_name": {"title": "Secret Name", "type": "string"},
|
"secret_name": {"title": "Secret Name", "type": "string"},
|
||||||
"age": {"title": "Age", "type": "integer"},
|
"age": IsDict(
|
||||||
|
{
|
||||||
|
"title": "Age",
|
||||||
|
"anyOf": [{"type": "integer"}, {"type": "null"}],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
| IsDict(
|
||||||
|
# TODO: remove when deprecating Pydantic v1
|
||||||
|
{"title": "Age", "type": "integer"}
|
||||||
|
),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"ValidationError": {
|
"ValidationError": {
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
from dirty_equals import IsDict
|
||||||
from fastapi.testclient import TestClient
|
from fastapi.testclient import TestClient
|
||||||
from sqlalchemy import inspect
|
from sqlalchemy import inspect
|
||||||
from sqlalchemy.engine.reflection import Inspector
|
from sqlalchemy.engine.reflection import Inspector
|
||||||
@ -53,11 +54,10 @@ def test_tutorial(clear_sqlmodel):
|
|||||||
assert data[1]["id"] != hero2_data["id"]
|
assert data[1]["id"] != hero2_data["id"]
|
||||||
|
|
||||||
response = client.get("/openapi.json")
|
response = client.get("/openapi.json")
|
||||||
data = response.json()
|
|
||||||
|
|
||||||
assert response.status_code == 200, response.text
|
assert response.status_code == 200, response.text
|
||||||
|
|
||||||
assert data == {
|
assert response.json() == {
|
||||||
"openapi": "3.1.0",
|
"openapi": "3.1.0",
|
||||||
"info": {"title": "FastAPI", "version": "0.1.0"},
|
"info": {"title": "FastAPI", "version": "0.1.0"},
|
||||||
"paths": {
|
"paths": {
|
||||||
@ -142,7 +142,16 @@ def test_tutorial(clear_sqlmodel):
|
|||||||
"properties": {
|
"properties": {
|
||||||
"name": {"title": "Name", "type": "string"},
|
"name": {"title": "Name", "type": "string"},
|
||||||
"secret_name": {"title": "Secret Name", "type": "string"},
|
"secret_name": {"title": "Secret Name", "type": "string"},
|
||||||
"age": {"title": "Age", "type": "integer"},
|
"age": IsDict(
|
||||||
|
{
|
||||||
|
"title": "Age",
|
||||||
|
"anyOf": [{"type": "integer"}, {"type": "null"}],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
| IsDict(
|
||||||
|
# TODO: remove when deprecating Pydantic v1
|
||||||
|
{"title": "Age", "type": "integer"}
|
||||||
|
),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"HeroRead": {
|
"HeroRead": {
|
||||||
@ -152,7 +161,16 @@ def test_tutorial(clear_sqlmodel):
|
|||||||
"properties": {
|
"properties": {
|
||||||
"name": {"title": "Name", "type": "string"},
|
"name": {"title": "Name", "type": "string"},
|
||||||
"secret_name": {"title": "Secret Name", "type": "string"},
|
"secret_name": {"title": "Secret Name", "type": "string"},
|
||||||
"age": {"title": "Age", "type": "integer"},
|
"age": IsDict(
|
||||||
|
{
|
||||||
|
"title": "Age",
|
||||||
|
"anyOf": [{"type": "integer"}, {"type": "null"}],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
| IsDict(
|
||||||
|
# TODO: remove when deprecating Pydantic v1
|
||||||
|
{"title": "Age", "type": "integer"}
|
||||||
|
),
|
||||||
"id": {"title": "Id", "type": "integer"},
|
"id": {"title": "Id", "type": "integer"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
from dirty_equals import IsDict
|
||||||
from fastapi.testclient import TestClient
|
from fastapi.testclient import TestClient
|
||||||
from sqlalchemy import inspect
|
from sqlalchemy import inspect
|
||||||
from sqlalchemy.engine.reflection import Inspector
|
from sqlalchemy.engine.reflection import Inspector
|
||||||
@ -56,11 +57,10 @@ def test_tutorial(clear_sqlmodel):
|
|||||||
assert data[1]["id"] != hero2_data["id"]
|
assert data[1]["id"] != hero2_data["id"]
|
||||||
|
|
||||||
response = client.get("/openapi.json")
|
response = client.get("/openapi.json")
|
||||||
data = response.json()
|
|
||||||
|
|
||||||
assert response.status_code == 200, response.text
|
assert response.status_code == 200, response.text
|
||||||
|
|
||||||
assert data == {
|
assert response.json() == {
|
||||||
"openapi": "3.1.0",
|
"openapi": "3.1.0",
|
||||||
"info": {"title": "FastAPI", "version": "0.1.0"},
|
"info": {"title": "FastAPI", "version": "0.1.0"},
|
||||||
"paths": {
|
"paths": {
|
||||||
@ -145,7 +145,16 @@ def test_tutorial(clear_sqlmodel):
|
|||||||
"properties": {
|
"properties": {
|
||||||
"name": {"title": "Name", "type": "string"},
|
"name": {"title": "Name", "type": "string"},
|
||||||
"secret_name": {"title": "Secret Name", "type": "string"},
|
"secret_name": {"title": "Secret Name", "type": "string"},
|
||||||
"age": {"title": "Age", "type": "integer"},
|
"age": IsDict(
|
||||||
|
{
|
||||||
|
"title": "Age",
|
||||||
|
"anyOf": [{"type": "integer"}, {"type": "null"}],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
| IsDict(
|
||||||
|
# TODO: remove when deprecating Pydantic v1
|
||||||
|
{"title": "Age", "type": "integer"}
|
||||||
|
),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"HeroRead": {
|
"HeroRead": {
|
||||||
@ -155,7 +164,16 @@ def test_tutorial(clear_sqlmodel):
|
|||||||
"properties": {
|
"properties": {
|
||||||
"name": {"title": "Name", "type": "string"},
|
"name": {"title": "Name", "type": "string"},
|
||||||
"secret_name": {"title": "Secret Name", "type": "string"},
|
"secret_name": {"title": "Secret Name", "type": "string"},
|
||||||
"age": {"title": "Age", "type": "integer"},
|
"age": IsDict(
|
||||||
|
{
|
||||||
|
"title": "Age",
|
||||||
|
"anyOf": [{"type": "integer"}, {"type": "null"}],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
| IsDict(
|
||||||
|
# TODO: remove when deprecating Pydantic v1
|
||||||
|
{"title": "Age", "type": "integer"}
|
||||||
|
),
|
||||||
"id": {"title": "Id", "type": "integer"},
|
"id": {"title": "Id", "type": "integer"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
from dirty_equals import IsDict
|
||||||
from fastapi.testclient import TestClient
|
from fastapi.testclient import TestClient
|
||||||
from sqlalchemy import inspect
|
from sqlalchemy import inspect
|
||||||
from sqlalchemy.engine.reflection import Inspector
|
from sqlalchemy.engine.reflection import Inspector
|
||||||
@ -56,11 +57,10 @@ def test_tutorial(clear_sqlmodel):
|
|||||||
assert data[1]["id"] != hero2_data["id"]
|
assert data[1]["id"] != hero2_data["id"]
|
||||||
|
|
||||||
response = client.get("/openapi.json")
|
response = client.get("/openapi.json")
|
||||||
data = response.json()
|
|
||||||
|
|
||||||
assert response.status_code == 200, response.text
|
assert response.status_code == 200, response.text
|
||||||
|
|
||||||
assert data == {
|
assert response.json() == {
|
||||||
"openapi": "3.1.0",
|
"openapi": "3.1.0",
|
||||||
"info": {"title": "FastAPI", "version": "0.1.0"},
|
"info": {"title": "FastAPI", "version": "0.1.0"},
|
||||||
"paths": {
|
"paths": {
|
||||||
@ -145,7 +145,16 @@ def test_tutorial(clear_sqlmodel):
|
|||||||
"properties": {
|
"properties": {
|
||||||
"name": {"title": "Name", "type": "string"},
|
"name": {"title": "Name", "type": "string"},
|
||||||
"secret_name": {"title": "Secret Name", "type": "string"},
|
"secret_name": {"title": "Secret Name", "type": "string"},
|
||||||
"age": {"title": "Age", "type": "integer"},
|
"age": IsDict(
|
||||||
|
{
|
||||||
|
"title": "Age",
|
||||||
|
"anyOf": [{"type": "integer"}, {"type": "null"}],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
| IsDict(
|
||||||
|
# TODO: remove when deprecating Pydantic v1
|
||||||
|
{"title": "Age", "type": "integer"}
|
||||||
|
),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"HeroRead": {
|
"HeroRead": {
|
||||||
@ -155,7 +164,16 @@ def test_tutorial(clear_sqlmodel):
|
|||||||
"properties": {
|
"properties": {
|
||||||
"name": {"title": "Name", "type": "string"},
|
"name": {"title": "Name", "type": "string"},
|
||||||
"secret_name": {"title": "Secret Name", "type": "string"},
|
"secret_name": {"title": "Secret Name", "type": "string"},
|
||||||
"age": {"title": "Age", "type": "integer"},
|
"age": IsDict(
|
||||||
|
{
|
||||||
|
"title": "Age",
|
||||||
|
"anyOf": [{"type": "integer"}, {"type": "null"}],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
| IsDict(
|
||||||
|
# TODO: remove when deprecating Pydantic v1
|
||||||
|
{"title": "Age", "type": "integer"}
|
||||||
|
),
|
||||||
"id": {"title": "Id", "type": "integer"},
|
"id": {"title": "Id", "type": "integer"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
from dirty_equals import IsDict
|
||||||
from fastapi.testclient import TestClient
|
from fastapi.testclient import TestClient
|
||||||
from sqlmodel import create_engine
|
from sqlmodel import create_engine
|
||||||
from sqlmodel.pool import StaticPool
|
from sqlmodel.pool import StaticPool
|
||||||
@ -38,11 +39,10 @@ def test_tutorial(clear_sqlmodel):
|
|||||||
assert response.status_code == 404, response.text
|
assert response.status_code == 404, response.text
|
||||||
|
|
||||||
response = client.get("/openapi.json")
|
response = client.get("/openapi.json")
|
||||||
data = response.json()
|
|
||||||
|
|
||||||
assert response.status_code == 200, response.text
|
assert response.status_code == 200, response.text
|
||||||
|
|
||||||
assert data == {
|
assert response.json() == {
|
||||||
"openapi": "3.1.0",
|
"openapi": "3.1.0",
|
||||||
"info": {"title": "FastAPI", "version": "0.1.0"},
|
"info": {"title": "FastAPI", "version": "0.1.0"},
|
||||||
"paths": {
|
"paths": {
|
||||||
@ -163,7 +163,16 @@ def test_tutorial(clear_sqlmodel):
|
|||||||
"properties": {
|
"properties": {
|
||||||
"name": {"title": "Name", "type": "string"},
|
"name": {"title": "Name", "type": "string"},
|
||||||
"secret_name": {"title": "Secret Name", "type": "string"},
|
"secret_name": {"title": "Secret Name", "type": "string"},
|
||||||
"age": {"title": "Age", "type": "integer"},
|
"age": IsDict(
|
||||||
|
{
|
||||||
|
"title": "Age",
|
||||||
|
"anyOf": [{"type": "integer"}, {"type": "null"}],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
| IsDict(
|
||||||
|
# TODO: remove when deprecating Pydantic v1
|
||||||
|
{"title": "Age", "type": "integer"}
|
||||||
|
),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"HeroRead": {
|
"HeroRead": {
|
||||||
@ -173,7 +182,16 @@ def test_tutorial(clear_sqlmodel):
|
|||||||
"properties": {
|
"properties": {
|
||||||
"name": {"title": "Name", "type": "string"},
|
"name": {"title": "Name", "type": "string"},
|
||||||
"secret_name": {"title": "Secret Name", "type": "string"},
|
"secret_name": {"title": "Secret Name", "type": "string"},
|
||||||
"age": {"title": "Age", "type": "integer"},
|
"age": IsDict(
|
||||||
|
{
|
||||||
|
"title": "Age",
|
||||||
|
"anyOf": [{"type": "integer"}, {"type": "null"}],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
| IsDict(
|
||||||
|
# TODO: remove when deprecating Pydantic v1
|
||||||
|
{"title": "Age", "type": "integer"}
|
||||||
|
),
|
||||||
"id": {"title": "Id", "type": "integer"},
|
"id": {"title": "Id", "type": "integer"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
from dirty_equals import IsDict
|
||||||
from fastapi.testclient import TestClient
|
from fastapi.testclient import TestClient
|
||||||
from sqlmodel import create_engine
|
from sqlmodel import create_engine
|
||||||
from sqlmodel.pool import StaticPool
|
from sqlmodel.pool import StaticPool
|
||||||
@ -41,11 +42,10 @@ def test_tutorial(clear_sqlmodel):
|
|||||||
assert response.status_code == 404, response.text
|
assert response.status_code == 404, response.text
|
||||||
|
|
||||||
response = client.get("/openapi.json")
|
response = client.get("/openapi.json")
|
||||||
data = response.json()
|
|
||||||
|
|
||||||
assert response.status_code == 200, response.text
|
assert response.status_code == 200, response.text
|
||||||
|
|
||||||
assert data == {
|
assert response.json() == {
|
||||||
"openapi": "3.1.0",
|
"openapi": "3.1.0",
|
||||||
"info": {"title": "FastAPI", "version": "0.1.0"},
|
"info": {"title": "FastAPI", "version": "0.1.0"},
|
||||||
"paths": {
|
"paths": {
|
||||||
@ -166,7 +166,16 @@ def test_tutorial(clear_sqlmodel):
|
|||||||
"properties": {
|
"properties": {
|
||||||
"name": {"title": "Name", "type": "string"},
|
"name": {"title": "Name", "type": "string"},
|
||||||
"secret_name": {"title": "Secret Name", "type": "string"},
|
"secret_name": {"title": "Secret Name", "type": "string"},
|
||||||
"age": {"title": "Age", "type": "integer"},
|
"age": IsDict(
|
||||||
|
{
|
||||||
|
"title": "Age",
|
||||||
|
"anyOf": [{"type": "integer"}, {"type": "null"}],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
| IsDict(
|
||||||
|
# TODO: remove when deprecating Pydantic v1
|
||||||
|
{"title": "Age", "type": "integer"}
|
||||||
|
),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"HeroRead": {
|
"HeroRead": {
|
||||||
@ -176,7 +185,16 @@ def test_tutorial(clear_sqlmodel):
|
|||||||
"properties": {
|
"properties": {
|
||||||
"name": {"title": "Name", "type": "string"},
|
"name": {"title": "Name", "type": "string"},
|
||||||
"secret_name": {"title": "Secret Name", "type": "string"},
|
"secret_name": {"title": "Secret Name", "type": "string"},
|
||||||
"age": {"title": "Age", "type": "integer"},
|
"age": IsDict(
|
||||||
|
{
|
||||||
|
"title": "Age",
|
||||||
|
"anyOf": [{"type": "integer"}, {"type": "null"}],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
| IsDict(
|
||||||
|
# TODO: remove when deprecating Pydantic v1
|
||||||
|
{"title": "Age", "type": "integer"}
|
||||||
|
),
|
||||||
"id": {"title": "Id", "type": "integer"},
|
"id": {"title": "Id", "type": "integer"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
from dirty_equals import IsDict
|
||||||
from fastapi.testclient import TestClient
|
from fastapi.testclient import TestClient
|
||||||
from sqlmodel import create_engine
|
from sqlmodel import create_engine
|
||||||
from sqlmodel.pool import StaticPool
|
from sqlmodel.pool import StaticPool
|
||||||
@ -41,11 +42,10 @@ def test_tutorial(clear_sqlmodel):
|
|||||||
assert response.status_code == 404, response.text
|
assert response.status_code == 404, response.text
|
||||||
|
|
||||||
response = client.get("/openapi.json")
|
response = client.get("/openapi.json")
|
||||||
data = response.json()
|
|
||||||
|
|
||||||
assert response.status_code == 200, response.text
|
assert response.status_code == 200, response.text
|
||||||
|
|
||||||
assert data == {
|
assert response.json() == {
|
||||||
"openapi": "3.1.0",
|
"openapi": "3.1.0",
|
||||||
"info": {"title": "FastAPI", "version": "0.1.0"},
|
"info": {"title": "FastAPI", "version": "0.1.0"},
|
||||||
"paths": {
|
"paths": {
|
||||||
@ -166,7 +166,16 @@ def test_tutorial(clear_sqlmodel):
|
|||||||
"properties": {
|
"properties": {
|
||||||
"name": {"title": "Name", "type": "string"},
|
"name": {"title": "Name", "type": "string"},
|
||||||
"secret_name": {"title": "Secret Name", "type": "string"},
|
"secret_name": {"title": "Secret Name", "type": "string"},
|
||||||
"age": {"title": "Age", "type": "integer"},
|
"age": IsDict(
|
||||||
|
{
|
||||||
|
"title": "Age",
|
||||||
|
"anyOf": [{"type": "integer"}, {"type": "null"}],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
| IsDict(
|
||||||
|
# TODO: remove when deprecating Pydantic v1
|
||||||
|
{"title": "Age", "type": "integer"}
|
||||||
|
),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"HeroRead": {
|
"HeroRead": {
|
||||||
@ -176,7 +185,16 @@ def test_tutorial(clear_sqlmodel):
|
|||||||
"properties": {
|
"properties": {
|
||||||
"name": {"title": "Name", "type": "string"},
|
"name": {"title": "Name", "type": "string"},
|
||||||
"secret_name": {"title": "Secret Name", "type": "string"},
|
"secret_name": {"title": "Secret Name", "type": "string"},
|
||||||
"age": {"title": "Age", "type": "integer"},
|
"age": IsDict(
|
||||||
|
{
|
||||||
|
"title": "Age",
|
||||||
|
"anyOf": [{"type": "integer"}, {"type": "null"}],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
| IsDict(
|
||||||
|
# TODO: remove when deprecating Pydantic v1
|
||||||
|
{"title": "Age", "type": "integer"}
|
||||||
|
),
|
||||||
"id": {"title": "Id", "type": "integer"},
|
"id": {"title": "Id", "type": "integer"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
from dirty_equals import IsDict
|
||||||
from fastapi.testclient import TestClient
|
from fastapi.testclient import TestClient
|
||||||
from sqlmodel import create_engine
|
from sqlmodel import create_engine
|
||||||
from sqlmodel.pool import StaticPool
|
from sqlmodel.pool import StaticPool
|
||||||
@ -531,8 +532,26 @@ def test_tutorial(clear_sqlmodel):
|
|||||||
"properties": {
|
"properties": {
|
||||||
"name": {"title": "Name", "type": "string"},
|
"name": {"title": "Name", "type": "string"},
|
||||||
"secret_name": {"title": "Secret Name", "type": "string"},
|
"secret_name": {"title": "Secret Name", "type": "string"},
|
||||||
"age": {"title": "Age", "type": "integer"},
|
"age": IsDict(
|
||||||
"team_id": {"title": "Team Id", "type": "integer"},
|
{
|
||||||
|
"title": "Age",
|
||||||
|
"anyOf": [{"type": "integer"}, {"type": "null"}],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
| IsDict(
|
||||||
|
# TODO: remove when deprecating Pydantic v1
|
||||||
|
{"title": "Age", "type": "integer"}
|
||||||
|
),
|
||||||
|
"team_id": IsDict(
|
||||||
|
{
|
||||||
|
"title": "Team Id",
|
||||||
|
"anyOf": [{"type": "integer"}, {"type": "null"}],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
| IsDict(
|
||||||
|
# TODO: remove when deprecating Pydantic v1
|
||||||
|
{"title": "Team Id", "type": "integer"}
|
||||||
|
),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"HeroRead": {
|
"HeroRead": {
|
||||||
@ -542,8 +561,26 @@ def test_tutorial(clear_sqlmodel):
|
|||||||
"properties": {
|
"properties": {
|
||||||
"name": {"title": "Name", "type": "string"},
|
"name": {"title": "Name", "type": "string"},
|
||||||
"secret_name": {"title": "Secret Name", "type": "string"},
|
"secret_name": {"title": "Secret Name", "type": "string"},
|
||||||
"age": {"title": "Age", "type": "integer"},
|
"age": IsDict(
|
||||||
"team_id": {"title": "Team Id", "type": "integer"},
|
{
|
||||||
|
"title": "Age",
|
||||||
|
"anyOf": [{"type": "integer"}, {"type": "null"}],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
| IsDict(
|
||||||
|
# TODO: remove when deprecating Pydantic v1
|
||||||
|
{"title": "Age", "type": "integer"}
|
||||||
|
),
|
||||||
|
"team_id": IsDict(
|
||||||
|
{
|
||||||
|
"title": "Team Id",
|
||||||
|
"anyOf": [{"type": "integer"}, {"type": "null"}],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
| IsDict(
|
||||||
|
# TODO: remove when deprecating Pydantic v1
|
||||||
|
{"title": "Team Id", "type": "integer"}
|
||||||
|
),
|
||||||
"id": {"title": "Id", "type": "integer"},
|
"id": {"title": "Id", "type": "integer"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -554,20 +591,85 @@ def test_tutorial(clear_sqlmodel):
|
|||||||
"properties": {
|
"properties": {
|
||||||
"name": {"title": "Name", "type": "string"},
|
"name": {"title": "Name", "type": "string"},
|
||||||
"secret_name": {"title": "Secret Name", "type": "string"},
|
"secret_name": {"title": "Secret Name", "type": "string"},
|
||||||
"age": {"title": "Age", "type": "integer"},
|
"age": IsDict(
|
||||||
"team_id": {"title": "Team Id", "type": "integer"},
|
{
|
||||||
|
"title": "Age",
|
||||||
|
"anyOf": [{"type": "integer"}, {"type": "null"}],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
| IsDict(
|
||||||
|
# TODO: remove when deprecating Pydantic v1
|
||||||
|
{"title": "Age", "type": "integer"}
|
||||||
|
),
|
||||||
|
"team_id": IsDict(
|
||||||
|
{
|
||||||
|
"title": "Team Id",
|
||||||
|
"anyOf": [{"type": "integer"}, {"type": "null"}],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
| IsDict(
|
||||||
|
# TODO: remove when deprecating Pydantic v1
|
||||||
|
{"title": "Team Id", "type": "integer"}
|
||||||
|
),
|
||||||
"id": {"title": "Id", "type": "integer"},
|
"id": {"title": "Id", "type": "integer"},
|
||||||
"team": {"$ref": "#/components/schemas/TeamRead"},
|
"team": IsDict(
|
||||||
|
{
|
||||||
|
"anyOf": [
|
||||||
|
{"$ref": "#/components/schemas/TeamRead"},
|
||||||
|
{"type": "null"},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
)
|
||||||
|
| IsDict(
|
||||||
|
# TODO: remove when deprecating Pydantic v1
|
||||||
|
{"$ref": "#/components/schemas/TeamRead"}
|
||||||
|
),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"HeroUpdate": {
|
"HeroUpdate": {
|
||||||
"title": "HeroUpdate",
|
"title": "HeroUpdate",
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
"name": {"title": "Name", "type": "string"},
|
"name": IsDict(
|
||||||
"secret_name": {"title": "Secret Name", "type": "string"},
|
{
|
||||||
"age": {"title": "Age", "type": "integer"},
|
"title": "Name",
|
||||||
"team_id": {"title": "Team Id", "type": "integer"},
|
"anyOf": [{"type": "string"}, {"type": "null"}],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
| IsDict(
|
||||||
|
# TODO: remove when deprecating Pydantic v1
|
||||||
|
{"title": "Name", "type": "string"}
|
||||||
|
),
|
||||||
|
"secret_name": IsDict(
|
||||||
|
{
|
||||||
|
"title": "Secret Name",
|
||||||
|
"anyOf": [{"type": "string"}, {"type": "null"}],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
| IsDict(
|
||||||
|
# TODO: remove when deprecating Pydantic v1
|
||||||
|
{"title": "Secret Name", "type": "string"}
|
||||||
|
),
|
||||||
|
"age": IsDict(
|
||||||
|
{
|
||||||
|
"title": "Age",
|
||||||
|
"anyOf": [{"type": "integer"}, {"type": "null"}],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
| IsDict(
|
||||||
|
# TODO: remove when deprecating Pydantic v1
|
||||||
|
{"title": "Age", "type": "integer"}
|
||||||
|
),
|
||||||
|
"team_id": IsDict(
|
||||||
|
{
|
||||||
|
"title": "Team Id",
|
||||||
|
"anyOf": [{"type": "integer"}, {"type": "null"}],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
| IsDict(
|
||||||
|
# TODO: remove when deprecating Pydantic v1
|
||||||
|
{"title": "Team Id", "type": "integer"}
|
||||||
|
),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"TeamCreate": {
|
"TeamCreate": {
|
||||||
@ -609,9 +711,36 @@ def test_tutorial(clear_sqlmodel):
|
|||||||
"title": "TeamUpdate",
|
"title": "TeamUpdate",
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
"id": {"title": "Id", "type": "integer"},
|
"id": IsDict(
|
||||||
"name": {"title": "Name", "type": "string"},
|
{
|
||||||
"headquarters": {"title": "Headquarters", "type": "string"},
|
"title": "Id",
|
||||||
|
"anyOf": [{"type": "integer"}, {"type": "null"}],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
| IsDict(
|
||||||
|
# TODO: remove when deprecating Pydantic v1
|
||||||
|
{"title": "Id", "type": "integer"}
|
||||||
|
),
|
||||||
|
"name": IsDict(
|
||||||
|
{
|
||||||
|
"title": "Name",
|
||||||
|
"anyOf": [{"type": "string"}, {"type": "null"}],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
| IsDict(
|
||||||
|
# TODO: remove when deprecating Pydantic v1
|
||||||
|
{"title": "Name", "type": "string"}
|
||||||
|
),
|
||||||
|
"headquarters": IsDict(
|
||||||
|
{
|
||||||
|
"title": "Headquarters",
|
||||||
|
"anyOf": [{"type": "string"}, {"type": "null"}],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
| IsDict(
|
||||||
|
# TODO: remove when deprecating Pydantic v1
|
||||||
|
{"title": "Headquarters", "type": "string"}
|
||||||
|
),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"ValidationError": {
|
"ValidationError": {
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
from dirty_equals import IsDict
|
||||||
from fastapi.testclient import TestClient
|
from fastapi.testclient import TestClient
|
||||||
from sqlmodel import create_engine
|
from sqlmodel import create_engine
|
||||||
from sqlmodel.pool import StaticPool
|
from sqlmodel.pool import StaticPool
|
||||||
@ -534,8 +535,26 @@ def test_tutorial(clear_sqlmodel):
|
|||||||
"properties": {
|
"properties": {
|
||||||
"name": {"title": "Name", "type": "string"},
|
"name": {"title": "Name", "type": "string"},
|
||||||
"secret_name": {"title": "Secret Name", "type": "string"},
|
"secret_name": {"title": "Secret Name", "type": "string"},
|
||||||
"age": {"title": "Age", "type": "integer"},
|
"age": IsDict(
|
||||||
"team_id": {"title": "Team Id", "type": "integer"},
|
{
|
||||||
|
"title": "Age",
|
||||||
|
"anyOf": [{"type": "integer"}, {"type": "null"}],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
| IsDict(
|
||||||
|
# TODO: remove when deprecating Pydantic v1
|
||||||
|
{"title": "Age", "type": "integer"}
|
||||||
|
),
|
||||||
|
"team_id": IsDict(
|
||||||
|
{
|
||||||
|
"title": "Team Id",
|
||||||
|
"anyOf": [{"type": "integer"}, {"type": "null"}],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
| IsDict(
|
||||||
|
# TODO: remove when deprecating Pydantic v1
|
||||||
|
{"title": "Team Id", "type": "integer"}
|
||||||
|
),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"HeroRead": {
|
"HeroRead": {
|
||||||
@ -545,8 +564,26 @@ def test_tutorial(clear_sqlmodel):
|
|||||||
"properties": {
|
"properties": {
|
||||||
"name": {"title": "Name", "type": "string"},
|
"name": {"title": "Name", "type": "string"},
|
||||||
"secret_name": {"title": "Secret Name", "type": "string"},
|
"secret_name": {"title": "Secret Name", "type": "string"},
|
||||||
"age": {"title": "Age", "type": "integer"},
|
"age": IsDict(
|
||||||
"team_id": {"title": "Team Id", "type": "integer"},
|
{
|
||||||
|
"title": "Age",
|
||||||
|
"anyOf": [{"type": "integer"}, {"type": "null"}],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
| IsDict(
|
||||||
|
# TODO: remove when deprecating Pydantic v1
|
||||||
|
{"title": "Age", "type": "integer"}
|
||||||
|
),
|
||||||
|
"team_id": IsDict(
|
||||||
|
{
|
||||||
|
"title": "Team Id",
|
||||||
|
"anyOf": [{"type": "integer"}, {"type": "null"}],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
| IsDict(
|
||||||
|
# TODO: remove when deprecating Pydantic v1
|
||||||
|
{"title": "Team Id", "type": "integer"}
|
||||||
|
),
|
||||||
"id": {"title": "Id", "type": "integer"},
|
"id": {"title": "Id", "type": "integer"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -557,20 +594,85 @@ def test_tutorial(clear_sqlmodel):
|
|||||||
"properties": {
|
"properties": {
|
||||||
"name": {"title": "Name", "type": "string"},
|
"name": {"title": "Name", "type": "string"},
|
||||||
"secret_name": {"title": "Secret Name", "type": "string"},
|
"secret_name": {"title": "Secret Name", "type": "string"},
|
||||||
"age": {"title": "Age", "type": "integer"},
|
"age": IsDict(
|
||||||
"team_id": {"title": "Team Id", "type": "integer"},
|
{
|
||||||
|
"title": "Age",
|
||||||
|
"anyOf": [{"type": "integer"}, {"type": "null"}],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
| IsDict(
|
||||||
|
# TODO: remove when deprecating Pydantic v1
|
||||||
|
{"title": "Age", "type": "integer"}
|
||||||
|
),
|
||||||
|
"team_id": IsDict(
|
||||||
|
{
|
||||||
|
"title": "Team Id",
|
||||||
|
"anyOf": [{"type": "integer"}, {"type": "null"}],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
| IsDict(
|
||||||
|
# TODO: remove when deprecating Pydantic v1
|
||||||
|
{"title": "Team Id", "type": "integer"}
|
||||||
|
),
|
||||||
"id": {"title": "Id", "type": "integer"},
|
"id": {"title": "Id", "type": "integer"},
|
||||||
"team": {"$ref": "#/components/schemas/TeamRead"},
|
"team": IsDict(
|
||||||
|
{
|
||||||
|
"anyOf": [
|
||||||
|
{"$ref": "#/components/schemas/TeamRead"},
|
||||||
|
{"type": "null"},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
)
|
||||||
|
| IsDict(
|
||||||
|
# TODO: remove when deprecating Pydantic v1
|
||||||
|
{"$ref": "#/components/schemas/TeamRead"}
|
||||||
|
),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"HeroUpdate": {
|
"HeroUpdate": {
|
||||||
"title": "HeroUpdate",
|
"title": "HeroUpdate",
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
"name": {"title": "Name", "type": "string"},
|
"name": IsDict(
|
||||||
"secret_name": {"title": "Secret Name", "type": "string"},
|
{
|
||||||
"age": {"title": "Age", "type": "integer"},
|
"title": "Name",
|
||||||
"team_id": {"title": "Team Id", "type": "integer"},
|
"anyOf": [{"type": "string"}, {"type": "null"}],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
| IsDict(
|
||||||
|
# TODO: remove when deprecating Pydantic v1
|
||||||
|
{"title": "Name", "type": "string"}
|
||||||
|
),
|
||||||
|
"secret_name": IsDict(
|
||||||
|
{
|
||||||
|
"title": "Secret Name",
|
||||||
|
"anyOf": [{"type": "string"}, {"type": "null"}],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
| IsDict(
|
||||||
|
# TODO: remove when deprecating Pydantic v1
|
||||||
|
{"title": "Secret Name", "type": "string"}
|
||||||
|
),
|
||||||
|
"age": IsDict(
|
||||||
|
{
|
||||||
|
"title": "Age",
|
||||||
|
"anyOf": [{"type": "integer"}, {"type": "null"}],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
| IsDict(
|
||||||
|
# TODO: remove when deprecating Pydantic v1
|
||||||
|
{"title": "Age", "type": "integer"}
|
||||||
|
),
|
||||||
|
"team_id": IsDict(
|
||||||
|
{
|
||||||
|
"title": "Team Id",
|
||||||
|
"anyOf": [{"type": "integer"}, {"type": "null"}],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
| IsDict(
|
||||||
|
# TODO: remove when deprecating Pydantic v1
|
||||||
|
{"title": "Team Id", "type": "integer"}
|
||||||
|
),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"TeamCreate": {
|
"TeamCreate": {
|
||||||
@ -612,9 +714,36 @@ def test_tutorial(clear_sqlmodel):
|
|||||||
"title": "TeamUpdate",
|
"title": "TeamUpdate",
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
"id": {"title": "Id", "type": "integer"},
|
"id": IsDict(
|
||||||
"name": {"title": "Name", "type": "string"},
|
{
|
||||||
"headquarters": {"title": "Headquarters", "type": "string"},
|
"title": "Id",
|
||||||
|
"anyOf": [{"type": "integer"}, {"type": "null"}],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
| IsDict(
|
||||||
|
# TODO: remove when deprecating Pydantic v1
|
||||||
|
{"title": "Id", "type": "integer"}
|
||||||
|
),
|
||||||
|
"name": IsDict(
|
||||||
|
{
|
||||||
|
"title": "Name",
|
||||||
|
"anyOf": [{"type": "string"}, {"type": "null"}],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
| IsDict(
|
||||||
|
# TODO: remove when deprecating Pydantic v1
|
||||||
|
{"title": "Name", "type": "string"}
|
||||||
|
),
|
||||||
|
"headquarters": IsDict(
|
||||||
|
{
|
||||||
|
"title": "Headquarters",
|
||||||
|
"anyOf": [{"type": "string"}, {"type": "null"}],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
| IsDict(
|
||||||
|
# TODO: remove when deprecating Pydantic v1
|
||||||
|
{"title": "Headquarters", "type": "string"}
|
||||||
|
),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"ValidationError": {
|
"ValidationError": {
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
from dirty_equals import IsDict
|
||||||
from fastapi.testclient import TestClient
|
from fastapi.testclient import TestClient
|
||||||
from sqlmodel import create_engine
|
from sqlmodel import create_engine
|
||||||
from sqlmodel.pool import StaticPool
|
from sqlmodel.pool import StaticPool
|
||||||
@ -534,8 +535,26 @@ def test_tutorial(clear_sqlmodel):
|
|||||||
"properties": {
|
"properties": {
|
||||||
"name": {"title": "Name", "type": "string"},
|
"name": {"title": "Name", "type": "string"},
|
||||||
"secret_name": {"title": "Secret Name", "type": "string"},
|
"secret_name": {"title": "Secret Name", "type": "string"},
|
||||||
"age": {"title": "Age", "type": "integer"},
|
"age": IsDict(
|
||||||
"team_id": {"title": "Team Id", "type": "integer"},
|
{
|
||||||
|
"title": "Age",
|
||||||
|
"anyOf": [{"type": "integer"}, {"type": "null"}],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
| IsDict(
|
||||||
|
# TODO: remove when deprecating Pydantic v1
|
||||||
|
{"title": "Age", "type": "integer"}
|
||||||
|
),
|
||||||
|
"team_id": IsDict(
|
||||||
|
{
|
||||||
|
"title": "Team Id",
|
||||||
|
"anyOf": [{"type": "integer"}, {"type": "null"}],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
| IsDict(
|
||||||
|
# TODO: remove when deprecating Pydantic v1
|
||||||
|
{"title": "Team Id", "type": "integer"}
|
||||||
|
),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"HeroRead": {
|
"HeroRead": {
|
||||||
@ -545,8 +564,26 @@ def test_tutorial(clear_sqlmodel):
|
|||||||
"properties": {
|
"properties": {
|
||||||
"name": {"title": "Name", "type": "string"},
|
"name": {"title": "Name", "type": "string"},
|
||||||
"secret_name": {"title": "Secret Name", "type": "string"},
|
"secret_name": {"title": "Secret Name", "type": "string"},
|
||||||
"age": {"title": "Age", "type": "integer"},
|
"age": IsDict(
|
||||||
"team_id": {"title": "Team Id", "type": "integer"},
|
{
|
||||||
|
"title": "Age",
|
||||||
|
"anyOf": [{"type": "integer"}, {"type": "null"}],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
| IsDict(
|
||||||
|
# TODO: remove when deprecating Pydantic v1
|
||||||
|
{"title": "Age", "type": "integer"}
|
||||||
|
),
|
||||||
|
"team_id": IsDict(
|
||||||
|
{
|
||||||
|
"title": "Team Id",
|
||||||
|
"anyOf": [{"type": "integer"}, {"type": "null"}],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
| IsDict(
|
||||||
|
# TODO: remove when deprecating Pydantic v1
|
||||||
|
{"title": "Team Id", "type": "integer"}
|
||||||
|
),
|
||||||
"id": {"title": "Id", "type": "integer"},
|
"id": {"title": "Id", "type": "integer"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -557,20 +594,85 @@ def test_tutorial(clear_sqlmodel):
|
|||||||
"properties": {
|
"properties": {
|
||||||
"name": {"title": "Name", "type": "string"},
|
"name": {"title": "Name", "type": "string"},
|
||||||
"secret_name": {"title": "Secret Name", "type": "string"},
|
"secret_name": {"title": "Secret Name", "type": "string"},
|
||||||
"age": {"title": "Age", "type": "integer"},
|
"age": IsDict(
|
||||||
"team_id": {"title": "Team Id", "type": "integer"},
|
{
|
||||||
|
"title": "Age",
|
||||||
|
"anyOf": [{"type": "integer"}, {"type": "null"}],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
| IsDict(
|
||||||
|
# TODO: remove when deprecating Pydantic v1
|
||||||
|
{"title": "Age", "type": "integer"}
|
||||||
|
),
|
||||||
|
"team_id": IsDict(
|
||||||
|
{
|
||||||
|
"title": "Team Id",
|
||||||
|
"anyOf": [{"type": "integer"}, {"type": "null"}],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
| IsDict(
|
||||||
|
# TODO: remove when deprecating Pydantic v1
|
||||||
|
{"title": "Team Id", "type": "integer"}
|
||||||
|
),
|
||||||
"id": {"title": "Id", "type": "integer"},
|
"id": {"title": "Id", "type": "integer"},
|
||||||
"team": {"$ref": "#/components/schemas/TeamRead"},
|
"team": IsDict(
|
||||||
|
{
|
||||||
|
"anyOf": [
|
||||||
|
{"$ref": "#/components/schemas/TeamRead"},
|
||||||
|
{"type": "null"},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
)
|
||||||
|
| IsDict(
|
||||||
|
# TODO: remove when deprecating Pydantic v1
|
||||||
|
{"$ref": "#/components/schemas/TeamRead"}
|
||||||
|
),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"HeroUpdate": {
|
"HeroUpdate": {
|
||||||
"title": "HeroUpdate",
|
"title": "HeroUpdate",
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
"name": {"title": "Name", "type": "string"},
|
"name": IsDict(
|
||||||
"secret_name": {"title": "Secret Name", "type": "string"},
|
{
|
||||||
"age": {"title": "Age", "type": "integer"},
|
"title": "Name",
|
||||||
"team_id": {"title": "Team Id", "type": "integer"},
|
"anyOf": [{"type": "string"}, {"type": "null"}],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
| IsDict(
|
||||||
|
# TODO: remove when deprecating Pydantic v1
|
||||||
|
{"title": "Name", "type": "string"}
|
||||||
|
),
|
||||||
|
"secret_name": IsDict(
|
||||||
|
{
|
||||||
|
"title": "Secret Name",
|
||||||
|
"anyOf": [{"type": "string"}, {"type": "null"}],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
| IsDict(
|
||||||
|
# TODO: remove when deprecating Pydantic v1
|
||||||
|
{"title": "Secret Name", "type": "string"}
|
||||||
|
),
|
||||||
|
"age": IsDict(
|
||||||
|
{
|
||||||
|
"title": "Age",
|
||||||
|
"anyOf": [{"type": "integer"}, {"type": "null"}],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
| IsDict(
|
||||||
|
# TODO: remove when deprecating Pydantic v1
|
||||||
|
{"title": "Age", "type": "integer"}
|
||||||
|
),
|
||||||
|
"team_id": IsDict(
|
||||||
|
{
|
||||||
|
"title": "Team Id",
|
||||||
|
"anyOf": [{"type": "integer"}, {"type": "null"}],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
| IsDict(
|
||||||
|
# TODO: remove when deprecating Pydantic v1
|
||||||
|
{"title": "Team Id", "type": "integer"}
|
||||||
|
),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"TeamCreate": {
|
"TeamCreate": {
|
||||||
@ -612,9 +714,36 @@ def test_tutorial(clear_sqlmodel):
|
|||||||
"title": "TeamUpdate",
|
"title": "TeamUpdate",
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
"id": {"title": "Id", "type": "integer"},
|
"id": IsDict(
|
||||||
"name": {"title": "Name", "type": "string"},
|
{
|
||||||
"headquarters": {"title": "Headquarters", "type": "string"},
|
"title": "Id",
|
||||||
|
"anyOf": [{"type": "integer"}, {"type": "null"}],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
| IsDict(
|
||||||
|
# TODO: remove when deprecating Pydantic v1
|
||||||
|
{"title": "Id", "type": "integer"}
|
||||||
|
),
|
||||||
|
"name": IsDict(
|
||||||
|
{
|
||||||
|
"title": "Name",
|
||||||
|
"anyOf": [{"type": "string"}, {"type": "null"}],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
| IsDict(
|
||||||
|
# TODO: remove when deprecating Pydantic v1
|
||||||
|
{"title": "Name", "type": "string"}
|
||||||
|
),
|
||||||
|
"headquarters": IsDict(
|
||||||
|
{
|
||||||
|
"title": "Headquarters",
|
||||||
|
"anyOf": [{"type": "string"}, {"type": "null"}],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
| IsDict(
|
||||||
|
# TODO: remove when deprecating Pydantic v1
|
||||||
|
{"title": "Headquarters", "type": "string"}
|
||||||
|
),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"ValidationError": {
|
"ValidationError": {
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
from dirty_equals import IsDict
|
||||||
from fastapi.testclient import TestClient
|
from fastapi.testclient import TestClient
|
||||||
from sqlmodel import create_engine
|
from sqlmodel import create_engine
|
||||||
from sqlmodel.pool import StaticPool
|
from sqlmodel.pool import StaticPool
|
||||||
@ -31,11 +32,10 @@ def test_tutorial(clear_sqlmodel):
|
|||||||
assert data[0]["secret_name"] == hero_data["secret_name"]
|
assert data[0]["secret_name"] == hero_data["secret_name"]
|
||||||
|
|
||||||
response = client.get("/openapi.json")
|
response = client.get("/openapi.json")
|
||||||
data = response.json()
|
|
||||||
|
|
||||||
assert response.status_code == 200, response.text
|
assert response.status_code == 200, response.text
|
||||||
|
|
||||||
assert data == {
|
assert response.json() == {
|
||||||
"openapi": "3.1.0",
|
"openapi": "3.1.0",
|
||||||
"info": {"title": "FastAPI", "version": "0.1.0"},
|
"info": {"title": "FastAPI", "version": "0.1.0"},
|
||||||
"paths": {
|
"paths": {
|
||||||
@ -114,10 +114,28 @@ def test_tutorial(clear_sqlmodel):
|
|||||||
"required": ["name", "secret_name"],
|
"required": ["name", "secret_name"],
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
"id": {"title": "Id", "type": "integer"},
|
"id": IsDict(
|
||||||
|
{
|
||||||
|
"title": "Id",
|
||||||
|
"anyOf": [{"type": "integer"}, {"type": "null"}],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
| IsDict(
|
||||||
|
# TODO: remove when deprecating Pydantic v1
|
||||||
|
{"title": "Id", "type": "integer"}
|
||||||
|
),
|
||||||
"name": {"title": "Name", "type": "string"},
|
"name": {"title": "Name", "type": "string"},
|
||||||
"secret_name": {"title": "Secret Name", "type": "string"},
|
"secret_name": {"title": "Secret Name", "type": "string"},
|
||||||
"age": {"title": "Age", "type": "integer"},
|
"age": IsDict(
|
||||||
|
{
|
||||||
|
"title": "Age",
|
||||||
|
"anyOf": [{"type": "integer"}, {"type": "null"}],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
| IsDict(
|
||||||
|
# TODO: remove when deprecating Pydantic v1
|
||||||
|
{"title": "Age", "type": "integer"}
|
||||||
|
),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"ValidationError": {
|
"ValidationError": {
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
from dirty_equals import IsDict
|
||||||
from fastapi.testclient import TestClient
|
from fastapi.testclient import TestClient
|
||||||
from sqlmodel import create_engine
|
from sqlmodel import create_engine
|
||||||
from sqlmodel.pool import StaticPool
|
from sqlmodel.pool import StaticPool
|
||||||
@ -34,11 +35,10 @@ def test_tutorial(clear_sqlmodel):
|
|||||||
assert data[0]["secret_name"] == hero_data["secret_name"]
|
assert data[0]["secret_name"] == hero_data["secret_name"]
|
||||||
|
|
||||||
response = client.get("/openapi.json")
|
response = client.get("/openapi.json")
|
||||||
data = response.json()
|
|
||||||
|
|
||||||
assert response.status_code == 200, response.text
|
assert response.status_code == 200, response.text
|
||||||
|
|
||||||
assert data == {
|
assert response.json() == {
|
||||||
"openapi": "3.1.0",
|
"openapi": "3.1.0",
|
||||||
"info": {"title": "FastAPI", "version": "0.1.0"},
|
"info": {"title": "FastAPI", "version": "0.1.0"},
|
||||||
"paths": {
|
"paths": {
|
||||||
@ -117,10 +117,28 @@ def test_tutorial(clear_sqlmodel):
|
|||||||
"required": ["name", "secret_name"],
|
"required": ["name", "secret_name"],
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
"id": {"title": "Id", "type": "integer"},
|
"id": IsDict(
|
||||||
|
{
|
||||||
|
"title": "Id",
|
||||||
|
"anyOf": [{"type": "integer"}, {"type": "null"}],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
| IsDict(
|
||||||
|
# TODO: remove when deprecating Pydantic v1
|
||||||
|
{"title": "Id", "type": "integer"}
|
||||||
|
),
|
||||||
"name": {"title": "Name", "type": "string"},
|
"name": {"title": "Name", "type": "string"},
|
||||||
"secret_name": {"title": "Secret Name", "type": "string"},
|
"secret_name": {"title": "Secret Name", "type": "string"},
|
||||||
"age": {"title": "Age", "type": "integer"},
|
"age": IsDict(
|
||||||
|
{
|
||||||
|
"title": "Age",
|
||||||
|
"anyOf": [{"type": "integer"}, {"type": "null"}],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
| IsDict(
|
||||||
|
# TODO: remove when deprecating Pydantic v1
|
||||||
|
{"title": "Age", "type": "integer"}
|
||||||
|
),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"ValidationError": {
|
"ValidationError": {
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
from dirty_equals import IsDict
|
||||||
from fastapi.testclient import TestClient
|
from fastapi.testclient import TestClient
|
||||||
from sqlmodel import create_engine
|
from sqlmodel import create_engine
|
||||||
from sqlmodel.pool import StaticPool
|
from sqlmodel.pool import StaticPool
|
||||||
@ -34,11 +35,10 @@ def test_tutorial(clear_sqlmodel):
|
|||||||
assert data[0]["secret_name"] == hero_data["secret_name"]
|
assert data[0]["secret_name"] == hero_data["secret_name"]
|
||||||
|
|
||||||
response = client.get("/openapi.json")
|
response = client.get("/openapi.json")
|
||||||
data = response.json()
|
|
||||||
|
|
||||||
assert response.status_code == 200, response.text
|
assert response.status_code == 200, response.text
|
||||||
|
|
||||||
assert data == {
|
assert response.json() == {
|
||||||
"openapi": "3.1.0",
|
"openapi": "3.1.0",
|
||||||
"info": {"title": "FastAPI", "version": "0.1.0"},
|
"info": {"title": "FastAPI", "version": "0.1.0"},
|
||||||
"paths": {
|
"paths": {
|
||||||
@ -117,10 +117,28 @@ def test_tutorial(clear_sqlmodel):
|
|||||||
"required": ["name", "secret_name"],
|
"required": ["name", "secret_name"],
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
"id": {"title": "Id", "type": "integer"},
|
"id": IsDict(
|
||||||
|
{
|
||||||
|
"title": "Id",
|
||||||
|
"anyOf": [{"type": "integer"}, {"type": "null"}],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
| IsDict(
|
||||||
|
# TODO: remove when deprecating Pydantic v1
|
||||||
|
{"title": "Id", "type": "integer"}
|
||||||
|
),
|
||||||
"name": {"title": "Name", "type": "string"},
|
"name": {"title": "Name", "type": "string"},
|
||||||
"secret_name": {"title": "Secret Name", "type": "string"},
|
"secret_name": {"title": "Secret Name", "type": "string"},
|
||||||
"age": {"title": "Age", "type": "integer"},
|
"age": IsDict(
|
||||||
|
{
|
||||||
|
"title": "Age",
|
||||||
|
"anyOf": [{"type": "integer"}, {"type": "null"}],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
| IsDict(
|
||||||
|
# TODO: remove when deprecating Pydantic v1
|
||||||
|
{"title": "Age", "type": "integer"}
|
||||||
|
),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"ValidationError": {
|
"ValidationError": {
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
from dirty_equals import IsDict
|
||||||
from fastapi.testclient import TestClient
|
from fastapi.testclient import TestClient
|
||||||
from sqlmodel import create_engine
|
from sqlmodel import create_engine
|
||||||
from sqlmodel.pool import StaticPool
|
from sqlmodel.pool import StaticPool
|
||||||
@ -284,7 +285,16 @@ def test_tutorial(clear_sqlmodel):
|
|||||||
"properties": {
|
"properties": {
|
||||||
"name": {"title": "Name", "type": "string"},
|
"name": {"title": "Name", "type": "string"},
|
||||||
"secret_name": {"title": "Secret Name", "type": "string"},
|
"secret_name": {"title": "Secret Name", "type": "string"},
|
||||||
"age": {"title": "Age", "type": "integer"},
|
"age": IsDict(
|
||||||
|
{
|
||||||
|
"title": "Age",
|
||||||
|
"anyOf": [{"type": "integer"}, {"type": "null"}],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
| IsDict(
|
||||||
|
# TODO: remove when deprecating Pydantic v1
|
||||||
|
{"title": "Age", "type": "integer"}
|
||||||
|
),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"HeroRead": {
|
"HeroRead": {
|
||||||
@ -294,7 +304,16 @@ def test_tutorial(clear_sqlmodel):
|
|||||||
"properties": {
|
"properties": {
|
||||||
"name": {"title": "Name", "type": "string"},
|
"name": {"title": "Name", "type": "string"},
|
||||||
"secret_name": {"title": "Secret Name", "type": "string"},
|
"secret_name": {"title": "Secret Name", "type": "string"},
|
||||||
"age": {"title": "Age", "type": "integer"},
|
"age": IsDict(
|
||||||
|
{
|
||||||
|
"title": "Age",
|
||||||
|
"anyOf": [{"type": "integer"}, {"type": "null"}],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
| IsDict(
|
||||||
|
# TODO: remove when deprecating Pydantic v1
|
||||||
|
{"title": "Age", "type": "integer"}
|
||||||
|
),
|
||||||
"id": {"title": "Id", "type": "integer"},
|
"id": {"title": "Id", "type": "integer"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -302,9 +321,36 @@ def test_tutorial(clear_sqlmodel):
|
|||||||
"title": "HeroUpdate",
|
"title": "HeroUpdate",
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
"name": {"title": "Name", "type": "string"},
|
"name": IsDict(
|
||||||
"secret_name": {"title": "Secret Name", "type": "string"},
|
{
|
||||||
"age": {"title": "Age", "type": "integer"},
|
"title": "Name",
|
||||||
|
"anyOf": [{"type": "string"}, {"type": "null"}],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
| IsDict(
|
||||||
|
# TODO: remove when deprecating Pydantic v1
|
||||||
|
{"title": "Name", "type": "string"}
|
||||||
|
),
|
||||||
|
"secret_name": IsDict(
|
||||||
|
{
|
||||||
|
"title": "Secret Name",
|
||||||
|
"anyOf": [{"type": "string"}, {"type": "null"}],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
| IsDict(
|
||||||
|
# TODO: remove when deprecating Pydantic v1
|
||||||
|
{"title": "Secret Name", "type": "string"}
|
||||||
|
),
|
||||||
|
"age": IsDict(
|
||||||
|
{
|
||||||
|
"title": "Age",
|
||||||
|
"anyOf": [{"type": "integer"}, {"type": "null"}],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
| IsDict(
|
||||||
|
# TODO: remove when deprecating Pydantic v1
|
||||||
|
{"title": "Age", "type": "integer"}
|
||||||
|
),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"ValidationError": {
|
"ValidationError": {
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
from dirty_equals import IsDict
|
||||||
from fastapi.testclient import TestClient
|
from fastapi.testclient import TestClient
|
||||||
from sqlmodel import create_engine
|
from sqlmodel import create_engine
|
||||||
from sqlmodel.pool import StaticPool
|
from sqlmodel.pool import StaticPool
|
||||||
@ -289,7 +290,16 @@ def test_tutorial(clear_sqlmodel):
|
|||||||
"properties": {
|
"properties": {
|
||||||
"name": {"title": "Name", "type": "string"},
|
"name": {"title": "Name", "type": "string"},
|
||||||
"secret_name": {"title": "Secret Name", "type": "string"},
|
"secret_name": {"title": "Secret Name", "type": "string"},
|
||||||
"age": {"title": "Age", "type": "integer"},
|
"age": IsDict(
|
||||||
|
{
|
||||||
|
"title": "Age",
|
||||||
|
"anyOf": [{"type": "integer"}, {"type": "null"}],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
| IsDict(
|
||||||
|
# TODO: remove when deprecating Pydantic v1
|
||||||
|
{"title": "Age", "type": "integer"}
|
||||||
|
),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"HeroRead": {
|
"HeroRead": {
|
||||||
@ -299,7 +309,16 @@ def test_tutorial(clear_sqlmodel):
|
|||||||
"properties": {
|
"properties": {
|
||||||
"name": {"title": "Name", "type": "string"},
|
"name": {"title": "Name", "type": "string"},
|
||||||
"secret_name": {"title": "Secret Name", "type": "string"},
|
"secret_name": {"title": "Secret Name", "type": "string"},
|
||||||
"age": {"title": "Age", "type": "integer"},
|
"age": IsDict(
|
||||||
|
{
|
||||||
|
"title": "Age",
|
||||||
|
"anyOf": [{"type": "integer"}, {"type": "null"}],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
| IsDict(
|
||||||
|
# TODO: remove when deprecating Pydantic v1
|
||||||
|
{"title": "Age", "type": "integer"}
|
||||||
|
),
|
||||||
"id": {"title": "Id", "type": "integer"},
|
"id": {"title": "Id", "type": "integer"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -307,9 +326,36 @@ def test_tutorial(clear_sqlmodel):
|
|||||||
"title": "HeroUpdate",
|
"title": "HeroUpdate",
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
"name": {"title": "Name", "type": "string"},
|
"name": IsDict(
|
||||||
"secret_name": {"title": "Secret Name", "type": "string"},
|
{
|
||||||
"age": {"title": "Age", "type": "integer"},
|
"title": "Name",
|
||||||
|
"anyOf": [{"type": "string"}, {"type": "null"}],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
| IsDict(
|
||||||
|
# TODO: remove when deprecating Pydantic v1
|
||||||
|
{"title": "Name", "type": "string"}
|
||||||
|
),
|
||||||
|
"secret_name": IsDict(
|
||||||
|
{
|
||||||
|
"title": "Secret Name",
|
||||||
|
"anyOf": [{"type": "string"}, {"type": "null"}],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
| IsDict(
|
||||||
|
# TODO: remove when deprecating Pydantic v1
|
||||||
|
{"title": "Secret Name", "type": "string"}
|
||||||
|
),
|
||||||
|
"age": IsDict(
|
||||||
|
{
|
||||||
|
"title": "Age",
|
||||||
|
"anyOf": [{"type": "integer"}, {"type": "null"}],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
| IsDict(
|
||||||
|
# TODO: remove when deprecating Pydantic v1
|
||||||
|
{"title": "Age", "type": "integer"}
|
||||||
|
),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"ValidationError": {
|
"ValidationError": {
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
from dirty_equals import IsDict
|
||||||
from fastapi.testclient import TestClient
|
from fastapi.testclient import TestClient
|
||||||
from sqlmodel import create_engine
|
from sqlmodel import create_engine
|
||||||
from sqlmodel.pool import StaticPool
|
from sqlmodel.pool import StaticPool
|
||||||
@ -289,7 +290,16 @@ def test_tutorial(clear_sqlmodel):
|
|||||||
"properties": {
|
"properties": {
|
||||||
"name": {"title": "Name", "type": "string"},
|
"name": {"title": "Name", "type": "string"},
|
||||||
"secret_name": {"title": "Secret Name", "type": "string"},
|
"secret_name": {"title": "Secret Name", "type": "string"},
|
||||||
"age": {"title": "Age", "type": "integer"},
|
"age": IsDict(
|
||||||
|
{
|
||||||
|
"title": "Age",
|
||||||
|
"anyOf": [{"type": "integer"}, {"type": "null"}],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
| IsDict(
|
||||||
|
# TODO: remove when deprecating Pydantic v1
|
||||||
|
{"title": "Age", "type": "integer"}
|
||||||
|
),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"HeroRead": {
|
"HeroRead": {
|
||||||
@ -299,7 +309,16 @@ def test_tutorial(clear_sqlmodel):
|
|||||||
"properties": {
|
"properties": {
|
||||||
"name": {"title": "Name", "type": "string"},
|
"name": {"title": "Name", "type": "string"},
|
||||||
"secret_name": {"title": "Secret Name", "type": "string"},
|
"secret_name": {"title": "Secret Name", "type": "string"},
|
||||||
"age": {"title": "Age", "type": "integer"},
|
"age": IsDict(
|
||||||
|
{
|
||||||
|
"title": "Age",
|
||||||
|
"anyOf": [{"type": "integer"}, {"type": "null"}],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
| IsDict(
|
||||||
|
# TODO: remove when deprecating Pydantic v1
|
||||||
|
{"title": "Age", "type": "integer"}
|
||||||
|
),
|
||||||
"id": {"title": "Id", "type": "integer"},
|
"id": {"title": "Id", "type": "integer"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -307,9 +326,36 @@ def test_tutorial(clear_sqlmodel):
|
|||||||
"title": "HeroUpdate",
|
"title": "HeroUpdate",
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
"name": {"title": "Name", "type": "string"},
|
"name": IsDict(
|
||||||
"secret_name": {"title": "Secret Name", "type": "string"},
|
{
|
||||||
"age": {"title": "Age", "type": "integer"},
|
"title": "Name",
|
||||||
|
"anyOf": [{"type": "string"}, {"type": "null"}],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
| IsDict(
|
||||||
|
# TODO: remove when deprecating Pydantic v1
|
||||||
|
{"title": "Name", "type": "string"}
|
||||||
|
),
|
||||||
|
"secret_name": IsDict(
|
||||||
|
{
|
||||||
|
"title": "Secret Name",
|
||||||
|
"anyOf": [{"type": "string"}, {"type": "null"}],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
| IsDict(
|
||||||
|
# TODO: remove when deprecating Pydantic v1
|
||||||
|
{"title": "Secret Name", "type": "string"}
|
||||||
|
),
|
||||||
|
"age": IsDict(
|
||||||
|
{
|
||||||
|
"title": "Age",
|
||||||
|
"anyOf": [{"type": "integer"}, {"type": "null"}],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
| IsDict(
|
||||||
|
# TODO: remove when deprecating Pydantic v1
|
||||||
|
{"title": "Age", "type": "integer"}
|
||||||
|
),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"ValidationError": {
|
"ValidationError": {
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
from dirty_equals import IsDict
|
||||||
from fastapi.testclient import TestClient
|
from fastapi.testclient import TestClient
|
||||||
from sqlmodel import create_engine
|
from sqlmodel import create_engine
|
||||||
from sqlmodel.pool import StaticPool
|
from sqlmodel.pool import StaticPool
|
||||||
@ -51,11 +52,10 @@ def test_tutorial(clear_sqlmodel):
|
|||||||
assert data[1]["id"] == hero2_data["id"]
|
assert data[1]["id"] == hero2_data["id"]
|
||||||
|
|
||||||
response = client.get("/openapi.json")
|
response = client.get("/openapi.json")
|
||||||
data = response.json()
|
|
||||||
|
|
||||||
assert response.status_code == 200, response.text
|
assert response.status_code == 200, response.text
|
||||||
|
|
||||||
assert data == {
|
assert response.json() == {
|
||||||
"openapi": "3.1.0",
|
"openapi": "3.1.0",
|
||||||
"info": {"title": "FastAPI", "version": "0.1.0"},
|
"info": {"title": "FastAPI", "version": "0.1.0"},
|
||||||
"paths": {
|
"paths": {
|
||||||
@ -120,10 +120,28 @@ def test_tutorial(clear_sqlmodel):
|
|||||||
"required": ["name", "secret_name"],
|
"required": ["name", "secret_name"],
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
"id": {"title": "Id", "type": "integer"},
|
"id": IsDict(
|
||||||
|
{
|
||||||
|
"title": "Id",
|
||||||
|
"anyOf": [{"type": "integer"}, {"type": "null"}],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
| IsDict(
|
||||||
|
# TODO: remove when deprecating Pydantic v1
|
||||||
|
{"title": "Id", "type": "integer"}
|
||||||
|
),
|
||||||
"name": {"title": "Name", "type": "string"},
|
"name": {"title": "Name", "type": "string"},
|
||||||
"secret_name": {"title": "Secret Name", "type": "string"},
|
"secret_name": {"title": "Secret Name", "type": "string"},
|
||||||
"age": {"title": "Age", "type": "integer"},
|
"age": IsDict(
|
||||||
|
{
|
||||||
|
"title": "Age",
|
||||||
|
"anyOf": [{"type": "integer"}, {"type": "null"}],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
| IsDict(
|
||||||
|
# TODO: remove when deprecating Pydantic v1
|
||||||
|
{"title": "Age", "type": "integer"}
|
||||||
|
),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"ValidationError": {
|
"ValidationError": {
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
from dirty_equals import IsDict
|
||||||
from fastapi.testclient import TestClient
|
from fastapi.testclient import TestClient
|
||||||
from sqlmodel import create_engine
|
from sqlmodel import create_engine
|
||||||
from sqlmodel.pool import StaticPool
|
from sqlmodel.pool import StaticPool
|
||||||
@ -54,11 +55,10 @@ def test_tutorial(clear_sqlmodel):
|
|||||||
assert data[1]["id"] == hero2_data["id"]
|
assert data[1]["id"] == hero2_data["id"]
|
||||||
|
|
||||||
response = client.get("/openapi.json")
|
response = client.get("/openapi.json")
|
||||||
data = response.json()
|
|
||||||
|
|
||||||
assert response.status_code == 200, response.text
|
assert response.status_code == 200, response.text
|
||||||
|
|
||||||
assert data == {
|
assert response.json() == {
|
||||||
"openapi": "3.1.0",
|
"openapi": "3.1.0",
|
||||||
"info": {"title": "FastAPI", "version": "0.1.0"},
|
"info": {"title": "FastAPI", "version": "0.1.0"},
|
||||||
"paths": {
|
"paths": {
|
||||||
@ -123,10 +123,28 @@ def test_tutorial(clear_sqlmodel):
|
|||||||
"required": ["name", "secret_name"],
|
"required": ["name", "secret_name"],
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
"id": {"title": "Id", "type": "integer"},
|
"id": IsDict(
|
||||||
|
{
|
||||||
|
"title": "Id",
|
||||||
|
"anyOf": [{"type": "integer"}, {"type": "null"}],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
| IsDict(
|
||||||
|
# TODO: remove when deprecating Pydantic v1
|
||||||
|
{"title": "Id", "type": "integer"}
|
||||||
|
),
|
||||||
"name": {"title": "Name", "type": "string"},
|
"name": {"title": "Name", "type": "string"},
|
||||||
"secret_name": {"title": "Secret Name", "type": "string"},
|
"secret_name": {"title": "Secret Name", "type": "string"},
|
||||||
"age": {"title": "Age", "type": "integer"},
|
"age": IsDict(
|
||||||
|
{
|
||||||
|
"title": "Age",
|
||||||
|
"anyOf": [{"type": "integer"}, {"type": "null"}],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
| IsDict(
|
||||||
|
# TODO: remove when deprecating Pydantic v1
|
||||||
|
{"title": "Age", "type": "integer"}
|
||||||
|
),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"ValidationError": {
|
"ValidationError": {
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
from dirty_equals import IsDict
|
||||||
from fastapi.testclient import TestClient
|
from fastapi.testclient import TestClient
|
||||||
from sqlmodel import create_engine
|
from sqlmodel import create_engine
|
||||||
from sqlmodel.pool import StaticPool
|
from sqlmodel.pool import StaticPool
|
||||||
@ -518,8 +519,26 @@ def test_tutorial(clear_sqlmodel):
|
|||||||
"properties": {
|
"properties": {
|
||||||
"name": {"title": "Name", "type": "string"},
|
"name": {"title": "Name", "type": "string"},
|
||||||
"secret_name": {"title": "Secret Name", "type": "string"},
|
"secret_name": {"title": "Secret Name", "type": "string"},
|
||||||
"age": {"title": "Age", "type": "integer"},
|
"age": IsDict(
|
||||||
"team_id": {"title": "Team Id", "type": "integer"},
|
{
|
||||||
|
"title": "Age",
|
||||||
|
"anyOf": [{"type": "integer"}, {"type": "null"}],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
| IsDict(
|
||||||
|
# TODO: remove when deprecating Pydantic v1
|
||||||
|
{"title": "Age", "type": "integer"}
|
||||||
|
),
|
||||||
|
"team_id": IsDict(
|
||||||
|
{
|
||||||
|
"title": "Team Id",
|
||||||
|
"anyOf": [{"type": "integer"}, {"type": "null"}],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
| IsDict(
|
||||||
|
# TODO: remove when deprecating Pydantic v1
|
||||||
|
{"title": "Team Id", "type": "integer"}
|
||||||
|
),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"HeroRead": {
|
"HeroRead": {
|
||||||
@ -529,8 +548,26 @@ def test_tutorial(clear_sqlmodel):
|
|||||||
"properties": {
|
"properties": {
|
||||||
"name": {"title": "Name", "type": "string"},
|
"name": {"title": "Name", "type": "string"},
|
||||||
"secret_name": {"title": "Secret Name", "type": "string"},
|
"secret_name": {"title": "Secret Name", "type": "string"},
|
||||||
"age": {"title": "Age", "type": "integer"},
|
"age": IsDict(
|
||||||
"team_id": {"title": "Team Id", "type": "integer"},
|
{
|
||||||
|
"title": "Age",
|
||||||
|
"anyOf": [{"type": "integer"}, {"type": "null"}],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
| IsDict(
|
||||||
|
# TODO: remove when deprecating Pydantic v1
|
||||||
|
{"title": "Age", "type": "integer"}
|
||||||
|
),
|
||||||
|
"team_id": IsDict(
|
||||||
|
{
|
||||||
|
"title": "Team Id",
|
||||||
|
"anyOf": [{"type": "integer"}, {"type": "null"}],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
| IsDict(
|
||||||
|
# TODO: remove when deprecating Pydantic v1
|
||||||
|
{"title": "Team Id", "type": "integer"}
|
||||||
|
),
|
||||||
"id": {"title": "Id", "type": "integer"},
|
"id": {"title": "Id", "type": "integer"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -538,10 +575,46 @@ def test_tutorial(clear_sqlmodel):
|
|||||||
"title": "HeroUpdate",
|
"title": "HeroUpdate",
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
"name": {"title": "Name", "type": "string"},
|
"name": IsDict(
|
||||||
"secret_name": {"title": "Secret Name", "type": "string"},
|
{
|
||||||
"age": {"title": "Age", "type": "integer"},
|
"title": "Name",
|
||||||
"team_id": {"title": "Team Id", "type": "integer"},
|
"anyOf": [{"type": "string"}, {"type": "null"}],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
| IsDict(
|
||||||
|
# TODO: remove when deprecating Pydantic v1
|
||||||
|
{"title": "Name", "type": "string"}
|
||||||
|
),
|
||||||
|
"secret_name": IsDict(
|
||||||
|
{
|
||||||
|
"title": "Secret Name",
|
||||||
|
"anyOf": [{"type": "string"}, {"type": "null"}],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
| IsDict(
|
||||||
|
# TODO: remove when deprecating Pydantic v1
|
||||||
|
{"title": "Secret Name", "type": "string"}
|
||||||
|
),
|
||||||
|
"age": IsDict(
|
||||||
|
{
|
||||||
|
"title": "Age",
|
||||||
|
"anyOf": [{"type": "integer"}, {"type": "null"}],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
| IsDict(
|
||||||
|
# TODO: remove when deprecating Pydantic v1
|
||||||
|
{"title": "Age", "type": "integer"}
|
||||||
|
),
|
||||||
|
"team_id": IsDict(
|
||||||
|
{
|
||||||
|
"title": "Team Id",
|
||||||
|
"anyOf": [{"type": "integer"}, {"type": "null"}],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
| IsDict(
|
||||||
|
# TODO: remove when deprecating Pydantic v1
|
||||||
|
{"title": "Team Id", "type": "integer"}
|
||||||
|
),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"TeamCreate": {
|
"TeamCreate": {
|
||||||
@ -567,8 +640,26 @@ def test_tutorial(clear_sqlmodel):
|
|||||||
"title": "TeamUpdate",
|
"title": "TeamUpdate",
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
"name": {"title": "Name", "type": "string"},
|
"name": IsDict(
|
||||||
"headquarters": {"title": "Headquarters", "type": "string"},
|
{
|
||||||
|
"title": "Name",
|
||||||
|
"anyOf": [{"type": "string"}, {"type": "null"}],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
| IsDict(
|
||||||
|
# TODO: remove when deprecating Pydantic v1
|
||||||
|
{"title": "Name", "type": "string"}
|
||||||
|
),
|
||||||
|
"headquarters": IsDict(
|
||||||
|
{
|
||||||
|
"title": "Headquarters",
|
||||||
|
"anyOf": [{"type": "string"}, {"type": "null"}],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
| IsDict(
|
||||||
|
# TODO: remove when deprecating Pydantic v1
|
||||||
|
{"title": "Headquarters", "type": "string"}
|
||||||
|
),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"ValidationError": {
|
"ValidationError": {
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
from dirty_equals import IsDict
|
||||||
from fastapi.testclient import TestClient
|
from fastapi.testclient import TestClient
|
||||||
from sqlmodel import create_engine
|
from sqlmodel import create_engine
|
||||||
from sqlmodel.pool import StaticPool
|
from sqlmodel.pool import StaticPool
|
||||||
@ -521,8 +522,26 @@ def test_tutorial(clear_sqlmodel):
|
|||||||
"properties": {
|
"properties": {
|
||||||
"name": {"title": "Name", "type": "string"},
|
"name": {"title": "Name", "type": "string"},
|
||||||
"secret_name": {"title": "Secret Name", "type": "string"},
|
"secret_name": {"title": "Secret Name", "type": "string"},
|
||||||
"age": {"title": "Age", "type": "integer"},
|
"age": IsDict(
|
||||||
"team_id": {"title": "Team Id", "type": "integer"},
|
{
|
||||||
|
"title": "Age",
|
||||||
|
"anyOf": [{"type": "integer"}, {"type": "null"}],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
| IsDict(
|
||||||
|
# TODO: remove when deprecating Pydantic v1
|
||||||
|
{"title": "Age", "type": "integer"}
|
||||||
|
),
|
||||||
|
"team_id": IsDict(
|
||||||
|
{
|
||||||
|
"title": "Team Id",
|
||||||
|
"anyOf": [{"type": "integer"}, {"type": "null"}],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
| IsDict(
|
||||||
|
# TODO: remove when deprecating Pydantic v1
|
||||||
|
{"title": "Team Id", "type": "integer"}
|
||||||
|
),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"HeroRead": {
|
"HeroRead": {
|
||||||
@ -532,8 +551,26 @@ def test_tutorial(clear_sqlmodel):
|
|||||||
"properties": {
|
"properties": {
|
||||||
"name": {"title": "Name", "type": "string"},
|
"name": {"title": "Name", "type": "string"},
|
||||||
"secret_name": {"title": "Secret Name", "type": "string"},
|
"secret_name": {"title": "Secret Name", "type": "string"},
|
||||||
"age": {"title": "Age", "type": "integer"},
|
"age": IsDict(
|
||||||
"team_id": {"title": "Team Id", "type": "integer"},
|
{
|
||||||
|
"title": "Age",
|
||||||
|
"anyOf": [{"type": "integer"}, {"type": "null"}],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
| IsDict(
|
||||||
|
# TODO: remove when deprecating Pydantic v1
|
||||||
|
{"title": "Age", "type": "integer"}
|
||||||
|
),
|
||||||
|
"team_id": IsDict(
|
||||||
|
{
|
||||||
|
"title": "Team Id",
|
||||||
|
"anyOf": [{"type": "integer"}, {"type": "null"}],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
| IsDict(
|
||||||
|
# TODO: remove when deprecating Pydantic v1
|
||||||
|
{"title": "Team Id", "type": "integer"}
|
||||||
|
),
|
||||||
"id": {"title": "Id", "type": "integer"},
|
"id": {"title": "Id", "type": "integer"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -541,10 +578,46 @@ def test_tutorial(clear_sqlmodel):
|
|||||||
"title": "HeroUpdate",
|
"title": "HeroUpdate",
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
"name": {"title": "Name", "type": "string"},
|
"name": IsDict(
|
||||||
"secret_name": {"title": "Secret Name", "type": "string"},
|
{
|
||||||
"age": {"title": "Age", "type": "integer"},
|
"title": "Name",
|
||||||
"team_id": {"title": "Team Id", "type": "integer"},
|
"anyOf": [{"type": "string"}, {"type": "null"}],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
| IsDict(
|
||||||
|
# TODO: remove when deprecating Pydantic v1
|
||||||
|
{"title": "Name", "type": "string"}
|
||||||
|
),
|
||||||
|
"secret_name": IsDict(
|
||||||
|
{
|
||||||
|
"title": "Secret Name",
|
||||||
|
"anyOf": [{"type": "string"}, {"type": "null"}],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
| IsDict(
|
||||||
|
# TODO: remove when deprecating Pydantic v1
|
||||||
|
{"title": "Secret Name", "type": "string"}
|
||||||
|
),
|
||||||
|
"age": IsDict(
|
||||||
|
{
|
||||||
|
"title": "Age",
|
||||||
|
"anyOf": [{"type": "integer"}, {"type": "null"}],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
| IsDict(
|
||||||
|
# TODO: remove when deprecating Pydantic v1
|
||||||
|
{"title": "Age", "type": "integer"}
|
||||||
|
),
|
||||||
|
"team_id": IsDict(
|
||||||
|
{
|
||||||
|
"title": "Team Id",
|
||||||
|
"anyOf": [{"type": "integer"}, {"type": "null"}],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
| IsDict(
|
||||||
|
# TODO: remove when deprecating Pydantic v1
|
||||||
|
{"title": "Team Id", "type": "integer"}
|
||||||
|
),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"TeamCreate": {
|
"TeamCreate": {
|
||||||
@ -570,8 +643,26 @@ def test_tutorial(clear_sqlmodel):
|
|||||||
"title": "TeamUpdate",
|
"title": "TeamUpdate",
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
"name": {"title": "Name", "type": "string"},
|
"name": IsDict(
|
||||||
"headquarters": {"title": "Headquarters", "type": "string"},
|
{
|
||||||
|
"title": "Name",
|
||||||
|
"anyOf": [{"type": "string"}, {"type": "null"}],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
| IsDict(
|
||||||
|
# TODO: remove when deprecating Pydantic v1
|
||||||
|
{"title": "Name", "type": "string"}
|
||||||
|
),
|
||||||
|
"headquarters": IsDict(
|
||||||
|
{
|
||||||
|
"title": "Headquarters",
|
||||||
|
"anyOf": [{"type": "string"}, {"type": "null"}],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
| IsDict(
|
||||||
|
# TODO: remove when deprecating Pydantic v1
|
||||||
|
{"title": "Headquarters", "type": "string"}
|
||||||
|
),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"ValidationError": {
|
"ValidationError": {
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
from dirty_equals import IsDict
|
||||||
from fastapi.testclient import TestClient
|
from fastapi.testclient import TestClient
|
||||||
from sqlmodel import create_engine
|
from sqlmodel import create_engine
|
||||||
from sqlmodel.pool import StaticPool
|
from sqlmodel.pool import StaticPool
|
||||||
@ -521,8 +522,26 @@ def test_tutorial(clear_sqlmodel):
|
|||||||
"properties": {
|
"properties": {
|
||||||
"name": {"title": "Name", "type": "string"},
|
"name": {"title": "Name", "type": "string"},
|
||||||
"secret_name": {"title": "Secret Name", "type": "string"},
|
"secret_name": {"title": "Secret Name", "type": "string"},
|
||||||
"age": {"title": "Age", "type": "integer"},
|
"age": IsDict(
|
||||||
"team_id": {"title": "Team Id", "type": "integer"},
|
{
|
||||||
|
"title": "Age",
|
||||||
|
"anyOf": [{"type": "integer"}, {"type": "null"}],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
| IsDict(
|
||||||
|
# TODO: remove when deprecating Pydantic v1
|
||||||
|
{"title": "Age", "type": "integer"}
|
||||||
|
),
|
||||||
|
"team_id": IsDict(
|
||||||
|
{
|
||||||
|
"title": "Team Id",
|
||||||
|
"anyOf": [{"type": "integer"}, {"type": "null"}],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
| IsDict(
|
||||||
|
# TODO: remove when deprecating Pydantic v1
|
||||||
|
{"title": "Team Id", "type": "integer"}
|
||||||
|
),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"HeroRead": {
|
"HeroRead": {
|
||||||
@ -532,8 +551,26 @@ def test_tutorial(clear_sqlmodel):
|
|||||||
"properties": {
|
"properties": {
|
||||||
"name": {"title": "Name", "type": "string"},
|
"name": {"title": "Name", "type": "string"},
|
||||||
"secret_name": {"title": "Secret Name", "type": "string"},
|
"secret_name": {"title": "Secret Name", "type": "string"},
|
||||||
"age": {"title": "Age", "type": "integer"},
|
"age": IsDict(
|
||||||
"team_id": {"title": "Team Id", "type": "integer"},
|
{
|
||||||
|
"title": "Age",
|
||||||
|
"anyOf": [{"type": "integer"}, {"type": "null"}],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
| IsDict(
|
||||||
|
# TODO: remove when deprecating Pydantic v1
|
||||||
|
{"title": "Age", "type": "integer"}
|
||||||
|
),
|
||||||
|
"team_id": IsDict(
|
||||||
|
{
|
||||||
|
"title": "Team Id",
|
||||||
|
"anyOf": [{"type": "integer"}, {"type": "null"}],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
| IsDict(
|
||||||
|
# TODO: remove when deprecating Pydantic v1
|
||||||
|
{"title": "Team Id", "type": "integer"}
|
||||||
|
),
|
||||||
"id": {"title": "Id", "type": "integer"},
|
"id": {"title": "Id", "type": "integer"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -541,10 +578,46 @@ def test_tutorial(clear_sqlmodel):
|
|||||||
"title": "HeroUpdate",
|
"title": "HeroUpdate",
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
"name": {"title": "Name", "type": "string"},
|
"name": IsDict(
|
||||||
"secret_name": {"title": "Secret Name", "type": "string"},
|
{
|
||||||
"age": {"title": "Age", "type": "integer"},
|
"title": "Name",
|
||||||
"team_id": {"title": "Team Id", "type": "integer"},
|
"anyOf": [{"type": "string"}, {"type": "null"}],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
| IsDict(
|
||||||
|
# TODO: remove when deprecating Pydantic v1
|
||||||
|
{"title": "Name", "type": "string"}
|
||||||
|
),
|
||||||
|
"secret_name": IsDict(
|
||||||
|
{
|
||||||
|
"title": "Secret Name",
|
||||||
|
"anyOf": [{"type": "string"}, {"type": "null"}],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
| IsDict(
|
||||||
|
# TODO: remove when deprecating Pydantic v1
|
||||||
|
{"title": "Secret Name", "type": "string"}
|
||||||
|
),
|
||||||
|
"age": IsDict(
|
||||||
|
{
|
||||||
|
"title": "Age",
|
||||||
|
"anyOf": [{"type": "integer"}, {"type": "null"}],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
| IsDict(
|
||||||
|
# TODO: remove when deprecating Pydantic v1
|
||||||
|
{"title": "Age", "type": "integer"}
|
||||||
|
),
|
||||||
|
"team_id": IsDict(
|
||||||
|
{
|
||||||
|
"title": "Team Id",
|
||||||
|
"anyOf": [{"type": "integer"}, {"type": "null"}],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
| IsDict(
|
||||||
|
# TODO: remove when deprecating Pydantic v1
|
||||||
|
{"title": "Team Id", "type": "integer"}
|
||||||
|
),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"TeamCreate": {
|
"TeamCreate": {
|
||||||
@ -570,8 +643,26 @@ def test_tutorial(clear_sqlmodel):
|
|||||||
"title": "TeamUpdate",
|
"title": "TeamUpdate",
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
"name": {"title": "Name", "type": "string"},
|
"name": IsDict(
|
||||||
"headquarters": {"title": "Headquarters", "type": "string"},
|
{
|
||||||
|
"title": "Name",
|
||||||
|
"anyOf": [{"type": "string"}, {"type": "null"}],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
| IsDict(
|
||||||
|
# TODO: remove when deprecating Pydantic v1
|
||||||
|
{"title": "Name", "type": "string"}
|
||||||
|
),
|
||||||
|
"headquarters": IsDict(
|
||||||
|
{
|
||||||
|
"title": "Headquarters",
|
||||||
|
"anyOf": [{"type": "string"}, {"type": "null"}],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
| IsDict(
|
||||||
|
# TODO: remove when deprecating Pydantic v1
|
||||||
|
{"title": "Headquarters", "type": "string"}
|
||||||
|
),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"ValidationError": {
|
"ValidationError": {
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
from dirty_equals import IsDict
|
||||||
from fastapi.testclient import TestClient
|
from fastapi.testclient import TestClient
|
||||||
from sqlmodel import create_engine
|
from sqlmodel import create_engine
|
||||||
from sqlmodel.pool import StaticPool
|
from sqlmodel.pool import StaticPool
|
||||||
@ -263,7 +264,16 @@ def test_tutorial(clear_sqlmodel):
|
|||||||
"properties": {
|
"properties": {
|
||||||
"name": {"title": "Name", "type": "string"},
|
"name": {"title": "Name", "type": "string"},
|
||||||
"secret_name": {"title": "Secret Name", "type": "string"},
|
"secret_name": {"title": "Secret Name", "type": "string"},
|
||||||
"age": {"title": "Age", "type": "integer"},
|
"age": IsDict(
|
||||||
|
{
|
||||||
|
"title": "Age",
|
||||||
|
"anyOf": [{"type": "integer"}, {"type": "null"}],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
| IsDict(
|
||||||
|
# TODO: remove when deprecating Pydantic v1
|
||||||
|
{"title": "Age", "type": "integer"}
|
||||||
|
),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"HeroRead": {
|
"HeroRead": {
|
||||||
@ -273,7 +283,16 @@ def test_tutorial(clear_sqlmodel):
|
|||||||
"properties": {
|
"properties": {
|
||||||
"name": {"title": "Name", "type": "string"},
|
"name": {"title": "Name", "type": "string"},
|
||||||
"secret_name": {"title": "Secret Name", "type": "string"},
|
"secret_name": {"title": "Secret Name", "type": "string"},
|
||||||
"age": {"title": "Age", "type": "integer"},
|
"age": IsDict(
|
||||||
|
{
|
||||||
|
"title": "Age",
|
||||||
|
"anyOf": [{"type": "integer"}, {"type": "null"}],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
| IsDict(
|
||||||
|
# TODO: remove when deprecating Pydantic v1
|
||||||
|
{"title": "Age", "type": "integer"}
|
||||||
|
),
|
||||||
"id": {"title": "Id", "type": "integer"},
|
"id": {"title": "Id", "type": "integer"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -281,9 +300,36 @@ def test_tutorial(clear_sqlmodel):
|
|||||||
"title": "HeroUpdate",
|
"title": "HeroUpdate",
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
"name": {"title": "Name", "type": "string"},
|
"name": IsDict(
|
||||||
"secret_name": {"title": "Secret Name", "type": "string"},
|
{
|
||||||
"age": {"title": "Age", "type": "integer"},
|
"title": "Name",
|
||||||
|
"anyOf": [{"type": "string"}, {"type": "null"}],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
| IsDict(
|
||||||
|
# TODO: remove when deprecating Pydantic v1
|
||||||
|
{"title": "Name", "type": "string"}
|
||||||
|
),
|
||||||
|
"secret_name": IsDict(
|
||||||
|
{
|
||||||
|
"title": "Secret Name",
|
||||||
|
"anyOf": [{"type": "string"}, {"type": "null"}],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
| IsDict(
|
||||||
|
# TODO: remove when deprecating Pydantic v1
|
||||||
|
{"title": "Secret Name", "type": "string"}
|
||||||
|
),
|
||||||
|
"age": IsDict(
|
||||||
|
{
|
||||||
|
"title": "Age",
|
||||||
|
"anyOf": [{"type": "integer"}, {"type": "null"}],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
| IsDict(
|
||||||
|
# TODO: remove when deprecating Pydantic v1
|
||||||
|
{"title": "Age", "type": "integer"}
|
||||||
|
),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"ValidationError": {
|
"ValidationError": {
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
from dirty_equals import IsDict
|
||||||
from fastapi.testclient import TestClient
|
from fastapi.testclient import TestClient
|
||||||
from sqlmodel import create_engine
|
from sqlmodel import create_engine
|
||||||
from sqlmodel.pool import StaticPool
|
from sqlmodel.pool import StaticPool
|
||||||
@ -266,7 +267,16 @@ def test_tutorial(clear_sqlmodel):
|
|||||||
"properties": {
|
"properties": {
|
||||||
"name": {"title": "Name", "type": "string"},
|
"name": {"title": "Name", "type": "string"},
|
||||||
"secret_name": {"title": "Secret Name", "type": "string"},
|
"secret_name": {"title": "Secret Name", "type": "string"},
|
||||||
"age": {"title": "Age", "type": "integer"},
|
"age": IsDict(
|
||||||
|
{
|
||||||
|
"title": "Age",
|
||||||
|
"anyOf": [{"type": "integer"}, {"type": "null"}],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
| IsDict(
|
||||||
|
# TODO: remove when deprecating Pydantic v1
|
||||||
|
{"title": "Age", "type": "integer"}
|
||||||
|
),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"HeroRead": {
|
"HeroRead": {
|
||||||
@ -276,7 +286,16 @@ def test_tutorial(clear_sqlmodel):
|
|||||||
"properties": {
|
"properties": {
|
||||||
"name": {"title": "Name", "type": "string"},
|
"name": {"title": "Name", "type": "string"},
|
||||||
"secret_name": {"title": "Secret Name", "type": "string"},
|
"secret_name": {"title": "Secret Name", "type": "string"},
|
||||||
"age": {"title": "Age", "type": "integer"},
|
"age": IsDict(
|
||||||
|
{
|
||||||
|
"title": "Age",
|
||||||
|
"anyOf": [{"type": "integer"}, {"type": "null"}],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
| IsDict(
|
||||||
|
# TODO: remove when deprecating Pydantic v1
|
||||||
|
{"title": "Age", "type": "integer"}
|
||||||
|
),
|
||||||
"id": {"title": "Id", "type": "integer"},
|
"id": {"title": "Id", "type": "integer"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -284,9 +303,36 @@ def test_tutorial(clear_sqlmodel):
|
|||||||
"title": "HeroUpdate",
|
"title": "HeroUpdate",
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
"name": {"title": "Name", "type": "string"},
|
"name": IsDict(
|
||||||
"secret_name": {"title": "Secret Name", "type": "string"},
|
{
|
||||||
"age": {"title": "Age", "type": "integer"},
|
"title": "Name",
|
||||||
|
"anyOf": [{"type": "string"}, {"type": "null"}],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
| IsDict(
|
||||||
|
# TODO: remove when deprecating Pydantic v1
|
||||||
|
{"title": "Name", "type": "string"}
|
||||||
|
),
|
||||||
|
"secret_name": IsDict(
|
||||||
|
{
|
||||||
|
"title": "Secret Name",
|
||||||
|
"anyOf": [{"type": "string"}, {"type": "null"}],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
| IsDict(
|
||||||
|
# TODO: remove when deprecating Pydantic v1
|
||||||
|
{"title": "Secret Name", "type": "string"}
|
||||||
|
),
|
||||||
|
"age": IsDict(
|
||||||
|
{
|
||||||
|
"title": "Age",
|
||||||
|
"anyOf": [{"type": "integer"}, {"type": "null"}],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
| IsDict(
|
||||||
|
# TODO: remove when deprecating Pydantic v1
|
||||||
|
{"title": "Age", "type": "integer"}
|
||||||
|
),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"ValidationError": {
|
"ValidationError": {
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
from dirty_equals import IsDict
|
||||||
from fastapi.testclient import TestClient
|
from fastapi.testclient import TestClient
|
||||||
from sqlmodel import create_engine
|
from sqlmodel import create_engine
|
||||||
from sqlmodel.pool import StaticPool
|
from sqlmodel.pool import StaticPool
|
||||||
@ -266,7 +267,16 @@ def test_tutorial(clear_sqlmodel):
|
|||||||
"properties": {
|
"properties": {
|
||||||
"name": {"title": "Name", "type": "string"},
|
"name": {"title": "Name", "type": "string"},
|
||||||
"secret_name": {"title": "Secret Name", "type": "string"},
|
"secret_name": {"title": "Secret Name", "type": "string"},
|
||||||
"age": {"title": "Age", "type": "integer"},
|
"age": IsDict(
|
||||||
|
{
|
||||||
|
"title": "Age",
|
||||||
|
"anyOf": [{"type": "integer"}, {"type": "null"}],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
| IsDict(
|
||||||
|
# TODO: remove when deprecating Pydantic v1
|
||||||
|
{"title": "Age", "type": "integer"}
|
||||||
|
),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"HeroRead": {
|
"HeroRead": {
|
||||||
@ -276,7 +286,16 @@ def test_tutorial(clear_sqlmodel):
|
|||||||
"properties": {
|
"properties": {
|
||||||
"name": {"title": "Name", "type": "string"},
|
"name": {"title": "Name", "type": "string"},
|
||||||
"secret_name": {"title": "Secret Name", "type": "string"},
|
"secret_name": {"title": "Secret Name", "type": "string"},
|
||||||
"age": {"title": "Age", "type": "integer"},
|
"age": IsDict(
|
||||||
|
{
|
||||||
|
"title": "Age",
|
||||||
|
"anyOf": [{"type": "integer"}, {"type": "null"}],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
| IsDict(
|
||||||
|
# TODO: remove when deprecating Pydantic v1
|
||||||
|
{"title": "Age", "type": "integer"}
|
||||||
|
),
|
||||||
"id": {"title": "Id", "type": "integer"},
|
"id": {"title": "Id", "type": "integer"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -284,9 +303,36 @@ def test_tutorial(clear_sqlmodel):
|
|||||||
"title": "HeroUpdate",
|
"title": "HeroUpdate",
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
"name": {"title": "Name", "type": "string"},
|
"name": IsDict(
|
||||||
"secret_name": {"title": "Secret Name", "type": "string"},
|
{
|
||||||
"age": {"title": "Age", "type": "integer"},
|
"title": "Name",
|
||||||
|
"anyOf": [{"type": "string"}, {"type": "null"}],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
| IsDict(
|
||||||
|
# TODO: remove when deprecating Pydantic v1
|
||||||
|
{"title": "Name", "type": "string"}
|
||||||
|
),
|
||||||
|
"secret_name": IsDict(
|
||||||
|
{
|
||||||
|
"title": "Secret Name",
|
||||||
|
"anyOf": [{"type": "string"}, {"type": "null"}],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
| IsDict(
|
||||||
|
# TODO: remove when deprecating Pydantic v1
|
||||||
|
{"title": "Secret Name", "type": "string"}
|
||||||
|
),
|
||||||
|
"age": IsDict(
|
||||||
|
{
|
||||||
|
"title": "Age",
|
||||||
|
"anyOf": [{"type": "integer"}, {"type": "null"}],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
| IsDict(
|
||||||
|
# TODO: remove when deprecating Pydantic v1
|
||||||
|
{"title": "Age", "type": "integer"}
|
||||||
|
),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"ValidationError": {
|
"ValidationError": {
|
||||||
|
@ -1,12 +1,14 @@
|
|||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from pydantic import validator
|
|
||||||
from pydantic.error_wrappers import ValidationError
|
from pydantic.error_wrappers import ValidationError
|
||||||
from sqlmodel import SQLModel
|
from sqlmodel import SQLModel
|
||||||
|
|
||||||
|
from .conftest import needs_pydanticv1, needs_pydanticv2
|
||||||
|
|
||||||
def test_validation(clear_sqlmodel):
|
|
||||||
|
@needs_pydanticv1
|
||||||
|
def test_validation_pydantic_v1(clear_sqlmodel):
|
||||||
"""Test validation of implicit and explicit None values.
|
"""Test validation of implicit and explicit None values.
|
||||||
|
|
||||||
# For consistency with pydantic, validators are not to be called on
|
# For consistency with pydantic, validators are not to be called on
|
||||||
@ -16,6 +18,7 @@ def test_validation(clear_sqlmodel):
|
|||||||
https://github.com/samuelcolvin/pydantic/issues/1223
|
https://github.com/samuelcolvin/pydantic/issues/1223
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
from pydantic import validator
|
||||||
|
|
||||||
class Hero(SQLModel):
|
class Hero(SQLModel):
|
||||||
name: Optional[str] = None
|
name: Optional[str] = None
|
||||||
@ -31,3 +34,32 @@ def test_validation(clear_sqlmodel):
|
|||||||
|
|
||||||
with pytest.raises(ValidationError):
|
with pytest.raises(ValidationError):
|
||||||
Hero.validate({"name": None, "age": 25})
|
Hero.validate({"name": None, "age": 25})
|
||||||
|
|
||||||
|
|
||||||
|
@needs_pydanticv2
|
||||||
|
def test_validation_pydantic_v2(clear_sqlmodel):
|
||||||
|
"""Test validation of implicit and explicit None values.
|
||||||
|
|
||||||
|
# For consistency with pydantic, validators are not to be called on
|
||||||
|
# arguments that are not explicitly provided.
|
||||||
|
|
||||||
|
https://github.com/tiangolo/sqlmodel/issues/230
|
||||||
|
https://github.com/samuelcolvin/pydantic/issues/1223
|
||||||
|
|
||||||
|
"""
|
||||||
|
from pydantic import field_validator
|
||||||
|
|
||||||
|
class Hero(SQLModel):
|
||||||
|
name: Optional[str] = None
|
||||||
|
secret_name: Optional[str] = None
|
||||||
|
age: Optional[int] = None
|
||||||
|
|
||||||
|
@field_validator("name", "secret_name", "age")
|
||||||
|
def reject_none(cls, v):
|
||||||
|
assert v is not None
|
||||||
|
return v
|
||||||
|
|
||||||
|
Hero.model_validate({"age": 25})
|
||||||
|
|
||||||
|
with pytest.raises(ValidationError):
|
||||||
|
Hero.model_validate({"name": None, "age": 25})
|
||||||
|
Loading…
x
Reference in New Issue
Block a user