Возможно ли преобразовать эти FBV, которые возвращают HttpResponse(), в CBV? (Django)

В настоящее время у меня есть следующие представления на основе функций, их цель - предпринять определенное действие, основанное на том, что выбрал пользователь:

@login_required()
@csrf_exempt
def remove_member(request, pk, project_id):
    if request.method == 'POST':
        user = get_object_or_404(User, id=pk)
        project = get_object_or_404(Project, id=project_id)
        project.members.remove(user)

        html = '<div class="alert alert-success" role="alert" id="message-response">' \
               'Update successful! ' + user.first_name + ' is no longer part of your team. </div>'

        return HttpResponse(html)

@login_required()
@csrf_exempt
def demote_admin(request, pk, project_id):
    if request.method == 'POST':
        user = get_object_or_404(User, id=pk)
        project = get_object_or_404(Project, id=project_id)
        project.admin.remove(user)

        html = '<div class="alert alert-success" role="alert" id="message-response">' \
               'Update successful! ' + user.first_name + ' is no longer an admin. </div>'

        return HttpResponse(html)

Я использую htmx в шаблоне, чтобы заполнить div с возвращенным html.

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

(admin и members - поля M2M, назначенные модели Project)

Нет причин объединять эти два разных действия в одно представление на основе класса.

Однако вы можете преобразовать их в собственные представления на основе классов и расширить из базового представления на основе классов. Но это только сократит некоторые строки и добавит несколько дополнительных строк.

from django.views.generic.base import View
from django.contrib.auth.mixins import LoginRequiredMixin


@csrf_exempt
class AbstractUserProjectView(LoginRequiredMixin, View):
    def post(request, pk, project_id):
        user = get_object_or_404(User, id=pk)
        project = get_object_or_404(Project, id=project_id)
        
        message = self.perform_action(user, project)

        return HttpResponse(
            '<div class="alert alert-success" role="alert" id="message-response">' + message + '</div>'
        )


@csrf_exempt
class RemoveMemberView(LoginRequiredMixin, View):
    def perform_action(user, project):
        project.members.remove(user)
        return 'Update successful! ' + user.first_name + ' is no longer part of your team.'


@csrf_exempt
class DemoteAdminView(LoginRequiredMixin, View):
    def perform_action(user, project):
        project.admin.remove(user)
        return 'Update successful! ' + user.first_name + ' is no longer an admin.'

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

from django.views.generic.base import View
from django.contrib.auth.mixins import LoginRequiredMixin

@csrf_exempt
class RemoveMemberView(LoginRequiredMixin, View):
    def post(request, pk, project_id):
        user = get_object_or_404(User, id=pk)
        project = get_object_or_404(Project, id=project_id)
        project.members.remove(user)
        return HttpResponse(
            '<div class="alert alert-success" role="alert" id="message-response">' \
               'Update successful! ' + user.first_name + ' is no longer part of your team. </div>'
        )


@csrf_exempt
class DemoteAdminView(LoginRequiredMixin, View):
    def post(request, pk, project_id):
        user = get_object_or_404(User, id=pk)
        project = get_object_or_404(Project, id=project_id)
        project.admin.remove(user)
        return HttpResponse(
            '<div class="alert alert-success" role="alert" id="message-response">' \
               'Update successful! ' + user.first_name + ' is no longer an admin. </div>'
        )

Спасибо за все советы относительно csrf и редиректов. Я придумал следующее решение этих проблем:

Вид:

class TeamUpdateView(ProjectAuthMixin, View):
    @staticmethod
    def post(request, *args, **kwargs):
        member = kwargs['pk']
        project = Project.objects.get(slug=kwargs['slug'])
        action = kwargs['edit']

        if action == 'remove-member':
            project.members.remove(member)
            messages.success(request, "Successfully removed user.")

        elif action == 'remove-admin':
            project.admin.remove(member)
            messages.success(request, "Successfully removed user from admin group.")

        elif action == 'promote-admin':
            project.admin.add(member)
            messages.success(request, "Successfully promoted user to admin.")

        return HttpResponseRedirect(reverse('customerportal:team', args=(project.slug,)))

Шаблон:

<div class="dropdown-menu">
    <form action="{% url 'customerportal:team-edit' slug 'remove-member' member.id %}" method="post">
        {% csrf_token %}
        <button type="submit" value="Submit" class="btn btn-block btn-primary">Remove Member</button>
    </form>
</div>

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

from django.shortcuts import get_object_or_404

class TeamUpdateView(ProjectAuthMixin, View):
    @staticmethod
    def post(request, *args, **kwargs):
        # use get_object_or_404() to return a 404 error
        # if a non-existing user or project is used
        member = get_object_or_404(User, pk=kwargs['pk'])
        project = get_object_or_404(Project, slug=kwargs['slug'])
        action = kwargs.get('edit')

        if action == 'remove-member':
            project.members.remove(member)
            # as we have the user object, we can add the username to the message
            # escaping is done by the template engine
            messages.success(request, "Successfully removed '%s'." % member.username)

        elif action == 'remove-admin':
            project.admin.remove(member)
            messages.success(request, "Successfully removed '%s' from admin group." % member.username)

        elif action == 'promote-admin':
            project.admin.add(member)
            messages.success(request, "Successfully promoted '%s' to admin." % member.username)

        return HttpResponseRedirect(reverse('customerportal:team', args=(project.slug,)))

Шаблон остается неизменным

<div class="dropdown-menu">
    <form action="{% url 'customerportal:team-edit' slug 'remove-member' member.id %}" method="post">
        {% csrf_token %}
        <button type="submit" value="Submit" class="btn btn-block btn-primary">Remove Member</button>
    </form>
</div>
Вернуться на верх