Как заставить Django перенаправлять на запрашиваемую страницу с LoginRequiredMixin после входа в систему?

Фон:

У меня есть приложение для инвентаризации, которое просматривает наши виртуальные машины и среды хранения для индексации вещей, чтобы моя команда могла получить некоторые быстрые ссылки без необходимости напрямую входить в vCenter/системы хранения для получения информации.

Поскольку он работает с потенциально конфиденциальной информацией, я наложил на него аутентификацию путем использования LoginRequiredMixin в моих классах Views. Часть аутентификации уже давно установлена и работает без проблем. Часть, на которой я застрял, заключается в том, что я реализовал параметр next в форме входа в систему, и он отображается в URL при запросе на вход. У меня есть отдельный класс представления для каждого типа вещей, которые я отслеживаю.

Гол:

Когда кто-то нажимает на ссылку, требующую аутентификации (в основном все страницы, кроме главной), он перенаправляет его на нужную страницу, требуя войти в систему.

Я видел несколько других вопросов, но упомянутые рекомендации по какой-то причине не сработали в моем случае. Я пробовал различные варианты реализации поля redirect_field_name, но оно все равно пытается перенаправить на /accounts/profile, если я не переопределяю его на странице настроек. У меня не установлено поле login_url, но перенаправление на страницу входа работает, как ожидалось.

----Code Snippets----

LoginView - Используем Django's LoginView и форму аутентификации в качестве основы, и просто соединяем их вместе, чтобы сохранить возможность поиска в верхней части страницы.

class LoginView(SearchMixin, auth_views.LoginView):
    template_name = 'VMwareInventory/Registration/login.html'
    form = AuthenticationForm

    def get_context_data(self, **kwargs):
        context = super(LoginView, self).get_context_data()
        context['login_form'] = self.form
        return context

Пример представления, требующего входа в систему. url заканчивается правильно: /accounts/login?next=/Clusters/

class StorageView(SearchMixin, LoginRequiredMixin, ListView):
    model = StorageSystem
    template_name = 'VMwareInventory/Storage_templates/cluster_list_page.html'
    context_object_name = 'cluster_list'
    queryset = StorageSystem.objects.all()

    def get_context_data(self, *args, **kwargs):
        context = super(StorageView, self).get_context_data()
        clusters = StorageSystem.objects.all()

        context['clusters'] = clusters

        return context

    def get(self, request, *args, **kwargs):
        if self.request.is_ajax():
            sorting_method = self.request.GET.get('sorting_method')
            ascending = self.request.GET.get('ascending')
            data = []
            query = StorageSystem.objects.all()
            results = None
            if ascending == "true":
                results = query.order_by(sorting_method)
            elif ascending != "true":
                results = query.order_by(sorting_method).reverse()
            else:
                query = None
                data = serialize('json', None)

            if query is not None:
                for obj in results:
                    url_link = '<a href="' + obj.url + '">' + obj.name + '</a>'
                    json_data = {"name": url_link, "node_count": obj.node_count}
                    data.append(json_data)

            return JsonResponse(data=data, safe=False)
        else:
            return render(self.request, self.template_name, context=self.get_context_data())

Страница входа в систему.html:

<div class="login" style="vertical-align: middle; height: 100%; margin-top: 13%">
        <table class="login" style="border: none; margin-top: auto; margin-bottom: auto">
            <tr>
                <td>
                    {% if form.errors %}
                        <p>Your username and/or password is incorrect.  Please try again</p>
                    {% endif %}

                    {% if next %}
                        <p>You need to login first before you can do that</p>
                    {% else %}
                        <p>To see this page, please login with Username and Password</p>
                    {% endif %}

                    <form method="post" action="{% url 'VMwareInventory:login' %}">
                        {% csrf_token %}
                        <table>
                                <tr>
                                    <td>{{ login_form.username.label_tag }}</td>
                                    <td>{{ login_form.username }}</td>
                                </tr>
                                <tr>
                                    <td>{{ login_form.password.label_tag }}</td>
                                    <td>{{ login_form.password }}</td>
                                </tr>
                        </table>
                        <div style="padding-top: 5px; text-align: center">
                            <input type="submit" value="login"/>
                            <input type="hidden" name="next" value="{{ next }}"/>
                        </div>
                    </form>
                </td>
            </tr>
        </table>

Урлы:

path('accounts/login/', views.LoginView.as_view(), name='login'),
    path('accounts/logout/', views.LogoutView.as_view(), name='logout'),
    path('accounts/password_change/', views.PasswordChangeView.as_view(), name='password_change'),
    path('accounts/password_change/done/', views.PasswordChangeDoneView.as_view(), name='password_change_done')

path('Clusters/', views.StorageView.as_view(), name='storList'), # cluster url for view listed above.

OK, поэтому, немного покопавшись в коде Django и посмотрев, как работает процесс, я обнаружил, что значение next не попадает в данные POST. В итоге мне пришлось перехватить значение next из request.GET.get('next') и поместить его в переменную redirect в html (я изменил ее с переменной next, которая была у меня изначально).

В целом, я внес всего несколько изменений в код, чтобы заставить его работать. Мне не пришлось заново создавать весь вид (что было полезно).

Надеюсь, это поможет некоторым другим людям с такой же проблемой запустить это без необходимости полностью переписывать представление или переопределять кучу методов.

Редактирование: Мне пришлось добавить проверку, чтобы убедиться, что если нет следующего значения, то оно не заполнит поле перенаправления.

LoginView:

class LoginView(SearchMixin, auth_views.LoginView):
    template_name = 'VMwareInventory/Registration/login.html'
    redirect_field_name = "redirect"  # added
    redirect_authenticated_user = True  # added
    form = AuthenticationForm

    def get_context_data(self, **kwargs):
        context = super(LoginView, self).get_context_data()
        context['login_form'] = self.form
        if self.request.GET.get('next'):
            context['redirect'] = self.request.GET.get('next')  # added

        return context

login.html:

<div class="login" style="vertical-align: middle; height: 100%; margin-top: 13%">
        <table class="login" style="border: none; margin-top: auto; margin-bottom: auto">
            <tr>
                <td>
                    {% if form.errors %}
                        <p>Your username and/or password is incorrect.  Please try again</p>
                    {% endif %}

                    {% if next %}
                        <p>You need to login first before you can do that</p>
                    {% else %}
                        <p>To see this page, please login with Username and Password</p>
                    {% endif %}

                    <form method="post" action="{% url 'VMwareInventory:login' %}">
                        {% csrf_token %}
                        <table>
                                <tr>
                                    <td>{{ login_form.username.label_tag }}</td>
                                    <td>{{ login_form.username }}</td>
                                </tr>
                                <tr>
                                    <td>{{ login_form.password.label_tag }}</td>
                                    <td>{{ login_form.password }}</td>
                                </tr>
                        </table>
                        <div style="padding-top: 5px; text-align: center">
                            <input type="submit" value="login"/>
                            <input type="hidden" name="redirect" value="{{ redirect }}"/>  # changed from before
                        </div>
                    </form>
                </td>
            </tr>
        </table>
Вернуться на верх