Учебное пособие по удалению событий API с помощью SQL ORM

В настоящее время сертификаты встречаются повсеместно. Документирование небольших проектов по повышению квалификации, которыми можно впоследствии похвастаться, – лучший способ получить признание в качестве профессионального инженера.

Задачи TL;DR

Загрузите предоставленный код для решения задачи.

Задача 1: Реализация функции отмены билетов

  • Добавьте новый API-маршрут или конечную точку для обработки отмены билета на основе его ID.
  • Перед тем как разрешить отмену, проверьте, не началось ли событие, связанное с билетом.
  • Убедитесь, что билет удаляется из базы данных, если отмена прошла успешно.

Задача 2: Реализовать функцию изменения имени билета

  • Добавьте еще один маршрут или конечную точку API для изменения имени билета на основе его ID.
  • Перед тем как разрешить изменение имени, проверьте, не началось ли еще событие для билета.
  • Обновление имени билета в базе данных, если изменение имени действительно.

Задача 3: Обработка связанных тикетов при удалении события

  • Модифицируйте API-маршрут или конечную точку для удаления события, чтобы также удалять все связанные с ним билеты.
  • Убедитесь, что при удалении события и билетов на него они удаляются из базы данных.

Задача 4: Модульные тесты для функции аннулирования билетов

  • Напишите модульные тесты для проверки корректности работы отмены билетов.
  • Включите тесты для проверки того, что в API выполняется условие запрета отмены билетов после начала мероприятия.

Задача 5: Модульные тесты для функции изменения имени билета

  • Создайте модульные тесты для проверки функциональности изменения имени в билете.
  • Убедитесь, что API правильно проверяет и предотвращает изменение имени после начала события.

Задача 6: Модульные тесты для связанных тикетов при удалении события

  • Написать модульные тесты, подтверждающие удаление всех связанных с событием тикетов при его удалении.
  • Протестируйте сценарии с несколькими билетами, связанными с событием, чтобы убедиться, что все они удалены.

Задача 7: Протестировать ограничение времени начала события

  • Создайте тесты для проверки того, ограничена ли отмена билетов и изменение имени после времени начала мероприятия.
  • Протестируйте оба случая на предмет действительных и недействительных изменений/отмен на основе времени начала мероприятия.

Задача 8: Обеспечить обработку ошибок и ответных сообщений

  • Реализовать соответствующую обработку ошибок при недействительных запросах (например, при попытке отменить/обменять билет после начала мероприятия).
  • Обеспечьте четкие и информативные ответные сообщения для различных сценариев.

Задача 9: Протестировать весь API с новой функциональностью

  • Запуск и проверка всего API с добавлением функций отмены билетов и изменения имени.
  • Тестирование различных сценариев и граничных ситуаций для обеспечения корректности и надежности API.

Решение

Мы просто добавляем новые возможности в наш API – в этой задаче давайте добавим функцию отмены и обновления. Вот как я реализовал эти изменения, сначала на операционном уровне:

# after
def delete_ticket(ticket_id: int, database: Database) -> Ticket:
    ticket = get_ticket(ticket_id, database)
    event = get_event(ticket.event_id, database)
    
    if event.has_started():
        raise EventAlreadyStarted(
            f"Event with id {ticket.event_id} has already started."
        )

    database.delete(ticket)
    event.available_tickets += 1
    
    database.commit()
    return ticket

def update_ticket(ticket_id: int, ticket_update: TicketUpdate, database: Database) -> Ticket:
    ticket = get_ticket(ticket_id, database)
    event = get_event(ticket.event_id, database)
    
    if event.has_started():
        raise EventAlreadyStarted(
            f"Event with id {ticket.event_id} has already started."
        )

    if ticket_update.customer_name:
        ticket.customer_name = ticket_update.customer_name
    database.commit()

Для обновления билетов мы можем создать новый объект базы данных TicketUpdate и отделить его от операций. И get_ticket, и get_event имеют свои собственные ошибки NotFoundError, которые мы хорошо продемонстрировали в последней задаче (посмотрите, если хотите перевести обработку ошибок без ответственности за сопряжение). Для дальнейшего поддержания низкого уровня сопряжения я добавил ошибку EventAlreadyStarted, которая возникает, когда четное событие уже началось. Как это реализовать?

Самое замечательное в SQLalchemy ORM то, что мы можем встраивать методы непосредственно в модель!

# Define event model
class Event(Base):
    __tablename__ = "events"

    id: Mapped[int] = mapped_column(Integer, primary_key=True, index=True)
    title: Mapped[str] = mapped_column(String, index=True)
    location: Mapped[str] = mapped_column(String, index=True)
    start_date: Mapped[datetime] = mapped_column(DateTime)
    end_date: Mapped[datetime] = mapped_column(DateTime)
    available_tickets: Mapped[int] = mapped_column(Integer, index=True)

    def has_started(self):
        return self.start_date < datetime.now()

Теперь, когда мы добавили операции для взаимодействия с базой данных, давайте посмотрим, как мы можем использовать слой операций для работы с маршрутизацией:

@app.put("/tickets/{ticket_id}")
async def update_ticket(
    ticket_id: int, ticket_update: TicketUpdate, database:   Session = Depends(get_db)
) -> Ticket:
    try:
        return operations.update_ticket(ticket_id, ticket_update, database)
    except operations.NotFoundError:
        raise HTTPException(status_code=404)
    except operations.EventAlreadyStarted:
        raise HTTPException(status_code=400)
    
@app.delete("/tickets/{ticket_id}")
async def cancel_ticket(ticket_id: int, database: Session = Depends(get_db)) -> Ticket:
    try:
        return operations.delete_ticket(ticket_id, database)
    except operations.NotFoundError:
        raise HTTPException(status_code=404)
    except operations.EventAlreadyStarted:
        raise HTTPException(status_code=400)

Операционный уровень может быть использован для разделения обработки исключений при маршрутизации и взаимодействии с базой данных путем инкапсуляции операций с базой данных в отдельный модуль или класс. Этот модуль или класс может обрабатывать все взаимодействия с базой данных и вызывать пользовательские исключения для любых ошибок, возникающих при этих взаимодействиях.

Затем слой маршрутизации может импортировать этот модуль или класс и использовать его для выполнения необходимых операций с базой данных. Если в процессе работы с базой данных возникает ошибка, то пользовательское исключение, вызванное операционным уровнем, может быть поймано и обработано соответствующим образом уровнем маршрутизации, в нашем случае для ticketsNotFoundError и eventsEventAlreadyStarted, которые будут преобразованы в исключения маршрутизации HTTPException.

Такое разделение задач позволяет сделать код более чистым и удобным для сопровождения, поскольку уровень маршрутизации может сосредоточиться на обработке HTTP-запросов и ответов, а операционный уровень – на обработке взаимодействий с базой данных и ошибок.

Вот и все!

+1
0
+1
0
+1
0
+1
0
+1
0

Ответить

Ваш адрес email не будет опубликован. Обязательные поля помечены *