Работа с данными в программировании является неотъемлемой частью процесса. Однако данные, которые мы получаем от пользователей или внешних систем, зачастую недостаточно «чистые». Например, они могут содержать ошибки или некорректные значения. Здесь на помощь приходят инструменты валидации — проверка данных на корректность по заданным условиям.
Валидация данных
Самый простой подход к валидации данных в Python — использовать базовые операторы языка и стандартные функции:
errors = []
if data['email'] == '':
errors.append('Email is empty')
# Гипотетическая функция проверки формата
if not has_email_format(data['email']):
errors.append('Email has incorrect format')
Правда такой код станет неприглядным и сложным при работе с большими формами и множеством правил валидации.
У этого подхода есть и другие недостатки. Например, он не поддерживает простое переиспользование правил для различных форм и проектов. Также многие проверки придется реализовывать вручную или использовать сторонние библиотеки. Это касается таких вещей, как проверка корректности URL-адресов, физических адресов и прочего.
Для более сложных случаев валидации и для большей структурированности кода используется паттерн проектирования Builder. Этот паттерн помогает разбить сложный процесс на более простые и управляемые части.
Builder
Builder (Строитель) — паттерн проектирования, который предлагает решение для проблемы создания сложных объектов. Он предлагает разделить процесс построения сложного объекта на различные части, каждая из которых отвечает за определенный аспект построения объекта. Так построение объекта становится более структурированным, гибким и понятным.
В объектно-ориентированных языках этот паттерн обычно реализуется с помощью fluent interface. В этом случае каждый метод возвращает ссылку на сам объект, что позволяет вызывать методы последовательно в одной цепочке.
Рассмотрим пример использования этого паттерна:
class DataValidator:
def __init__(self, data):
self.data = data
self.errors = []
def validate_email(self):
if '@' not in self.data.get('email', ''):
self.errors.append('Invalid email')
return self
def validate_password(self):
if len(self.data.get('password', '')) < 8:
self.errors.append('Password is too short')
return self
def get_errors(self):
return self.errors
data = {"email": "test", "password": "short"}
validator = DataValidator(data)
errors = validator.validate_email().validate_password().get_errors()
if errors:
print(errors)
Здесь мы создаем экземпляр класса DataValidator
, передавая ему данные для валидации. Далее с помощью методов validate_email
и validate_password
проверяем данные на валидность. Если данные не проходят валидацию, соответствующие ошибки добавляются в список errors
.
Такой подход позволяет последовательно применять различные проверки к данным и получать список всех ошибок в конце. Это делает код более чистым и упрощает процесс валидации данных.
В Python также существуют и готовые инструменты. Они могут значительно упростить процесс валидации и сделать код более чистым и структурированным. Один из таких инструментов — библиотека Pydantic.
Pydantic
Библиотека Pydantic предоставляет набор инструментов для валидации данных с использованием python-типов:
class User(BaseModel):
name: str
email: EmailStr
try:
User(name='Tom', email='not a valid email')
except ValidationError as e:
print(e)
Здесь мы определяем модель User, которая содержит два поля: name
и email
. Затем мы передаем некорректный email, чем пытаемся создать экземпляр User.
Так как EmailStr
проверяет, что значение действительно является корректным адресом электронной почты, в этом случае будет выброшено исключение ValidationError
.
Pydantic позволяет с помощью объявления типов поля в модели определять правила валидации. Например, мы можем определить, что возраст пользователя должен быть положительным числом:
class User(BaseModel):
name: str
email: EmailStr
age: PositiveInt
try:
User(name='Tom', email='tom@example.com', age=-1)
except ValidationError as e:
print(e)
В этом примере PositiveInt
гарантирует, что значение age
является положительным целым числом. Так попытка создать пользователя с отрицательным возрастом вызывает ошибку валидации.
Однако валидация данных — это не единственная область, где может быть полезен паттерн Builder. Этот паттерн также широко используется для создания сложных запросов к базам данных.
Query Builder
Query Builder — паттерн проектирования, который позволяет собирать сложные запросы по частям. Чаще всего он встречается при работе с базами данных для сбора SQL либо для коллекций.
В Python одним из наиболее известных примеров использования этого паттерна является Django ORM. Это фреймворк, который позволяет строить запросы к базе данных с помощью fluent interface.
Вместо написания «сырых» SQL-запросов, мы используем методы Django ORM для построения запросов по частям. Это упрощает чтение и поддержку кода.
Рассмотрим пример:
# Запрос выборки для модели User с условиями и сортировкой
users = (
User.objects.filter(name='Tom')
.exclude(is_active=False)
.order_by('-last_login')
)
for user in users:
print(user.username)
В этом примере мы создаем запрос выборки (filter
) для модели User
, исключаем не активных пользователей (exclude
) и сортируем результат по дате последнего входа в систему (order_by
). Всё это происходит без написания SQL-запросов.
Также паттерн Builder в Django ORM позволяет легко изменить запрос с помощью добавления дополнительных условий, изменения порядка сортировки и прочего. Это делает код более гибким и поддерживаемым.
Выводы
Паттерн Builder позволяет упростить и структурировать процесс создания сложных объектов. Валидация данных и построение SQL-запросов являются отличными примерами использования этого паттерна. Они часто включают в себя сложные и многопараметричные проверки и действия.
Паттерн Builder предлагает эффективный способ упрощения и структурирования процессов создания сложных объектов в программировании. Он делает код более гибким и понятным. Это позволяет легко изменять и расширять функциональность кода.
Остались вопросы? Задайте их в разделе «Обсуждение»
Вам ответят команда поддержки Хекслета или другие студенты
Для полного доступа к курсу нужен базовый план
Базовый план откроет полный доступ ко всем курсам, упражнениям и урокам Хекслета, проектам и пожизненный доступ к теории пройденных уроков. Подписку можно отменить в любой момент.