We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Editing an existing user token shows "create" buttons instead of "update"
<!--
    NOTE: IF YOUR ISSUE DOES NOT FOLLOW THIS TEMPLATE, IT WILL BE CLOSED.

    This form is only for reporting reproducible bugs. If you need assistance
    with Nautobot installation, or if you have a general question, please start a
    discussion instead: https://github.com/nautobot/nautobot/discussions

    Please describe the environment in which you are running Nautobot. Be sure
    that you are running an unmodified instance of the latest stable release
    before submitting a bug report, and that any plugins have been disabled.
-->
### Environment
* Python version:
* Nautobot version:

<!--
    Describe in detail the exact steps that someone else can take to reproduce
    this bug using the current stable release of Nautobot. Begin with the
    creation of any necessary database objects and call out every operation
    being performed explicitly. If reporting a bug in the REST API, be sure to
    reconstruct the raw HTTP request(s) being made: Don't rely on a client
    library such as pynautobot.
-->
### Steps to Reproduce
1. Navigate to user "Profile"
2. Navigate to "API Tokens"
3. Click "Add token"
4. Click "Create"
5. From the token list view, click "Edit" on the token you just created

<!-- What did you expect to happen? -->
### Expected Behavior

There should be an "Update" button.

<!-- What happened instead? -->
### Observed Behavior

There are "Create" and "Create and Add Another" buttons. 

![Cursor_and_Notification_Center](https://user-images.githubusercontent.com/138052/111223484-7176c900-859a-11eb-95c0-289b9361b01a.png)



--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `nautobot/users/views.py`
Content:
```
1 import logging
2 
3 from django.conf import settings
4 from django.contrib import messages
5 from django.contrib.auth import (
6     login as auth_login,
7     logout as auth_logout,
8     update_session_auth_hash,
9 )
10 from django.contrib.auth.mixins import LoginRequiredMixin
11 from django.contrib.auth.models import update_last_login
12 from django.contrib.auth.signals import user_logged_in
13 from django.http import HttpResponseForbidden, HttpResponseRedirect
14 from django.shortcuts import get_object_or_404, redirect, render
15 from django.urls import reverse
16 from django.utils.decorators import method_decorator
17 from django.utils.http import is_safe_url
18 from django.views.decorators.debug import sensitive_post_parameters
19 from django.views.generic import View
20 
21 from nautobot.utilities.forms import ConfirmationForm
22 from .forms import LoginForm, PasswordChangeForm, TokenForm
23 from .models import Token
24 
25 
26 #
27 # Login/logout
28 #
29 
30 
31 class LoginView(View):
32     """
33     Perform user authentication via the web UI.
34     """
35 
36     template_name = "login.html"
37 
38     @method_decorator(sensitive_post_parameters("password"))
39     def dispatch(self, *args, **kwargs):
40         return super().dispatch(*args, **kwargs)
41 
42     def get(self, request):
43         form = LoginForm(request)
44 
45         if request.user.is_authenticated:
46             logger = logging.getLogger("nautobot.auth.login")
47             return self.redirect_to_next(request, logger)
48 
49         return render(
50             request,
51             self.template_name,
52             {
53                 "form": form,
54             },
55         )
56 
57     def post(self, request):
58         logger = logging.getLogger("nautobot.auth.login")
59         form = LoginForm(request, data=request.POST)
60 
61         if form.is_valid():
62             logger.debug("Login form validation was successful")
63 
64             # If maintenance mode is enabled, assume the database is read-only, and disable updating the user's
65             # last_login time upon authentication.
66             if settings.MAINTENANCE_MODE:
67                 logger.warning("Maintenance mode enabled: disabling update of most recent login time")
68                 user_logged_in.disconnect(update_last_login, dispatch_uid="update_last_login")
69 
70             # Authenticate user
71             auth_login(request, form.get_user())
72             logger.info(f"User {request.user} successfully authenticated")
73             messages.info(request, "Logged in as {}.".format(request.user))
74 
75             return self.redirect_to_next(request, logger)
76 
77         else:
78             logger.debug("Login form validation failed")
79 
80         return render(
81             request,
82             self.template_name,
83             {
84                 "form": form,
85             },
86         )
87 
88     def redirect_to_next(self, request, logger):
89         if request.method == "POST":
90             redirect_to = request.POST.get("next", reverse("home"))
91         else:
92             redirect_to = request.GET.get("next", reverse("home"))
93 
94         if redirect_to and not is_safe_url(url=redirect_to, allowed_hosts=request.get_host()):
95             logger.warning(f"Ignoring unsafe 'next' URL passed to login form: {redirect_to}")
96             redirect_to = reverse("home")
97 
98         logger.debug(f"Redirecting user to {redirect_to}")
99         return HttpResponseRedirect(redirect_to)
100 
101 
102 class LogoutView(View):
103     """
104     Deauthenticate a web user.
105     """
106 
107     def get(self, request):
108         logger = logging.getLogger("nautobot.auth.logout")
109 
110         # Log out the user
111         username = request.user
112         auth_logout(request)
113         logger.info(f"User {username} has logged out")
114         messages.info(request, "You have logged out.")
115 
116         # Delete session key cookie (if set) upon logout
117         response = HttpResponseRedirect(reverse("home"))
118         response.delete_cookie("session_key")
119 
120         return response
121 
122 
123 #
124 # User profiles
125 #
126 
127 
128 class ProfileView(LoginRequiredMixin, View):
129     template_name = "users/profile.html"
130 
131     def get(self, request):
132 
133         return render(
134             request,
135             self.template_name,
136             {
137                 "active_tab": "profile",
138             },
139         )
140 
141 
142 class UserConfigView(LoginRequiredMixin, View):
143     template_name = "users/preferences.html"
144 
145     def get(self, request):
146 
147         return render(
148             request,
149             self.template_name,
150             {
151                 "preferences": request.user.all_config(),
152                 "active_tab": "preferences",
153             },
154         )
155 
156     def post(self, request):
157         user = request.user
158         data = user.all_config()
159 
160         # Delete selected preferences
161         for key in request.POST.getlist("pk"):
162             if key in data:
163                 user.clear_config(key)
164         user.save()
165         messages.success(request, "Your preferences have been updated.")
166 
167         return redirect("user:preferences")
168 
169 
170 class ChangePasswordView(LoginRequiredMixin, View):
171     template_name = "users/change_password.html"
172 
173     def get(self, request):
174         # LDAP users cannot change their password here
175         if getattr(request.user, "ldap_username", None):
176             messages.warning(
177                 request,
178                 "LDAP-authenticated user credentials cannot be changed within Nautobot.",
179             )
180             return redirect("user:profile")
181 
182         form = PasswordChangeForm(user=request.user)
183 
184         return render(
185             request,
186             self.template_name,
187             {
188                 "form": form,
189                 "active_tab": "change_password",
190             },
191         )
192 
193     def post(self, request):
194         form = PasswordChangeForm(user=request.user, data=request.POST)
195         if form.is_valid():
196             form.save()
197             update_session_auth_hash(request, form.user)
198             messages.success(request, "Your password has been changed successfully.")
199             return redirect("user:profile")
200 
201         return render(
202             request,
203             self.template_name,
204             {
205                 "form": form,
206                 "active_tab": "change_password",
207             },
208         )
209 
210 
211 #
212 # API tokens
213 #
214 
215 
216 class TokenListView(LoginRequiredMixin, View):
217     def get(self, request):
218 
219         tokens = Token.objects.filter(user=request.user)
220 
221         return render(
222             request,
223             "users/api_tokens.html",
224             {
225                 "tokens": tokens,
226                 "active_tab": "api_tokens",
227             },
228         )
229 
230 
231 class TokenEditView(LoginRequiredMixin, View):
232     def get(self, request, pk=None):
233 
234         if pk is not None:
235             if not request.user.has_perm("users.change_token"):
236                 return HttpResponseForbidden()
237             token = get_object_or_404(Token.objects.filter(user=request.user), pk=pk)
238         else:
239             if not request.user.has_perm("users.add_token"):
240                 return HttpResponseForbidden()
241             token = Token(user=request.user)
242 
243         form = TokenForm(instance=token)
244 
245         return render(
246             request,
247             "generic/object_edit.html",
248             {
249                 "obj": token,
250                 "obj_type": token._meta.verbose_name,
251                 "form": form,
252                 "return_url": reverse("user:token_list"),
253             },
254         )
255 
256     def post(self, request, pk=None):
257 
258         if pk is not None:
259             token = get_object_or_404(Token.objects.filter(user=request.user), pk=pk)
260             form = TokenForm(request.POST, instance=token)
261         else:
262             token = Token()
263             form = TokenForm(request.POST)
264 
265         if form.is_valid():
266             token = form.save(commit=False)
267             token.user = request.user
268             token.save()
269 
270             msg = "Modified token {}".format(token) if pk else "Created token {}".format(token)
271             messages.success(request, msg)
272 
273             if "_addanother" in request.POST:
274                 return redirect(request.path)
275             else:
276                 return redirect("user:token_list")
277 
278         return render(
279             request,
280             "generic/object_edit.html",
281             {
282                 "obj": token,
283                 "obj_type": token._meta.verbose_name,
284                 "form": form,
285                 "return_url": reverse("user:token_list"),
286             },
287         )
288 
289 
290 class TokenDeleteView(LoginRequiredMixin, View):
291     def get(self, request, pk):
292 
293         token = get_object_or_404(Token.objects.filter(user=request.user), pk=pk)
294         initial_data = {
295             "return_url": reverse("user:token_list"),
296         }
297         form = ConfirmationForm(initial=initial_data)
298 
299         return render(
300             request,
301             "generic/object_delete.html",
302             {
303                 "obj": token,
304                 "obj_type": token._meta.verbose_name,
305                 "form": form,
306                 "return_url": reverse("user:token_list"),
307             },
308         )
309 
310     def post(self, request, pk):
311 
312         token = get_object_or_404(Token.objects.filter(user=request.user), pk=pk)
313         form = ConfirmationForm(request.POST)
314         if form.is_valid():
315             token.delete()
316             messages.success(request, "Token deleted")
317             return redirect("user:token_list")
318 
319         return render(
320             request,
321             "generic/object_delete.html",
322             {
323                 "obj": token,
324                 "obj_type": token._meta.verbose_name,
325                 "form": form,
326                 "return_url": reverse("user:token_list"),
327             },
328         )
329
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/nautobot/users/views.py b/nautobot/users/views.py
--- a/nautobot/users/views.py
+++ b/nautobot/users/views.py
@@ -250,6 +250,7 @@
                 "obj_type": token._meta.verbose_name,
                 "form": form,
                 "return_url": reverse("user:token_list"),
+                "editing": token.present_in_database,
             },
         )
 
@@ -283,6 +284,7 @@
                 "obj_type": token._meta.verbose_name,
                 "form": form,
                 "return_url": reverse("user:token_list"),
+                "editing": token.present_in_database,
             },
         )

{"golden_diff": "diff --git a/nautobot/users/views.py b/nautobot/users/views.py\n--- a/nautobot/users/views.py\n+++ b/nautobot/users/views.py\n@@ -250,6 +250,7 @@\n                 \"obj_type\": token._meta.verbose_name,\n                 \"form\": form,\n                 \"return_url\": reverse(\"user:token_list\"),\n+                \"editing\": token.present_in_database,\n             },\n         )\n \n@@ -283,6 +284,7 @@\n                 \"obj_type\": token._meta.verbose_name,\n                 \"form\": form,\n                 \"return_url\": reverse(\"user:token_list\"),\n+                \"editing\": token.present_in_database,\n             },\n         )\n", "issue": "Editing an existing user token shows \"create\" buttons instead of \"update\"\n<!--\r\n    NOTE: IF YOUR ISSUE DOES NOT FOLLOW THIS TEMPLATE, IT WILL BE CLOSED.\r\n\r\n    This form is only for reporting reproducible bugs. If you need assistance\r\n    with Nautobot installation, or if you have a general question, please start a\r\n    discussion instead: https://github.com/nautobot/nautobot/discussions\r\n\r\n    Please describe the environment in which you are running Nautobot. Be sure\r\n    that you are running an unmodified instance of the latest stable release\r\n    before submitting a bug report, and that any plugins have been disabled.\r\n-->\r\n### Environment\r\n* Python version:\r\n* Nautobot version:\r\n\r\n<!--\r\n    Describe in detail the exact steps that someone else can take to reproduce\r\n    this bug using the current stable release of Nautobot. Begin with the\r\n    creation of any necessary database objects and call out every operation\r\n    being performed explicitly. If reporting a bug in the REST API, be sure to\r\n    reconstruct the raw HTTP request(s) being made: Don't rely on a client\r\n    library such as pynautobot.\r\n-->\r\n### Steps to Reproduce\r\n1. Navigate to user \"Profile\"\r\n2. Navigate to \"API Tokens\"\r\n3. Click \"Add token\"\r\n4. Click \"Create\"\r\n5. From the token list view, click \"Edit\" on the token you just created\r\n\r\n<!-- What did you expect to happen? -->\r\n### Expected Behavior\r\n\r\nThere should be an \"Update\" button.\r\n\r\n<!-- What happened instead? -->\r\n### Observed Behavior\r\n\r\nThere are \"Create\" and \"Create and Add Another\" buttons. \r\n\r\n![Cursor_and_Notification_Center](https://user-images.githubusercontent.com/138052/111223484-7176c900-859a-11eb-95c0-289b9361b01a.png)\r\n\r\n\n", "before_files": [{"content": "import logging\n\nfrom django.conf import settings\nfrom django.contrib import messages\nfrom django.contrib.auth import (\n    login as auth_login,\n    logout as auth_logout,\n    update_session_auth_hash,\n)\nfrom django.contrib.auth.mixins import LoginRequiredMixin\nfrom django.contrib.auth.models import update_last_login\nfrom django.contrib.auth.signals import user_logged_in\nfrom django.http import HttpResponseForbidden, HttpResponseRedirect\nfrom django.shortcuts import get_object_or_404, redirect, render\nfrom django.urls import reverse\nfrom django.utils.decorators import method_decorator\nfrom django.utils.http import is_safe_url\nfrom django.views.decorators.debug import sensitive_post_parameters\nfrom django.views.generic import View\n\nfrom nautobot.utilities.forms import ConfirmationForm\nfrom .forms import LoginForm, PasswordChangeForm, TokenForm\nfrom .models import Token\n\n\n#\n# Login/logout\n#\n\n\nclass LoginView(View):\n    \"\"\"\n    Perform user authentication via the web UI.\n    \"\"\"\n\n    template_name = \"login.html\"\n\n    @method_decorator(sensitive_post_parameters(\"password\"))\n    def dispatch(self, *args, **kwargs):\n        return super().dispatch(*args, **kwargs)\n\n    def get(self, request):\n        form = LoginForm(request)\n\n        if request.user.is_authenticated:\n            logger = logging.getLogger(\"nautobot.auth.login\")\n            return self.redirect_to_next(request, logger)\n\n        return render(\n            request,\n            self.template_name,\n            {\n                \"form\": form,\n            },\n        )\n\n    def post(self, request):\n        logger = logging.getLogger(\"nautobot.auth.login\")\n        form = LoginForm(request, data=request.POST)\n\n        if form.is_valid():\n            logger.debug(\"Login form validation was successful\")\n\n            # If maintenance mode is enabled, assume the database is read-only, and disable updating the user's\n            # last_login time upon authentication.\n            if settings.MAINTENANCE_MODE:\n                logger.warning(\"Maintenance mode enabled: disabling update of most recent login time\")\n                user_logged_in.disconnect(update_last_login, dispatch_uid=\"update_last_login\")\n\n            # Authenticate user\n            auth_login(request, form.get_user())\n            logger.info(f\"User {request.user} successfully authenticated\")\n            messages.info(request, \"Logged in as {}.\".format(request.user))\n\n            return self.redirect_to_next(request, logger)\n\n        else:\n            logger.debug(\"Login form validation failed\")\n\n        return render(\n            request,\n            self.template_name,\n            {\n                \"form\": form,\n            },\n        )\n\n    def redirect_to_next(self, request, logger):\n        if request.method == \"POST\":\n            redirect_to = request.POST.get(\"next\", reverse(\"home\"))\n        else:\n            redirect_to = request.GET.get(\"next\", reverse(\"home\"))\n\n        if redirect_to and not is_safe_url(url=redirect_to, allowed_hosts=request.get_host()):\n            logger.warning(f\"Ignoring unsafe 'next' URL passed to login form: {redirect_to}\")\n            redirect_to = reverse(\"home\")\n\n        logger.debug(f\"Redirecting user to {redirect_to}\")\n        return HttpResponseRedirect(redirect_to)\n\n\nclass LogoutView(View):\n    \"\"\"\n    Deauthenticate a web user.\n    \"\"\"\n\n    def get(self, request):\n        logger = logging.getLogger(\"nautobot.auth.logout\")\n\n        # Log out the user\n        username = request.user\n        auth_logout(request)\n        logger.info(f\"User {username} has logged out\")\n        messages.info(request, \"You have logged out.\")\n\n        # Delete session key cookie (if set) upon logout\n        response = HttpResponseRedirect(reverse(\"home\"))\n        response.delete_cookie(\"session_key\")\n\n        return response\n\n\n#\n# User profiles\n#\n\n\nclass ProfileView(LoginRequiredMixin, View):\n    template_name = \"users/profile.html\"\n\n    def get(self, request):\n\n        return render(\n            request,\n            self.template_name,\n            {\n                \"active_tab\": \"profile\",\n            },\n        )\n\n\nclass UserConfigView(LoginRequiredMixin, View):\n    template_name = \"users/preferences.html\"\n\n    def get(self, request):\n\n        return render(\n            request,\n            self.template_name,\n            {\n                \"preferences\": request.user.all_config(),\n                \"active_tab\": \"preferences\",\n            },\n        )\n\n    def post(self, request):\n        user = request.user\n        data = user.all_config()\n\n        # Delete selected preferences\n        for key in request.POST.getlist(\"pk\"):\n            if key in data:\n                user.clear_config(key)\n        user.save()\n        messages.success(request, \"Your preferences have been updated.\")\n\n        return redirect(\"user:preferences\")\n\n\nclass ChangePasswordView(LoginRequiredMixin, View):\n    template_name = \"users/change_password.html\"\n\n    def get(self, request):\n        # LDAP users cannot change their password here\n        if getattr(request.user, \"ldap_username\", None):\n            messages.warning(\n                request,\n                \"LDAP-authenticated user credentials cannot be changed within Nautobot.\",\n            )\n            return redirect(\"user:profile\")\n\n        form = PasswordChangeForm(user=request.user)\n\n        return render(\n            request,\n            self.template_name,\n            {\n                \"form\": form,\n                \"active_tab\": \"change_password\",\n            },\n        )\n\n    def post(self, request):\n        form = PasswordChangeForm(user=request.user, data=request.POST)\n        if form.is_valid():\n            form.save()\n            update_session_auth_hash(request, form.user)\n            messages.success(request, \"Your password has been changed successfully.\")\n            return redirect(\"user:profile\")\n\n        return render(\n            request,\n            self.template_name,\n            {\n                \"form\": form,\n                \"active_tab\": \"change_password\",\n            },\n        )\n\n\n#\n# API tokens\n#\n\n\nclass TokenListView(LoginRequiredMixin, View):\n    def get(self, request):\n\n        tokens = Token.objects.filter(user=request.user)\n\n        return render(\n            request,\n            \"users/api_tokens.html\",\n            {\n                \"tokens\": tokens,\n                \"active_tab\": \"api_tokens\",\n            },\n        )\n\n\nclass TokenEditView(LoginRequiredMixin, View):\n    def get(self, request, pk=None):\n\n        if pk is not None:\n            if not request.user.has_perm(\"users.change_token\"):\n                return HttpResponseForbidden()\n            token = get_object_or_404(Token.objects.filter(user=request.user), pk=pk)\n        else:\n            if not request.user.has_perm(\"users.add_token\"):\n                return HttpResponseForbidden()\n            token = Token(user=request.user)\n\n        form = TokenForm(instance=token)\n\n        return render(\n            request,\n            \"generic/object_edit.html\",\n            {\n                \"obj\": token,\n                \"obj_type\": token._meta.verbose_name,\n                \"form\": form,\n                \"return_url\": reverse(\"user:token_list\"),\n            },\n        )\n\n    def post(self, request, pk=None):\n\n        if pk is not None:\n            token = get_object_or_404(Token.objects.filter(user=request.user), pk=pk)\n            form = TokenForm(request.POST, instance=token)\n        else:\n            token = Token()\n            form = TokenForm(request.POST)\n\n        if form.is_valid():\n            token = form.save(commit=False)\n            token.user = request.user\n            token.save()\n\n            msg = \"Modified token {}\".format(token) if pk else \"Created token {}\".format(token)\n            messages.success(request, msg)\n\n            if \"_addanother\" in request.POST:\n                return redirect(request.path)\n            else:\n                return redirect(\"user:token_list\")\n\n        return render(\n            request,\n            \"generic/object_edit.html\",\n            {\n                \"obj\": token,\n                \"obj_type\": token._meta.verbose_name,\n                \"form\": form,\n                \"return_url\": reverse(\"user:token_list\"),\n            },\n        )\n\n\nclass TokenDeleteView(LoginRequiredMixin, View):\n    def get(self, request, pk):\n\n        token = get_object_or_404(Token.objects.filter(user=request.user), pk=pk)\n        initial_data = {\n            \"return_url\": reverse(\"user:token_list\"),\n        }\n        form = ConfirmationForm(initial=initial_data)\n\n        return render(\n            request,\n            \"generic/object_delete.html\",\n            {\n                \"obj\": token,\n                \"obj_type\": token._meta.verbose_name,\n                \"form\": form,\n                \"return_url\": reverse(\"user:token_list\"),\n            },\n        )\n\n    def post(self, request, pk):\n\n        token = get_object_or_404(Token.objects.filter(user=request.user), pk=pk)\n        form = ConfirmationForm(request.POST)\n        if form.is_valid():\n            token.delete()\n            messages.success(request, \"Token deleted\")\n            return redirect(\"user:token_list\")\n\n        return render(\n            request,\n            \"generic/object_delete.html\",\n            {\n                \"obj\": token,\n                \"obj_type\": token._meta.verbose_name,\n                \"form\": form,\n                \"return_url\": reverse(\"user:token_list\"),\n            },\n        )\n", "path": "nautobot/users/views.py"}], "after_files": [{"content": "import logging\n\nfrom django.conf import settings\nfrom django.contrib import messages\nfrom django.contrib.auth import (\n    login as auth_login,\n    logout as auth_logout,\n    update_session_auth_hash,\n)\nfrom django.contrib.auth.mixins import LoginRequiredMixin\nfrom django.contrib.auth.models import update_last_login\nfrom django.contrib.auth.signals import user_logged_in\nfrom django.http import HttpResponseForbidden, HttpResponseRedirect\nfrom django.shortcuts import get_object_or_404, redirect, render\nfrom django.urls import reverse\nfrom django.utils.decorators import method_decorator\nfrom django.utils.http import is_safe_url\nfrom django.views.decorators.debug import sensitive_post_parameters\nfrom django.views.generic import View\n\nfrom nautobot.utilities.forms import ConfirmationForm\nfrom .forms import LoginForm, PasswordChangeForm, TokenForm\nfrom .models import Token\n\n\n#\n# Login/logout\n#\n\n\nclass LoginView(View):\n    \"\"\"\n    Perform user authentication via the web UI.\n    \"\"\"\n\n    template_name = \"login.html\"\n\n    @method_decorator(sensitive_post_parameters(\"password\"))\n    def dispatch(self, *args, **kwargs):\n        return super().dispatch(*args, **kwargs)\n\n    def get(self, request):\n        form = LoginForm(request)\n\n        if request.user.is_authenticated:\n            logger = logging.getLogger(\"nautobot.auth.login\")\n            return self.redirect_to_next(request, logger)\n\n        return render(\n            request,\n            self.template_name,\n            {\n                \"form\": form,\n            },\n        )\n\n    def post(self, request):\n        logger = logging.getLogger(\"nautobot.auth.login\")\n        form = LoginForm(request, data=request.POST)\n\n        if form.is_valid():\n            logger.debug(\"Login form validation was successful\")\n\n            # If maintenance mode is enabled, assume the database is read-only, and disable updating the user's\n            # last_login time upon authentication.\n            if settings.MAINTENANCE_MODE:\n                logger.warning(\"Maintenance mode enabled: disabling update of most recent login time\")\n                user_logged_in.disconnect(update_last_login, dispatch_uid=\"update_last_login\")\n\n            # Authenticate user\n            auth_login(request, form.get_user())\n            logger.info(f\"User {request.user} successfully authenticated\")\n            messages.info(request, \"Logged in as {}.\".format(request.user))\n\n            return self.redirect_to_next(request, logger)\n\n        else:\n            logger.debug(\"Login form validation failed\")\n\n        return render(\n            request,\n            self.template_name,\n            {\n                \"form\": form,\n            },\n        )\n\n    def redirect_to_next(self, request, logger):\n        if request.method == \"POST\":\n            redirect_to = request.POST.get(\"next\", reverse(\"home\"))\n        else:\n            redirect_to = request.GET.get(\"next\", reverse(\"home\"))\n\n        if redirect_to and not is_safe_url(url=redirect_to, allowed_hosts=request.get_host()):\n            logger.warning(f\"Ignoring unsafe 'next' URL passed to login form: {redirect_to}\")\n            redirect_to = reverse(\"home\")\n\n        logger.debug(f\"Redirecting user to {redirect_to}\")\n        return HttpResponseRedirect(redirect_to)\n\n\nclass LogoutView(View):\n    \"\"\"\n    Deauthenticate a web user.\n    \"\"\"\n\n    def get(self, request):\n        logger = logging.getLogger(\"nautobot.auth.logout\")\n\n        # Log out the user\n        username = request.user\n        auth_logout(request)\n        logger.info(f\"User {username} has logged out\")\n        messages.info(request, \"You have logged out.\")\n\n        # Delete session key cookie (if set) upon logout\n        response = HttpResponseRedirect(reverse(\"home\"))\n        response.delete_cookie(\"session_key\")\n\n        return response\n\n\n#\n# User profiles\n#\n\n\nclass ProfileView(LoginRequiredMixin, View):\n    template_name = \"users/profile.html\"\n\n    def get(self, request):\n\n        return render(\n            request,\n            self.template_name,\n            {\n                \"active_tab\": \"profile\",\n            },\n        )\n\n\nclass UserConfigView(LoginRequiredMixin, View):\n    template_name = \"users/preferences.html\"\n\n    def get(self, request):\n\n        return render(\n            request,\n            self.template_name,\n            {\n                \"preferences\": request.user.all_config(),\n                \"active_tab\": \"preferences\",\n            },\n        )\n\n    def post(self, request):\n        user = request.user\n        data = user.all_config()\n\n        # Delete selected preferences\n        for key in request.POST.getlist(\"pk\"):\n            if key in data:\n                user.clear_config(key)\n        user.save()\n        messages.success(request, \"Your preferences have been updated.\")\n\n        return redirect(\"user:preferences\")\n\n\nclass ChangePasswordView(LoginRequiredMixin, View):\n    template_name = \"users/change_password.html\"\n\n    def get(self, request):\n        # LDAP users cannot change their password here\n        if getattr(request.user, \"ldap_username\", None):\n            messages.warning(\n                request,\n                \"LDAP-authenticated user credentials cannot be changed within Nautobot.\",\n            )\n            return redirect(\"user:profile\")\n\n        form = PasswordChangeForm(user=request.user)\n\n        return render(\n            request,\n            self.template_name,\n            {\n                \"form\": form,\n                \"active_tab\": \"change_password\",\n            },\n        )\n\n    def post(self, request):\n        form = PasswordChangeForm(user=request.user, data=request.POST)\n        if form.is_valid():\n            form.save()\n            update_session_auth_hash(request, form.user)\n            messages.success(request, \"Your password has been changed successfully.\")\n            return redirect(\"user:profile\")\n\n        return render(\n            request,\n            self.template_name,\n            {\n                \"form\": form,\n                \"active_tab\": \"change_password\",\n            },\n        )\n\n\n#\n# API tokens\n#\n\n\nclass TokenListView(LoginRequiredMixin, View):\n    def get(self, request):\n\n        tokens = Token.objects.filter(user=request.user)\n\n        return render(\n            request,\n            \"users/api_tokens.html\",\n            {\n                \"tokens\": tokens,\n                \"active_tab\": \"api_tokens\",\n            },\n        )\n\n\nclass TokenEditView(LoginRequiredMixin, View):\n    def get(self, request, pk=None):\n\n        if pk is not None:\n            if not request.user.has_perm(\"users.change_token\"):\n                return HttpResponseForbidden()\n            token = get_object_or_404(Token.objects.filter(user=request.user), pk=pk)\n        else:\n            if not request.user.has_perm(\"users.add_token\"):\n                return HttpResponseForbidden()\n            token = Token(user=request.user)\n\n        form = TokenForm(instance=token)\n\n        return render(\n            request,\n            \"generic/object_edit.html\",\n            {\n                \"obj\": token,\n                \"obj_type\": token._meta.verbose_name,\n                \"form\": form,\n                \"return_url\": reverse(\"user:token_list\"),\n                \"editing\": token.present_in_database,\n            },\n        )\n\n    def post(self, request, pk=None):\n\n        if pk is not None:\n            token = get_object_or_404(Token.objects.filter(user=request.user), pk=pk)\n            form = TokenForm(request.POST, instance=token)\n        else:\n            token = Token()\n            form = TokenForm(request.POST)\n\n        if form.is_valid():\n            token = form.save(commit=False)\n            token.user = request.user\n            token.save()\n\n            msg = \"Modified token {}\".format(token) if pk else \"Created token {}\".format(token)\n            messages.success(request, msg)\n\n            if \"_addanother\" in request.POST:\n                return redirect(request.path)\n            else:\n                return redirect(\"user:token_list\")\n\n        return render(\n            request,\n            \"generic/object_edit.html\",\n            {\n                \"obj\": token,\n                \"obj_type\": token._meta.verbose_name,\n                \"form\": form,\n                \"return_url\": reverse(\"user:token_list\"),\n                \"editing\": token.present_in_database,\n            },\n        )\n\n\nclass TokenDeleteView(LoginRequiredMixin, View):\n    def get(self, request, pk):\n\n        token = get_object_or_404(Token.objects.filter(user=request.user), pk=pk)\n        initial_data = {\n            \"return_url\": reverse(\"user:token_list\"),\n        }\n        form = ConfirmationForm(initial=initial_data)\n\n        return render(\n            request,\n            \"generic/object_delete.html\",\n            {\n                \"obj\": token,\n                \"obj_type\": token._meta.verbose_name,\n                \"form\": form,\n                \"return_url\": reverse(\"user:token_list\"),\n            },\n        )\n\n    def post(self, request, pk):\n\n        token = get_object_or_404(Token.objects.filter(user=request.user), pk=pk)\n        form = ConfirmationForm(request.POST)\n        if form.is_valid():\n            token.delete()\n            messages.success(request, \"Token deleted\")\n            return redirect(\"user:token_list\")\n\n        return render(\n            request,\n            \"generic/object_delete.html\",\n            {\n                \"obj\": token,\n                \"obj_type\": token._meta.verbose_name,\n                \"form\": form,\n                \"return_url\": reverse(\"user:token_list\"),\n            },\n        )\n", "path": "nautobot/users/views.py"}]}