Django: объединить ListView и DeleteView для использования с HTMX?
Я использую Django с HTMX для управления CRUD-таблицей, в которой я хочу перечислять и удалять объекты.
Для этого у меня есть ListView, который отображает таблицу (используя django-tables) с функциями пагинации, сортировки и текстового поиска. С HTMX это работает как ожидалось: например, если вы переходите на следующую страницу, вся страница не перезагружается: HTMX получает таблицу и загружает определенную часть DOM.
Выглядит это следующим образом:
Код ListView выглядит следующим образом:
class Proposals(SingleTableMixin, SearchableMixin, ListView):
table_class = ProposalTable # for django-tables2
ordering = "id"
model = Proposal
paginate_by = 5
search_filters = ["title__icontains"] # custom text search
def get_template_names(self):
# Trick to chose between loading the whole page (first time user goes to the page) or just the table via HTMX (when user uses pagination or sorting, it reloads the table only in the DOM)
if self.request.htmx:
return "fragments/proposals_fragment.html"
return "proposals.html"
Сейчас я пытаюсь добавить функцию удаления с наилучшим UX. До сих пор я исследовал несколько способов:
Простое удаление строки из DOM после того, как объект фактически удален из БД → плохое: хотя это быстро, это делает пагинацию неправильной/непоследовательной, и с меньшим количеством объектов на странице таблицы.
Указание HTMX перенаправить на текущий url (
response["HX-Redirect"] = request.htmx.current_url
) → плохо: хотя конечный результат в порядке, это медленно, и пользователь может использовать UI и совершать нежелательные действия, пока перенаправление фактически не произошло. И, конечно, я не пользуюсь возможностями частичного обновления DOM в HTMX.
Итак, я подумал о третьем методе, который звучит лучше:
- Когда пользователь удаляет объект, он должен удалить объект в БД, а затем действовать точно так же, как ListView в режиме HTMX (т.е. возвращать страницу таблицы). Таким образом, я смогу локально обновлять DOM новой страницей таблицы, без полной перезагрузки страницы. .
Дело в том, что я понятия не имею, как это сделать в Django. Я почти уверен, что не хочу возиться с FBV, но мне нужно какое-то руководство. Вот вещи, о которых я думаю:
- использование двух разных адресов ("proposals" (GET) и "proposals/int:pk/delete" (DELETE)), указывающих на одно и то же представление .
- Это представление может быть пользовательским представлением с именем "ListDeleteView", объединяющим миксины Django более низкого уровня, как
MultipleObjectTemplateResponseMixin
+BaseListView
+DeletionMixin
. А может быть и другие?
На правильном ли я пути? Можете ли вы дать некоторые рекомендации, особенно о том, как сделать комбинированные общие представления, если это хорошая идея для вас?
Большое спасибо.
Что вы хотите сделать, так это реализовать delete
метод на Proposals
представлении списка, DeleteMixin
реализация очень проста, поэтому вы можете использовать ее и получить что-то вроде этого:
class Proposals(SingleTableMixin, SearchableMixin, DeletionMixin, ListView):
table_class = ProposalTable # for django-tables2
ordering = "id"
model = Proposal
paginate_by = 5
search_filters = ["title__icontains"] # custom text search
def get_template_names(self):
if self.request.htmx and not self.request.htmx.history_restore_request:
return "fragments/proposals_fragment.html"
return "proposals.html"
def delete(self, request, *args, **kwargs):
super().delete(request, *args, **kwargs)
return super().get(request, *args, **kwargs)
Здесь вы переопределяете delete, позволяете ему играть, а затем возвращаете результат как обычный get вместо редиректа. И вы также хотите обработать htmx.history_restore_request
Я добавил и это.
В качестве альтернативы вы можете использовать представление функции, которое ничего не возвращает, чтобы заменить удаленную строку. Следуя вашему примеру
@require_http_methods(['DELETE'])
def delete_proposal(request, pk):
Proposals.objects.filter(id=pk).delete()
return render(request, "proposal/partial/proposal_delete.html")
Где proposal/partial/proposal_delete.html
будет пустой файл. Вам нужно будет ввести это представление в ваш hx-delete
, но это должно сделать работу.