Как реализовать HMTX с помощью Django Class Based Views
Я пытаюсь понять, как использовать htmx с Django Class Based Views. Чтобы обеспечить некоторый контекст, вот отражение сценария, в котором я хочу реализовать django class-based views и htmx вместе:
У меня есть начальное представление, которое отображает основной шаблон и начальный контекст. В качестве части этого начального контекста я включаю форму даты. Я хочу использовать htmx для отправки данных из этой формы в APIView (назовем его APIView1).
Я хочу, чтобы APIView1 получал данные, которые передаются ему через htmx, запускал на них функцию, а затем возвращал данные обратно в виде контекста, одновременно отображая частичный шаблон (назовем его PartialTemplate1), который использует этот контекст. Насколько я понимаю, я могу сделать это в рамках функции get(); так что это может выглядеть примерно так:
views.py
Class InitialView(View):
template_name = “initial_template.html”
form_class = date_form
def get(self, request, *args, **kwargs):
…
date_form=self.form_class()
context = {
'date_form': date_form
}
return render(request, template_name, context)
Class APIView1(APIView):
template_name = “PartialTemplate1.html”
def get(self, request, *args, **kwargs):
date = request.POST.get(‘date’)
timeslots = function(date)
context = {
‘timeslots’: timeslots
}
return render(request, template_name, context).
initial_template.html:
<form hx-post='{% url "appname:apiview1_url" %}' hx-target='#ticket-form'>
{% csrf_token %}
{{ date_form }}
<input type="submit" value="View Time Slots">
</form>
<div id="booking-form-container">
<div id="ticket-form">
<input type="submit" value="View Time Slots">
</div>
</div>
PartialTemplate1.html:
<div class="container mt-5" style="width: 80%">
<div id="time-slots">
{% for timeslot in timeslots %}
<button name="timeslot_button" hx-post='{% url " appname:apiview2_url" %}' hx-target="#booking-form-container" hx-swap="outerHTML show:#ticket-form:top " hx-include="this">
{{ timeslot.start_time|format_time }} - {{ timeslot.end_time|format_time }}
<input name="data-timeslot" value="{{timeslot}}" type="hidden">
</button>
{% endfor %}
</div>
</div>
PartialTemplate1 должен отображаться в основном шаблоне при отправке формы даты. Он должен содержать список свободных временных интервалов на заданную дату.
Затем пользователь может выбрать временной интервал из сегмента PartialTemplate1. Когда пользователь выбирает временной интервал, данные о выбранном им временном интервале должны быть помещены в другой APIView (APIView2):
Class APIView2(APIView):
form_class = ticket_form
def get(self, request, *args, **kwargs):
selected_timeslot = request.POST.get(‘timeslot’)
timeslot_ticket_data = function(selected_timeslot)
form = self.form_class(timeslot_ticket_data=timeslot_ticket_data)
return response(form)
APIView2 заменит список временных интервалов экземпляром формы (ticket_form). После этого пользователь мог заполнить форму.
Теперь, насколько я понимаю, данные из формы даты, которая была изначально включена, выбранного таймслота и билетов, которые они выбирают в форме билетов, должны быть доступны в этом исходном шаблоне? Таким образом, возвращаясь к моему первоначальному представлению, я должен иметь возможность в запросе post получить все эти данные, которые я сгенерировал с помощью htmx и моих APIViews, и отправить их вместе (при отправке ticket_form) для создания, что в данном примере было бы резюме бронирования?
Class InitialView(View):
…
def post(self, request, *args, **kwargs):
date = request.GET.get(‘date)
timeslot = request.GET.get(‘timeslot’)
form = ticket_form(request.POST)
if form.is_valid():
*create a new model instance using above data on post*
redirect(reverse(app_name:booking_summary, kwargs={'slug': self.kwargs[‘slug’]}))
Правильно ли я понимаю?
Если судить по тому, чего вам нужно достичь, вам не нужны три отдельных вида. Достаточно иметь два: InitialView
и TimeslotView
. InitialView
должен отрисовывать date_form
во время GET
запроса и по POST
запросу возвращать timeslots
частичный шаблон (partial_template_1.html). После этого TimeslotsView
останется только обработать POST
запрос и вернуть ticket_form
.
Вот предлагаемая структура:
class DateView(View): # or InitialView
def get(self, request, *args, **kwargs):
context = dict(date_form=date_form())
return render(request, self.template_name, context)
def post(self, request, *args, **kwargs):
date = request.POST.get("date")
timeslots = ....
context = dict(timeslots=timeslots)
return render(request, timeslots_partial.html, context)
class TimeslotsView(View):
def post(self, request, *args, **kwargs):
timeslot = request.POST.get("timeslot")
timeslot_data = function(timeslot)
ticket_form = ticket_form(timeslot_data=timeslot_data)
context = dict(form=ticket_form)
return render(request, ticket_form_partial.html, context)
class TicketView(View):
def get(self, request, *args, **kwargs):
....
def post(self, request, *args, **kwargs):
....
Основной шаблон:
<form hx-post='{% url "appname:date_view" %}' hx-target='#ticket-form'>
{% csrf_token %}
{{ date_form }}
<input type="submit" value="View Time Slots">
</form>
<div id="booking-form-container">
<div id="ticket-form">
<!-- leave this empty -->
</div>
</div>
timeslots_partial.html
Я превратил таймслоты в форму, потому что считаю, что логичнее представить их в виде формы, а не произвольного значения (наверное, есть исключения, но в данном случае я их не вижу.
).<form hx-post='{% url "appname:timeslots_view" %}' hx-target='#ticket-form'>
{% csrf_token %}
<label for="timeslot">Available timeslots </label>
<select id="timeslot" name="timeslot">
{% for timeslot in timeslots %}
<option value="{{timeslot}}"> {{ timeslot.start_time|format_time }} - {{ timeslot.end_time|format_time }} </option>
{% endfor %}
</select>
<input type="submit" value="View ticket">
</form>
<div id="booking-form-container">
<div id="ticket-form">
<input type="submit" value="View Time Slots">
</div>
</div>
ticket_form.html
<form hx-post='{% url "appname:ticket_view" %}' hx-swap="none">
<!-- I don't know what you want to do after submitting a ticket, maybe redirect?? -->
{% csrf_token %}
{{ ticket_form }}
<input type="submit" value="Save ticket">
</form>
NOTE
В ваших методах get
в обоих APIViews вы пытаетесь получить доступ к представленному значению с помощью request.POST.get(..)
. Это не может сработать. request.POST
всегда будет None
при использовании внутри метода, который не является post
. Точно так же, как если вы используете request.GET.get(...)
внутри метода, который не является get
, это будет None
. Поэтому не забывайте использовать соответствующие значения для получения значений из объекта запроса.