Перейти к содержанию

Представления

Представления - основная логика нашего сайта. Перейдём в файл views.py в нашем приложении и напишем несколько необходимых для нас функций.

from django.contrib import messages
from django.contrib.auth import login
from django.shortcuts import render, redirect

from .forms import RegistrationForm


def register_request(request):
    if request.method == "POST":
        form = RegistrationForm(request.POST)
        if form.is_valid():
            user = form.save()
            login(request, user)
            messages.success(request, "Вы успешно зарегистрировались!")
            return redirect("view_flights")
        messages.error(request, "Регистрация не удалась. Проверьте введённые данные.")
    else:
        form = RegistrationForm()
    return render(request=request, template_name="flights/register.html", context={"register_form": form})

Если пользователь отправил GET-запрос, ему нужно вывести форму регистрации. Для этого создадим объект формы и вызовем функцию render, которая отправит этот объект в наш шаблон register.html, отрендерит его и вернёт пользователю.

Если пользователь отправил POST-запрос, значит он уже заполнил форму и нажал на submit кнопку. В таком случае нам нужно провалидировать введённые им данные. Для этого создадим объект формы регистрации с этими данными (получим их из request.POST) и вызовем метод is_valid().

Если метод вернул True, значит все валидации прошли успешно. В таком случае создаём новый объект пользователя, авторизуем его с помощью функции login(), отправим ему сообщение об успехе и переведём на страницу просмотра рейсов с помощью функции redirect.

Если метод вернул False, значит данные в форме заполнены некорректно. Поэтому мы отправим пользователю соответствующее сообщение и вернём форму с уже введёнными данными так же с помощью функции render.

from django.contrib import messages
from django.contrib.auth import login, authenticate
from django.contrib.auth.forms import AuthenticationForm
from django.shortcuts import render, redirect


def login_request(request):
    if request.method == "POST":
        form = AuthenticationForm(request, data=request.POST)
        if form.is_valid():
            username = form.cleaned_data.get("username")
            password = form.cleaned_data.get("password")
            user = authenticate(username=username, password=password)
            if user:
                login(request, user)
                messages.info(request, f"Вы вошли как {user.last_name} {user.first_name}.")
                return redirect("view_flights")
            else:
                messages.error(request, "Неверный логин или пароль.")
        else:
            messages.error(request, "Неверный логин или пароль.")
    form = AuthenticationForm()
    return render(request=request, template_name="flights/login.html", context={"login_form": form})

Для входа нам достаточно встроенной формы авторизации AuthenticationForm, воспользуемся ей.

Если пользователь отправил GET-запрос, вернём ему эту форму через шаблон login.html.

Если же пользователь отправил POST-запрос, так же проверим валидации формы, получим из неё логин и пароль и проверим их корректность с помощью функции authenticate. Если проверка успешна, авторизуем пользователя с помощью функции login, отправим сообщение об успехе и переведём на страницу просмотра рейсов.

Если же форма не прошла валидацию или введённые данные не прошли проверку, выведем сообщение с ошибкой и вернём заполненную форму обратно.

from django.contrib import messages
from django.contrib.auth import logout
from django.shortcuts import redirect


def logout_request(request):
    logout(request)
    messages.info(request, "Вы успешно вышли.")
    return redirect("login")

Самое базовое представление. Вызываем функцию logout, отправляем сообщение об успешном выходе и переводим на страницу входа.

from django.contrib.auth.decorators import login_required
from django.shortcuts import render

from .models import Flight


@login_required
def view_flights(request):
    flights = Flight.objects.exclude(bookings__user=request.user).filter(date__gt=timezone.now()).order_by("date")
    return render(request, "flights/flights_list.html", {"flights": flights})

С помощью декоратора @login_required обеспечиваем доступ к этому представлению только для авторизованных пользователей.

С помощью ORM обращаемся к модели Flight и получаем список рейсов, исключая из него с помощью метода excluded те рейсы, на которые у пользователя уже есть бронь, фильтруя с помощью метода filter только те рейсы, время отправления которых позже текущего времени, и сортируя их с помощью метода order_by по времени отправления.

Отправляем этот список в функцию render вместе с шаблоном flights_list.html и отправляем пользователю.

from django.contrib.auth.decorators import login_required
from django.shortcuts import render
from django.utils import timezone

from .models import Booking


@login_required
def view_bookings(request, tab):
    if tab == "upcoming":
        bookings = Booking.objects.filter(user=request.user, flight__date__gte=timezone.now())
    else:
        bookings = Booking.objects.filter(user=request.user, flight__date__lt=timezone.now())

    return render(request, "flights/my_flights.html", {"bookings": bookings, "active_tab": tab})

Это представление так же защищаем от неавторизованных пользователей с помощью декоратора @login_required.

Для просмотра броней хочется сделать, чтобы была возможность просматривать предстоящие и уже завершённые рейсы. Для этого будем передавать в маршруте название активной вкладки, которое наше представление получит в виде арумента tab.

Если tab == "upcoming", то есть просматриваются предстоящие рейсы, с помощью ORM и фильтров будем получать только те брони, которые, в первую очередь, связаны с нашим пользователем и время рейса которых позже текущего.

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

В шаблон my_flights.html отправляем активную вкладку и полученные брони, рендерим и отправляем пользователю.

from django.contrib import messages
from django.contrib.auth.decorators import login_required
from django.shortcuts import redirect

from .models import Flight, Booking


@login_required
def book_flight(request, flight_id):
    flight = Flight.objects.get(id=flight_id)
    Booking.objects.create(user=request.user, flight=flight)
    messages.success(request, "Рейс успешно забронирован!")
    return redirect("view_flights")

Это представление так же защищаем от неавторизованных пользователей с помощью декоратора @login_required.

Также это представление должно получить id рейса из маршрута пользователя. С помощью ORM получаем объект выбранного рейса и создаём новую запись в таблице броней, привязав к ней пользователя, отправившего запрос и и полученный рейс.

Отправляем сообщение об успехе и переводим на страницу просмотра рейсов.

from django.contrib import messages
from django.contrib.auth.decorators import login_required
from django.shortcuts import redirect

from .models import Booking


@login_required
def delete_booking(request, booking_id):
    booking = Booking.objects.get(id=booking_id, user=request.user)
    booking.delete()
    messages.success(request, "Бронь успешно отменена!")
    return redirect("view_bookings", "upcoming")

Представление должно получить id брони, которую планируется удалить, поэтому укажем это в аргументах функции.

С помощью ORM получим объект этой брони (в случае если она привязана к этому пользователю) и с помощью метода delete удалим её.

Затем вернём сообщение об успехе и переведём на вкладку предстоящих рейсов страницы просмотра броней (кнопка для удаления броней будет только на странице предстоящих рейсов, поэтому переводим именно туда).

from django.contrib import messages
from django.contrib.auth.decorators import login_required
from django.shortcuts import render, redirect

from .forms import FeedbackForm
from .models import Flight


@login_required
def give_feedback(request, flight_id):
    if request.method == "POST":
        form = FeedbackForm(request.POST)
        if form.is_valid():
            feedback = form.save(commit=False)
            feedback.user = request.user
            feedback.flight = Flight.objects.get(id=flight_id)
            feedback.save()
            messages.success(request, "Ваш отзыв успешно отправлен!")
            return redirect("view_bookings", "past")
    else:
        form = FeedbackForm()
    return render(request, "flights/feedback.html", {"form": form})

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

Если пользователь отправил GET-запрос, вернём его отрендеренный шаблон feedback.html с пустой формой.

Если пользователь отправил POST-запрос, провалидируем введённые им данные и в случае успеха создадим объект на основе полученных данных, привяжем к нему пользователя и рейс и сохраним в БД. Отправим сообщение об успехе и переведём на вкладку прошедших рейсов страницы просмотра броней (кнопка для отзывов будет только на странице прошедших рейсов, поэтому переводим именно туда).

from django.contrib.auth.decorators import login_required
from django.shortcuts import render, get_object_or_404

from .models import Flight


@login_required
def flight_details(request, flight_id):
    flight = get_object_or_404(Flight, pk=flight_id)
    return render(request, "flights/flight_details.html", {"flight": flight})

Представление получает id рейса и пытается его найти. Если такого рейса не существует, отправляет ошибку 404, в ином случае отправляем объекта рейса в шаблон flight_details.html, рендерит и отправляет пользователю.