Является ли это правильным способом обработки всех CRUD-операций в одном представлении (конечной точке) с помощью HTMX?

Я новичок в использовании HTMX с Django и ищу совета. Я нахожусь в процессе редизайна большого CRUD-приложения, стремясь интегрировать HTMX для улучшения взаимодействия. Учитывая сложность приложения и его взаимодействие с базой данных через различные модели, я хочу быть уверен, что начну с правильной ноги, приняв лучшие практики HTMX на раннем этапе.

Я видел во многих руководствах, что все следуют общепринятой практике создания отдельных представлений и URL-путей для каждой транзакции базы данных (такой как создание, редактирование, удаление, обновление). Поскольку я считаю, что это может привести к значительному количеству дополнительного кода и временным затратам, я бы предпочел поместить всю логику приложения в одно представление (или конечную точку) для каждой модели.

Можете ли вы, пожалуйста, подсказать мне, правильный ли это поток и правильный способ сделать это:

def movie(request, template_name="movies.html"):
    movies = Movie.objects.all()
    movie_form = MovieForm(request.POST or None)
    
    if request.method == 'POST':
        action = request.POST.get('action')
        
        # 1) CREATE
        if action == 'add_movie':
            if movie_form.is_valid():
                movie_form.save()
                content = render(request, 'partials/movie-table.html', {'movies': movies})
                return HttpResponse(content, status=201, 
                        headers={
                            'HX-Trigger': json.dumps({'create': movie_form.cleaned_data['name']})}
                            )
            else:
                content = render(request, 'error.html', {'error': movie_form.errors})
                return HttpResponse(content, status=202)
        # 2) DELETE
        elif action == 'delete_movie':
            Movie.objects.get(id=request.POST.get('movie_id')).delete()
            content = render(request, 'movie.html', {'movies': movies})
            return HttpResponse(content, status=201, 
                        headers={
                            'HX-Trigger': json.dumps({'delete: movie_form.cleaned_data['name']})}
                            )
        
        # 3) UPDATE (two steps are necessary)
        # first step is used to get the necessary movie data that will populate the form
        elif action == 'load_movie':
            movie_id = request.POST.get('movie_id')
            movie_form = MovieForm(instance = Movie.objects.get(id=request.POST.get('movie_id')))
            movie_form.id = movie_id
            return render(request, 'edit-movie.html', {'movie_form': movie_form})
        
        # UPDATE (second step, actual edit)
        elif action == 'edit_movie':
            movie_form = MovieForm(request.POST, instance = Movie.objects.get(id=request.POST.get('movie_id')))
            if movie_form.is_valid():
                movie_form.save()
                content = render(request, 'movie.html', {'movies': movies})
                return HttpResponse(content, status=201, 
                        headers={
                            'HX-Trigger': json.dumps({'update': movie_form.cleaned_data['name']})}
                            )
            else:
                return HttpResponse(status=400)
    return render(request, template_name, {'movies' : movies, 'movie_form' : movie_form})

Вот пояснение к приведенному выше коду - пользовательский интерфейс состоит из трех отдельных кнопок: Добавить, Редактировать и Удалить. Каждая кнопка вызывает свое действие, передаваемое в бэкенд с помощью аргумента hx-vals:

<div class="ui button" id="add_movie__button" name="add_movie__button" type="submit" hx-post="{% url 'movie' %}" hx-vals='{"action": "add_movie"}' hx-target="#table_movies"> На основании значения hx-vals (action) бэкенд определяет, как обрабатывать размещенные параметры. После завершения процесса добавления, редактирования или удаления бэкэнд отправляет соответствующий HX-триггер (например, 'update') на JavaScript. Этот триггер помогает определить последующие действия, такие как обновление модала и отображение соответствующих сообщений для пользователя.

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

На всякий случай, если кто-то захочет проверить, я включил соответствующие части шаблонов:

modals.html

    <!-- Delete button -->
    ...
    <div class="button" id="delete_movie__button" name="delete_movie__button" type="button" hx-post="{% url 'movie' %}" hx-target="#table_movies">Delete<div>
    ...

    <!-- Edit button, that is part of the edit form, that is part of the modal -->
    ...
    <div class="button" id="edit_movie__button" name="edit_movie__button" type="submit" hx-post="{% url 'movie' %}" hx-target="#table_movies" hx-vals='{"action": "edit_movie", "movie_id": "{{ movie_form.id }}"}'>
        <i class="add icon"></i> Edit
    ...

main_template.html

    ...

    function deleteMovieModal(){
        // opens delete movie modal
        $('#delete_movie__modal').modal('show');
    }
    function editMovieModal(){
        // opens edit movie modal
        $('#edit_movie__modal').modal('show');
    }
    function deleteMovieId(movie_id){
        // adds hx-vals argument to the delete button in delete movie modal
        document.getElementById("delete_movie__button").setAttribute("hx-vals",`{"action": "delete_movie", "movie_id": "${movie_id}"}`);    
    }

    ...

partials/table_movies.html

    ...

    <td>
        <!-- clicking this button opens delete movie modal and calls the function that passes an movie id argument to the delete button in delete movie modal -->
        <div class="button" onclick="deleteMovieModal();deleteMovieId({{ movie.id }});">Delete</div>
    </td>
    <td>
        <!-- clicking this button opens edit movie modal and sends htmx request that populates form data with choosen movie, in the edit movie modal -->
        <div class="button" onclick="editMovieModal();editMovieId({{ movie.id }});" hx-post="{% url 'movie' %}" hx-vals='{"action": "load_movie", "movie_id": {{ movie.id }} }' hx-target="#edit_movie__modal_content" >Edit </div>
    </td>

    ...

Можете ли вы поделиться своими мыслями о таком подходе? Любые мысли/идеи/предложения будут оценены по достоинству.

Вернуться на верх