Python · API · Продвинутый уровень

Класс API-клиента

5 задач

Оберните API в многоразовый класс с requests.Session для чистого и удобного кода.

Зачем оборачивать API в класс

#
До этого момента мы вызывали API-эндпоинты напрямую: каждый скрипт вручную получал токен и передавал заголовок Authorization в каждый запрос. При написании больших программ это становится повторяющимся и хрупким — если API меняется, приходится обновлять код во многих местах. Оборачивание API в класс решает эту проблему: ```python client = APIClient() client.login('alice42', 'securepass123') products = client.get_products(category='chairs') client.add_to_cart('oak-chair', quantity=2) ``` По сравнению с разрозненными прямыми вызовами такой код: - выражает *что мы делаем* (login, get_products), а не *как это реализовано* (POST /api/auth/token/, разбор ответа) - централизует URL и логику авторизации - упрощает тестирование — можно подменить клиент фейком Внутри класс хранит сессию requests.Session() и устанавливает заголовок авторизации один раз при входе: ```python import requests class APIClient: BASE_URL = 'https://apilearn.tukas.dev' def __init__(self): self.session = requests.Session() def login(self, username, password): token = self.session.post(f'{self.BASE_URL}/api/auth/token/', json={ 'username': username, 'password': password, }).json()['token'] self.session.headers['Authorization'] = f'Token {token}' ``` Теперь каждый следующий метод — get_products(), add_to_cart() и т.д. — просто вызывает self.session.get/post и автоматически получает авторизацию.

Построение APIClient шаг за шагом

#
Полная реализация APIClient с наиболее полезными методами: ```python import requests class APIClient: BASE_URL = 'https://apilearn.tukas.dev' def __init__(self): self.session = requests.Session() def login(self, username, password): token = self.session.post(f'{self.BASE_URL}/api/auth/token/', json={ 'username': username, 'password': password, }).json()['token'] self.session.headers['Authorization'] = f'Token {token}' def get_products(self, **params): return self.session.get( f'{self.BASE_URL}/api/products/', params=params ).json()['results'] def get_profile(self): return self.session.get(f'{self.BASE_URL}/api/users/profile/').json() def add_to_cart(self, product_slug, quantity=1): return self.session.post(f'{self.BASE_URL}/api/cart/items/', json={ 'product_slug': product_slug, 'quantity': quantity, }).json() def clear_cart(self): self.session.delete(f'{self.BASE_URL}/api/cart/items/') ``` Использование: ```python client = APIClient() client.login('alice42', 'securepass123') # Получить все кресла chairs = client.get_products(category='chairs') print(f'Найдено кресел: {len(chairs)}') # Добавить первое в корзину if chairs: item = client.add_to_cart(chairs[0]['slug'], quantity=2) print('Добавлено, id элемента корзины:', item['id']) # Проверить профиль profile = client.get_profile() print('Авторизован как:', profile['username']) # Очистить корзину client.clear_cart() ``` Обратите внимание: self.session хранит заголовок Authorization после login(), поэтому ни один метод не нуждается в повторном получении токена.

Справочник Session и класса клиента

#
Чеклист для написания API-клиент класса: ``` self.session = requests.Session() Создать один раз в __init__ session.headers['Authorization'] Установить один раз в login() self.BASE_URL Хранить в атрибуте класса Методы делегируют к self.session get, post, patch, delete ``` Session vs заголовки в каждом запросе: ```python # БЕЗ Session — повторение в каждом методе def get_profile(self, token): return requests.get(url, headers={'Authorization': f'Token {token}'}).json() # С Session — заголовок установлен один раз def get_profile(self): return self.session.get(url).json() ``` Типичная структура метода: ```python def place_order(self, phone_number): response = self.session.post( f'{self.BASE_URL}/api/orders/', json={'phone_number': phone_number}, ) response.raise_for_status() return response.json() ``` Паттерн с raise_for_status() необязателен, но рекомендован — он сразу бросает исключение при 4xx/5xx, вместо тихого возврата ответа с ошибкой.
01

Базовый класс APIClient

#

Создайте класс APIClient с методами __init__() и login(). __init__ должен создавать requests.Session(). login() должен получать токен и устанавливать заголовок авторизации. Протестируйте, вызвав login() и сделав прямой GET к /api/users/profile/ через session.

import requests

class APIClient:
    BASE_URL = 'https://apilearn.tukas.dev'

    def __init__(self):
        # Создать session
        pass

    def login(self, username, password):
        # Получить токен и установить Authorization header
        pass

client = APIClient()
client.login('ВАШ_ЛОГИН', 'ВАШ_ПАРОЛЬ')
# Проверьте: session должна возвращать профиль без дополнительных заголовков
Решение
import requests

class APIClient:
    BASE_URL = 'https://apilearn.tukas.dev'

    def __init__(self):
        self.session = requests.Session()

    def login(self, username, password):
        token = self.session.post(f'{self.BASE_URL}/api/auth/token/', json={
            'username': username, 'password': password,
        }).json()['token']
        self.session.headers['Authorization'] = f'Token {token}'

client = APIClient()
client.login('ВАШ_ЛОГИН', 'ВАШ_ПАРОЛЬ')
profile = client.session.get(f'{client.BASE_URL}/api/users/profile/').json()
print(profile['username'])
02

Добавить метод get_products()

#

Расширьте APIClient методом get_products(**params), который возвращает список результатов из /api/products/. Протестируйте, вызвав get_products() и get_products(category='chairs').

import requests

class APIClient:
    BASE_URL = 'https://apilearn.tukas.dev'

    def __init__(self):
        self.session = requests.Session()

    def login(self, username, password):
        token = self.session.post(f'{self.BASE_URL}/api/auth/token/', json={
            'username': username, 'password': password,
        }).json()['token']
        self.session.headers['Authorization'] = f'Token {token}'

    def get_products(self, **params):
        # GET /api/products/ с переданными params
        pass

client = APIClient()
client.login('ВАШ_ЛОГИН', 'ВАШ_ПАРОЛЬ')
print(len(client.get_products()))
print(len(client.get_products(category='chairs')))
Решение
import requests

class APIClient:
    BASE_URL = 'https://apilearn.tukas.dev'

    def __init__(self):
        self.session = requests.Session()

    def login(self, username, password):
        token = self.session.post(f'{self.BASE_URL}/api/auth/token/', json={
            'username': username, 'password': password,
        }).json()['token']
        self.session.headers['Authorization'] = f'Token {token}'

    def get_products(self, **params):
        return self.session.get(
            f'{self.BASE_URL}/api/products/', params=params
        ).json()['results']

client = APIClient()
client.login('ВАШ_ЛОГИН', 'ВАШ_ПАРОЛЬ')
print(len(client.get_products()))
print(len(client.get_products(category='chairs')))
03

Добавить метод add_to_cart()

#

Добавьте в APIClient метод add_to_cart(product_slug, quantity=1). Он должен POST-ить к /api/cart/items/ и возвращать ответ. Протестируйте, получив slug первого продукта и добавив его в корзину.

import requests

class APIClient:
    BASE_URL = 'https://apilearn.tukas.dev'

    def __init__(self):
        self.session = requests.Session()

    def login(self, username, password):
        token = self.session.post(f'{self.BASE_URL}/api/auth/token/', json={
            'username': username, 'password': password,
        }).json()['token']
        self.session.headers['Authorization'] = f'Token {token}'

    def get_products(self, **params):
        return self.session.get(
            f'{self.BASE_URL}/api/products/', params=params
        ).json()['results']

    def add_to_cart(self, product_slug, quantity=1):
        # POST к /api/cart/items/ и вернуть ответ
        pass

client = APIClient()
client.login('ВАШ_ЛОГИН', 'ВАШ_ПАРОЛЬ')
slug = client.get_products()[0]['slug']
item = client.add_to_cart(slug, quantity=2)
print(item['id'])
Решение
import requests

class APIClient:
    BASE_URL = 'https://apilearn.tukas.dev'

    def __init__(self):
        self.session = requests.Session()

    def login(self, username, password):
        token = self.session.post(f'{self.BASE_URL}/api/auth/token/', json={
            'username': username, 'password': password,
        }).json()['token']
        self.session.headers['Authorization'] = f'Token {token}'

    def get_products(self, **params):
        return self.session.get(
            f'{self.BASE_URL}/api/products/', params=params
        ).json()['results']

    def add_to_cart(self, product_slug, quantity=1):
        return self.session.post(
            f'{self.BASE_URL}/api/cart/items/',
            json={'product_slug': product_slug, 'quantity': quantity},
        ).json()

client = APIClient()
client.login('ВАШ_ЛОГИН', 'ВАШ_ПАРОЛЬ')
slug = client.get_products()[0]['slug']
item = client.add_to_cart(slug, quantity=2)
print(item['id'])
04

Полный класс APIClient

#

Соберите полный APIClient с методами: login(), get_products(**params), get_profile(), add_to_cart(product_slug, quantity=1), clear_cart(). Затем используйте его чтобы: авторизоваться, получить продукты, добавить первый в корзину, распечатать профиль и очистить корзину.

import requests

class APIClient:
    BASE_URL = 'https://apilearn.tukas.dev'

    def __init__(self):
        self.session = requests.Session()

    def login(self, username, password):
        pass

    def get_products(self, **params):
        pass

    def get_profile(self):
        pass

    def add_to_cart(self, product_slug, quantity=1):
        pass

    def clear_cart(self):
        pass

# Используйте все методы
Решение
import requests

class APIClient:
    BASE_URL = 'https://apilearn.tukas.dev'

    def __init__(self):
        self.session = requests.Session()

    def login(self, username, password):
        token = self.session.post(f'{self.BASE_URL}/api/auth/token/', json={
            'username': username, 'password': password,
        }).json()['token']
        self.session.headers['Authorization'] = f'Token {token}'

    def get_products(self, **params):
        return self.session.get(
            f'{self.BASE_URL}/api/products/', params=params
        ).json()['results']

    def get_profile(self):
        return self.session.get(f'{self.BASE_URL}/api/users/profile/').json()

    def add_to_cart(self, product_slug, quantity=1):
        return self.session.post(
            f'{self.BASE_URL}/api/cart/items/',
            json={'product_slug': product_slug, 'quantity': quantity},
        ).json()

    def clear_cart(self):
        self.session.delete(f'{self.BASE_URL}/api/cart/items/')

client = APIClient()
client.login('ВАШ_ЛОГИН', 'ВАШ_ПАРОЛЬ')

products = client.get_products()
print(f'Продуктов: {len(products)}')

item = client.add_to_cart(products[0]['slug'])
print(f'Добавлен id элемента корзины: {item["id"]}')

profile = client.get_profile()
print(f'Авторизован как: {profile["username"]}')

client.clear_cart()
print('Корзина очищена')
05

Расширить класс методом place_order()

#

Добавьте метод place_order(phone_number) в APIClient. Он должен POST-ить к /api/orders/ и возвращать ответ. Затем напишите скрипт, который использует класс чтобы авторизоваться, добавить продукт, оформить заказ и вывести id и статус заказа.

import requests

class APIClient:
    BASE_URL = 'https://apilearn.tukas.dev'

    def __init__(self):
        self.session = requests.Session()

    def login(self, username, password):
        token = self.session.post(f'{self.BASE_URL}/api/auth/token/', json={
            'username': username, 'password': password,
        }).json()['token']
        self.session.headers['Authorization'] = f'Token {token}'

    def get_products(self, **params):
        return self.session.get(
            f'{self.BASE_URL}/api/products/', params=params
        ).json()['results']

    def add_to_cart(self, product_slug, quantity=1):
        return self.session.post(
            f'{self.BASE_URL}/api/cart/items/',
            json={'product_slug': product_slug, 'quantity': quantity},
        ).json()

    def place_order(self, phone_number):
        # POST к /api/orders/ и вернуть ответ
        pass

# Используйте класс для полного процесса заказа
Решение
import requests

class APIClient:
    BASE_URL = 'https://apilearn.tukas.dev'

    def __init__(self):
        self.session = requests.Session()

    def login(self, username, password):
        token = self.session.post(f'{self.BASE_URL}/api/auth/token/', json={
            'username': username, 'password': password,
        }).json()['token']
        self.session.headers['Authorization'] = f'Token {token}'

    def get_products(self, **params):
        return self.session.get(
            f'{self.BASE_URL}/api/products/', params=params
        ).json()['results']

    def add_to_cart(self, product_slug, quantity=1):
        return self.session.post(
            f'{self.BASE_URL}/api/cart/items/',
            json={'product_slug': product_slug, 'quantity': quantity},
        ).json()

    def place_order(self, phone_number):
        return self.session.post(
            f'{self.BASE_URL}/api/orders/',
            json={'phone_number': phone_number},
        ).json()

client = APIClient()
client.login('ВАШ_ЛОГИН', 'ВАШ_ПАРОЛЬ')

products = client.get_products()
client.add_to_cart(products[0]['slug'])

order = client.place_order('+79991234567')
print(order['id'])
print(order['status'])