简体   繁体   中英

Is it possible to convert these FBVs that return HttpResponse() into CBVs? (Django)

I currently have the following function-based views, their purpose is to take a specific action based on what the user selected:

@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)

I'm using htmx on the template to populate a div with the returned html.

There are several similar functions to the examples above. So I would like to convert these into a single CBV in order to reduce redundancy. The new CBV would also require some sort of check, in order to take the appropriate action. However, I'm not sure which class would be best suited for this, and which method would be best to override in this scenario.

(admin and members are M2M fields assigned to the Project model)

There is no reason to combine those two different actions into a single class based view.

However, you can convert them into their own class based views and extend from a basic class based view. But this will only reduce some lines and adding some extra lines.

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.'

But i would suggest to write them as their own views like:

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>'
        )

You could improve your solution even a bit more and getting the username displayed in the message in a safe way.

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,)))

The template remains unchanged

<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>

Thank you for all the advice regarding csrf and redirects. I came up with the following to solve those issues:

View:

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,)))

Template:

<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>

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM