Generate Literal in Runtime - python

I want to create a pydantic Model with a Literal field, but the options should be derived from a list. Something like this:
from pydantic import BaseModel
from typing import Literal
opts=['foo','bar']
class MyModel(BaseModel):
select_one : Literal[opts]
Is there some way this could be solved by enumeration?

Yes, there is. If you mix in str with enum.Enum, you'll get an enumeration with members that are fully backwards-compatible with strings (can be used anywhere a str type is expected) and are type-checker friendly.
from enum import Enum
from pydantic import BaseModel
class Options(str, Enum):
FOO = 'foo'
BAR = 'bar'
class MyModel(BaseModel):
select_one : Options

In order for this to work, the Literal needs to be over a tuple (see also here).
There are several options, depending on your situation:
from pydantic import BaseModel
from typing import Literal
# First option:
class MyModel(BaseModel):
select_one: Literal['foo', 'bar']
# Second option:
opts_tuple = ('foo', 'bar')
class MyModel(BaseModel):
select_one: Literal[opts_tuple]
# Third option:
opts_list = ['foo', 'bar']
class MyModel(BaseModel):
select_one: Literal[tuple(opts_list)]

Related

FastAPI - Pydantic Query Parameter with Enum [duplicate]

I have this following class:
class Quiz(BaseModel):
question: str
subject: str
choice: str = Query(choices=('eu', 'us', 'cn', 'ru'))
I can render the form bases on this class like this
#api.post("/postdata")
def post_data(form_data: Quiz = Depends()):
return form_data
How can I display a drop down list for choice field ?
Option 1
Use literal values. Literal type is a new feature of the Python standard library as of Python 3.8 (prior to Python 3.8, it requires the typing-extensions package) and is supported by Pydantic. Example:
from fastapi import FastAPI, Depends
from pydantic import BaseModel
from typing import Literal
app = FastAPI()
class Quiz(BaseModel):
question: str
subject: str
choice: Literal['eu', 'us', 'cn', 'ru'] = 'us'
#app.post('/submit')
def post_data(data: Quiz = Depends()):
return data
Option 2
Use Enums (also, see Python's enum module, as well as FastAPI's documentation on Predefined values). By having your Enum sub-class inheriting from str, the API docs will be able to know that the values must be of type string and will be able to render correctly. Example:
from fastapi import FastAPI, Depends
from pydantic import BaseModel
from enum import Enum
app = FastAPI()
class Country(str, Enum):
eu = 'eu'
us = 'us'
cn = 'cn'
ru = 'ru'
class Quiz(BaseModel):
question: str
subject: str
choice: Country = Country.us
#app.post('/submit')
def post_data(data: Quiz = Depends()):
return data

how to validate keys with whitespaces in pydantic

I have a json key with whitespace in it:
My_dict = {"my key": 1}
I want to create a Model to model it:
from pydantic import BaseModel
class MyModel(BaseModel):
mykey: int
# my key isn't a legit variable name
# my_key is, but like mykey - it doesn't catch the correct key from the json
MyModel(**my_dict)
This doesn't work.
I tried playing with the BaseModel.Config, but didn't get anywhere. Didn't see anything on the docs as well.
Is this possible?
I can use a workaround: Go over the json, replace all key's whitespaces into underscores, and then use pydantic but I would love to not use this...
Yes, it's possible by using Field's aliases:
from pydantic import BaseModel, Field
class MyModel(BaseModel):
mykey: int = Field(alias='my key')
class Config:
allow_population_by_field_name = True
print(MyModel(**{"my key": 1}))
print(MyModel(**{"mykey": 1}))

Type variable ... is unbound

I have a base class BaseTemplateData which inherits from pydantic.BaseModel. Then, I want to have a property of the BaseTemplate class which stores a type of a child class of BaseTemplateData.
I'm doing the following, but I'm getting a mypy error saying Type variable "file.path.TemplateDataType" is unbound, when I'm explicitly passing a bound parameter to the TypeVar call.
I also would like to have another class BaseTemplate2 which property doesn't store the type itself, but an instance of a child class of BaseTemplateData. Would the approach be correct?
Any help would be much appreciated. Thanks!
from typing import Type, TypeVar
from pydantic import BaseModel
class BaseTemplateData(BaseModel):
"""
Base class for all templates.
"""
TemplateDataType = TypeVar("TemplateDataType", bound=BaseTemplateData)
class BaseTemplate(BaseModel):
"""
Template class for email templates
"""
data_model: Type[TemplateDataType]
class BaseTemplate2(BaseModel):
"""
Template class for email templates 2
"""
data_model: TemplateDataType
I'm not so familiar with pythons Typing and can't imagine what you trying to solve by this code. But I don't understand why you need TemplateDataType?
Your expectations are:
I want to have a property of the BaseTemplate class which stores a type of a child class of BaseTemplateData
I also would like to have another class BaseTemplate2 which property doesn't store the type itself, but an instance of a child class of BaseTemplateData
Okay:
from typing import Type
from pydantic import BaseModel
class BaseTemplateData(BaseModel):
"""
Base class for all templates.
"""
class ChildBaseTemplateData(BaseTemplateData):
"""
Child class for Base class.
"""
class BaseTemplate(BaseModel):
"""
Template class for email templates
"""
data_model: Type[BaseTemplateData]
class BaseTemplate2(BaseModel):
"""
Template class for email templates 2
"""
data_model: BaseTemplateData
a = BaseTemplate()
# it is okay. Because a.data_model is a "type of a child class of BaseTemplateData"
a.data_model = ChildBaseTemplateData
b = BaseTemplate2()
# it is okay too. Because b.data_model is "an instance of a child class of BaseTemplateData"
b.data_model = ChildBaseTemplateData()
c = BaseTemplate2()
# error: Incompatible types in assignment (expression has type "str", variable has type "BaseTemplateData")
c.data_model = "string"
Here is a good explanation about TypeVar
Also TypeVar is using with Generics.
Here is no Generics in your example
Proper usage of TypeVar will be something like:
from typing import Type, TypeVar, Generic
A = TypeVar("A", int, str)
class B(Generic[A]):
pass
# Ok
a = B[int]
# Error: error: Value of type variable "A" of "B" cannot be "float"
b = B[float]

Assigning Pydantic Fields not by alias

How can I create a pydantic object, without useing alias names?
from pydantic import BaseModel, Field
class Params(BaseModel):
var_name: int = Field(alias='var_alias')
Params(var_alias=5) # works
Params(var_name=5) # does not work
You need to use allow_population_by_field_name model config option, which is False by default.
from pydantic import BaseModel, Field
class Params(BaseModel):
var_name: int = Field(alias='var_alias')
class Config:
allow_population_by_field_name = True
Params(var_alias=5) # works
Params(var_name=5) # works

Why is TypeError raised when attribute name matches class name using typing?

I'm unable to initialise a dataclass list attribute where the attribute has the same name as the list elements' class.
The initialisation works fine when the attribute name is changed. I have the same problem with Pydantic classes.
from dataclasses import dataclass, field
from typing import List
#dataclass
class Thing:
name: str
#dataclass
class MyClass:
Thing: List[Thing] = field(default_factory=list)
c = MyClass()
This gives the following error:
TypeError: Parameters to generic types must be types. Got Field(name=None,type=None,default=<dataclasses._MISSING_TYPE object at 0x00000201CA208518>,default_f.
When I change:
Thing: List[Thing] = field(default_factory=list)
to:
thing: List[Thing] = field(default_factory=list)
the TypeError is not raised.
Because then it's overriding Thing.
That's why thing works.

Categories

Resources