Validating user email and password is one of the most common tasks in software development nowadays since most applications require users to create accounts and log in. This practical, example-based article will walk you through a couple of different ways to validate user email and password by using Pydantic (We’ll use the latest version of Pydantic in the upcoming examples. If you are totally new to the library, please see this article first).
Table of Contents
Using EmailStr and constr types
This approach uses the built-in types EmailStr
and constr
from Pydantic to validate the user email and password. EmailStr
is a type that checks if the input is a valid email address. constr
is a type that allows specifying constraints on the length and format of a string.
email-validator
is an optional dependency that is needed for the EmailStr
type so you have to install it before importing EmailStr
into your code:
pip install email-validator
From here, the steps are:
- Import
EmailStr
andconstr
from Pydantic. - Define a
User
model that hasemail
andpassword
fields of typeEmailStr
andconstr
respectively. - Specify the constraints on the password field using the
min_length
andmax_length
parameters ofconstr
. In addition, you can use the pattern parameter to declare a regex pattern that the password must match. - Create an instance of the
User
model with some input data and check if it is valid or raises aValidationError
.
Example:
from pydantic import BaseModel, EmailStr, constr, ValidationError
# Defome User model
class User(BaseModel):
email: EmailStr
password: constr(
min_length=8,
max_length=20
)
Test it with valid data:
try:
user = User(email="[email protected]", password="Passw0rd!")
print(user)
# Success
# email='[email protected]' password='Passw0rd!'
except ValidationError as e:
print(e)
And give it a try with invalid data to see what happens:
try:
user = User(email="test", password="123")
print(user)
except ValidationError as e:
print(e)
# 2 validation errors for User
# email
# value is not a valid email address: The email address is not valid. It must have exactly one @-sign. [type=value_error, input_value='test', input_type=str]
# password
# String should have at least 8 characters [type=string_too_short, input_value='123', input_type=str]
This approach is simple and neat but it may not be flexible enough to customize the validation logic or error messages for non-common scenarios or requirements.
Using custom validators
The main idea here is to use custom validators from Pydantic to validate user email and password. Custom validators are functions that take the input value and return a modified value or raise a ValueError
if the validation fails. They can be decorated with @field_validator
or @model_validator
to apply them to specific fields or the whole model, respectively.
The steps are:
- Import
BaseModel
andfield_validator
from Pydantic. - Define a
User
model that hasemail
andpassword
fields of typestr
. - Write a custom validator function for the
email
field that checks if the input is a valid email address using regular expressions (you can use a third-party library if you want). - Write a custom validator function for the
password
field that checks if the input meets some criteria on the length and format of the password. - Decorate the validator functions with
@field_validator
and specify the field names they apply to.
The example below will give you more clarity. It requires a password to have at least 8 characters, at least one uppercase letter, at least one lowercase letter, and at least one digit.
from pydantic import BaseModel, field_validator
import re
# Define a Pydantic model for a user
class User(BaseModel):
email: str
password: str
# Define a validator for the email field
@field_validator("email")
def check_email(cls, value):
# use a regex to check that the email has a valid format
email_regex = r"^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$"
if not re.match(email_regex, value):
raise ValueError("Invalid email address")
return value
# Define a validator for the password field
@field_validator("password")
def check_password(cls, value):
# convert the password to a string if it is not already
value = str(value)
# check that the password has at least 8 characters, one uppercase letter, one lowercase letter, and one digit
if len(value) < 8:
raise ValueError("Password must have at least 8 characters")
if not any(c.isupper() for c in value):
raise ValueError("Password must have at least one uppercase letter")
if not any(c.islower() for c in value):
raise ValueError("Password must have at least one lowercase letter")
if not any(c.isdigit() for c in value):
raise ValueError("Password must have at least one digit")
return value
Let’s test our model:
# Create a user with a valid email and password
try:
u1 = User(email="[email protected]", password="Password1")
print(u1)
# Output: email='[email protected]', password='Password1'
except ValueError as e:
print(e)
# Create a user with an invalid email
try:
u2 = User(email="test", password="Password1")
print(u2)
except ValueError as e:
print(e)
# 1 validation error for User
# email
# Value error, Invalid email address [type=value_error, input_value='test', input_type=str]
# Create a user with an invalid password
try:
u3 = User(email="[email protected]", password="password")
print(u3)
except ValueError as e:
print(e)
# 1 validation error for User
# password
# Value error, Password must have at least one uppercase letter [type=value_error, input_value='password', input_type=str]
This approach is more verbose than the first one, but it can handle complex or cross-field validation scenarios.
Conclusion
If you want to become a professional web developer with Python, tasks related to validating user email and password cannot be avoided. Therefore, mastering how to complete those tasks with Pydantic is extremely important and will save you a lot of time in the future. Take a close look at the examples, try changing a few lines of code, and see what happens next. Happy coding & have a nice day with Pydantic and Python programming!