constr and Fields don't serve the same purpose.

constr is a specific type that give validation rules regarding this specific type. You have equivalent for all classic python types.

arguments of constr:

    strip_whitespace: bool = False: removes leading and trailing whitespace
    to_lower: bool = False: turns all characters to lowercase
    to_upper: bool = False: turns all characters to uppercase
    strict: bool = False: controls type coercion
    min_length: int = None: minimum length of the string
    max_length: int = None: maximum length of the string
    curtail_length: int = None: shrinks the string length to the set value when it is longer than the set value
    regex: str = None: regex to validate the string against

As you can see thoses arguments allow you to manipulate the str itself not the behavior of pydantic with this field.

Field doesn't serve the same purpose, it's a way of customizing fields, all fields not only str, it add 18 customization variables that you can find here.

Is it just a matter of code style? Is one of them preferred over the other?

for the specific case of str it is a matter of code style and what is preferred doesn't matter, only your use-case does.

In general it is better to don't mix different syntax together and since you often need Field(), you will find it often.

A classic use case would be api response that send json object in camelCase or PascalCase, you would use field alias to match theses objects and work with their variables in snake_case.

exemple:

class Voice(BaseModel):
    name: str = Field(None, alias='ActorName')
    language_code: str = None
    mood: str = None

I personally prefer to use pydantic types to clearly separate type rules and field annotations.

Basic example:

class Car(BaseModel):
    description: Union[constr(min_length=1, max_length=64), None] = Field(
        default=None,
        example="something",
        description="Your car description",
    )

In any case you should only use one style of model structure (field, pydantic type or both toguether) for global coherence and better readability of your project.

for your 2nd question you are right, using constr is surely the best approach since the validation rule will be added into the openapi doc.

If you want to learn more about limitation and field rules enforcement check this.

Answer from Bastien B on Stack Overflow
🌐
Pydantic
docs.pydantic.dev › latest › concepts › types
Types - Pydantic Validation
Pydantic uses types to define how validation and serialization should be performed. Built-in and standard library types (such as int, str, date) can be used as is. Strictness can be controlled and constraints can be applied on them.
Top answer
1 of 3
15

constr and Fields don't serve the same purpose.

constr is a specific type that give validation rules regarding this specific type. You have equivalent for all classic python types.

arguments of constr:

    strip_whitespace: bool = False: removes leading and trailing whitespace
    to_lower: bool = False: turns all characters to lowercase
    to_upper: bool = False: turns all characters to uppercase
    strict: bool = False: controls type coercion
    min_length: int = None: minimum length of the string
    max_length: int = None: maximum length of the string
    curtail_length: int = None: shrinks the string length to the set value when it is longer than the set value
    regex: str = None: regex to validate the string against

As you can see thoses arguments allow you to manipulate the str itself not the behavior of pydantic with this field.

Field doesn't serve the same purpose, it's a way of customizing fields, all fields not only str, it add 18 customization variables that you can find here.

Is it just a matter of code style? Is one of them preferred over the other?

for the specific case of str it is a matter of code style and what is preferred doesn't matter, only your use-case does.

In general it is better to don't mix different syntax together and since you often need Field(), you will find it often.

A classic use case would be api response that send json object in camelCase or PascalCase, you would use field alias to match theses objects and work with their variables in snake_case.

exemple:

class Voice(BaseModel):
    name: str = Field(None, alias='ActorName')
    language_code: str = None
    mood: str = None

I personally prefer to use pydantic types to clearly separate type rules and field annotations.

Basic example:

class Car(BaseModel):
    description: Union[constr(min_length=1, max_length=64), None] = Field(
        default=None,
        example="something",
        description="Your car description",
    )

In any case you should only use one style of model structure (field, pydantic type or both toguether) for global coherence and better readability of your project.

for your 2nd question you are right, using constr is surely the best approach since the validation rule will be added into the openapi doc.

If you want to learn more about limitation and field rules enforcement check this.

2 of 3
3

This link shows the methods that do and don't work for pydantic and mypy together: https://lyz-code.github.io/blue-book/coding/python/pydantic_types/#using-constrained-strings-in-list-attributes

The best option for my use case was to make a class that inherited from pydantic.ConstrainedStr as so:

import pydantic
from typing import List

...

class Regex(pydantic.ConstrainedStr):
    regex = re.compile("^[0-9a-z_]*$")

class Data(pydantic.BaseModel):
    regex: List[Regex]
    # regex: list[Regex] if you are on 3.9+
Discussions

Using constr (or conbytes, etc) raises mypy errors
I would like to be able to reuse constr types for multiple attributes, for multiple models. The code works as expected, and creates a JSON schema as expected, but mypy raises errors. I am using: Py... More on github.com
🌐 github.com
1
1
March 23, 2021
Different `pydantic.constr()` validation behavior in different environments
Bug The main bug here is a difference of validation behavior on different environments. observations Linux behavior (docker container) OS - docker run -it --rm python:3.6.8 /bin/sh: # uname -a Linu... More on github.com
🌐 github.com
6
December 3, 2019
Enable idiomatic use of constrained types without triggering linting errors
I love how tersely I'm able to define how I want my data to be shaped - and the use of constrained types plays a major role in this. Unfortunately, the most idiomatic way to use constrained types runs afoul of linters - Pylance in my case. ... import pydantic class MyClass(pydantic.BaseModel): ... More on github.com
🌐 github.com
9
February 4, 2023
Using conlist and constr show type Any in type intellisense
from pydantic import BaseModel, conlist, constr class MyValidator(BaseModel): testProp: conlist(str, min_length=1) myData = MyValidator(**{"testProp": ['asdf1', 'asdf2']}) print(type(myData.testPro... More on github.com
🌐 github.com
1
2
August 8, 2023
🌐
Pydantic
docs.pydantic.dev › latest › api › types
Pydantic Types - Pydantic Validation
from typing import Annotated from pydantic.types import StringConstraints ConstrainedStr = Annotated[str, StringConstraints(min_length=1, max_length=10)]
🌐
Pydantic
docs.pydantic.dev › 1.10 › usage › types
Field Types - Pydantic
from decimal import Decimal from pydantic import ( BaseModel, NegativeFloat, NegativeInt, PositiveFloat, PositiveInt, NonNegativeFloat, NonNegativeInt, NonPositiveFloat, NonPositiveInt, conbytes, condecimal, confloat, conint, conlist, conset, constr, Field, ) class Model(BaseModel): upper_bytes: conbytes(to_upper=True) lower_bytes: conbytes(to_lower=True) short_bytes: conbytes(min_length=2, max_length=10) strip_bytes: conbytes(strip_whitespace=True) upper_str: constr(to_upper=True) lower_str: constr(to_lower=True) short_str: constr(min_length=2, max_length=10) regex_str: constr(regex=r'^apple
🌐
Pydantic
docs.pydantic.dev › 2.0 › usage › types › string_types
String Types - Pydantic
from pydantic import BaseModel, ImportString class ImportThings(BaseModel): obj: ImportString # Create an instance m = ImportThings(obj='math:cos') print(m) #> obj=<built-in function cos> print(m.model_dump_json()) #> {"obj":"math.cos"} The value of numerous common types can be restricted using con* type functions. The following arguments are available when using the constr type function
🌐
Strawberry GraphQL
strawberry.rocks › docs › integrations › pydantic
Pydantic support | 🍓 Strawberry GraphQL
Strawberry will call the constructor of the new type annotation with the field value as input, so this only works when conversion is possible through a constructor. ... The other, more comprehensive, method for modifying the conversion logic is to provide custom implementations of from_pydantic and to_pydantic .
🌐
GitHub
github.com › samuelcolvin › pydantic › issues › 1070
Different `pydantic.constr()` validation behavior in different environments · Issue #1070 · pydantic/pydantic
December 3, 2019 - import pydantic mytype = pydantic.constr() value = "value" mytype_value = mytype(value) class Model(pydantic.BaseModel): attr: mytype # this works Model(attr=value) # this causes a validation error, shown below Model(attr=mytype_value)
Author   mdscruggs
Find elsewhere
🌐
Pydantic
docs.pydantic.dev › latest › concepts › fields
Fields - Pydantic Validation
As far as static type checkers are concerned, name is still typed as str, but Pydantic leverages the available metadata to add validation logic, type constraints, etc.
🌐
Pydantic
docs.pydantic.dev › 2.2 › usage › types › strict_types
Strict Types - Pydantic
See the Migration Guide for tips on essential changes from Pydantic V1! Strict types enable you to prevent coercion from compatible types. ... These types will only pass validation when the validated value is of the respective type or is a subtype of that type.
🌐
Pydantic
docs.pydantic.dev › 1.10 › usage › schema
Schema - Pydantic
For more details see https://docs.pydantic.dev/usage/schema/#unenforced- field-constraints """ # but you can set the schema attribute directly: # (Note: here exclusiveMaximum will not be enforce) class Model(BaseModel): foo: PositiveInt = Field(..., exclusiveMaximum=10) print(Model.schema()) """ { 'title': 'Model', 'type': 'object', 'properties': { 'foo': { 'title': 'Foo', 'exclusiveMaximum': 10, 'exclusiveMinimum': 0, 'type': 'integer', }, }, 'required': ['foo'], } """ # if you find yourself needing this, an alternative is to declare # the constraints in Field (or you could use conint()) # he
🌐
Pydantic
docs.pydantic.dev › 1.10 › usage › models
Models - Pydantic
In other words, pydantic guarantees the types and constraints of the output model, not the input data.
🌐
DEV Community
dev.to › devasservice › best-practices-for-using-pydantic-in-python-2021
Best Practices for Using Pydantic in Python - DEV Community
July 17, 2024 - These allow you to add additional validation rules, such as length constraints on strings or value ranges for integers. ... from pydantic import BaseModel, conint, constr class Product(BaseModel): name: constr(min_length=2, max_length=50) quantity: ...
🌐
GitHub
github.com › pydantic › pydantic › issues › 5006
Enable idiomatic use of constrained types without triggering linting errors · Issue #5006 · pydantic/pydantic
February 4, 2023 - I love how tersely I'm able to define how I want my data to be shaped - and the use of constrained types plays a major role in this. Unfortunately, the most idiomatic way to use constrained types runs afoul of linters - Pylance in my case. ... import pydantic class MyClass(pydantic.BaseModel): my_constrained_float: pydantic.confloat(gt = 0, lt = 1)
Author   vineetrajasekhar
🌐
LinkedIn
linkedin.com › pulse › best-practices-using-pydantic-python-nuno-bispo-hooke
Best Practices for Using Pydantic in Python
July 16, 2024 - These allow you to add additional validation rules, such as length constraints on strings or value ranges for integers. ... from pydantic import BaseModel, conint, constr class Product(BaseModel): name: constr(min_length=2, max_length=50) quantity: ...
🌐
Blue Book
lyz-code.github.io › blue-book › coding › python › pydantic_types
Pydantic types - The Blue Book
constr : type method for constraining strs; see Constrained Types. You can also define your own custom data types. There are several ways to achieve it. You use a custom class with a classmethod __get_validators__. It will be called to get validators to parse and validate the input data.
🌐
GitHub
github.com › pydantic › pydantic › discussions › 7278
Pattern / Regular expression in Pydantic 2.X · pydantic/pydantic · Discussion #7278
I used to add regex rules to constrain my input in 1.X like this: class Numbers(BaseModel): """ Helper pydantic model for the numbers """ # Pydantic 1.X duration: constr(regex=r'\d{2,3}m') time: constr(regex=r'\d{8}\.\d{4}') But after I upgrade to 2.X, I noticed regex is deprecated, and suggests to use 'pattern' instead.
Author   pydantic
🌐
Blogger
self-learning-java-tutorial.blogspot.com › 2021 › 10 › pydantic-constr-constrain-string-values.html
Programming for beginners: Pydantic: constr: Constrain string values
October 25, 2021 - ‘constr’ type method is used to constraining str values. Below table summarizes the arguments of ‘constr’ method. Argument ...