Как заставить 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>