Breakout (most) user stuff to separate module

The model remains in RIGS for now, as it's pretty painful to move...
This commit is contained in:
2020-04-12 19:13:06 +01:00
parent 2bf643cd7a
commit f308a095f3
32 changed files with 314 additions and 250 deletions

57
users/forms.py Normal file
View File

@@ -0,0 +1,57 @@
import simplejson
from captcha.fields import ReCaptchaField
from django import forms
from django.conf import settings
from django.contrib.auth.forms import (AuthenticationForm, PasswordResetForm,
UserChangeForm, UserCreationForm)
from django.core import serializers
from django.core.mail import EmailMessage, EmailMultiAlternatives
from django.utils import formats
from registration.forms import RegistrationFormUniqueEmail
from RIGS import models
# Registration
class ProfileRegistrationFormUniqueEmail(RegistrationFormUniqueEmail):
captcha = ReCaptchaField()
class Meta:
model = models.Profile
fields = ('username', 'email', 'first_name', 'last_name', 'initials')
def clean_initials(self):
"""
Validate that the supplied initials are unique.
"""
if models.Profile.objects.filter(initials__iexact=self.cleaned_data['initials']):
raise forms.ValidationError("These initials are already in use. Please supply different initials.")
return self.cleaned_data['initials']
class CheckApprovedForm(AuthenticationForm):
def confirm_login_allowed(self, user):
if user.is_approved or user.is_superuser:
return AuthenticationForm.confirm_login_allowed(self, user)
else:
raise forms.ValidationError("Your account hasn't been approved by an administrator yet. Please check back in a few minutes!")
# Embedded Login form - remove the autofocus
class EmbeddedAuthenticationForm(CheckApprovedForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['username'].widget.attrs.pop('autofocus', None)
class PasswordReset(PasswordResetForm):
captcha = ReCaptchaField(label='Captcha')
class ProfileCreationForm(UserCreationForm):
class Meta(UserCreationForm.Meta):
model = models.Profile
class ProfileChangeForm(UserChangeForm):
class Meta(UserChangeForm.Meta):
model = models.Profile

15
users/regbackend.py Normal file
View File

@@ -0,0 +1,15 @@
from RIGS.models import Profile
from users.forms import ProfileRegistrationFormUniqueEmail
from registration.signals import user_registered
def user_created(sender, user, request, **kwargs):
form = ProfileRegistrationFormUniqueEmail(request.POST)
user.first_name = form.data['first_name']
user.last_name = form.data['last_name']
user.initials = form.data['initials']
# user.phone = form.data['phone']
user.save()
user_registered.connect(user_created)

View File

@@ -0,0 +1,12 @@
{# pass in variable "profile" to this template #}
<button title="{{profile.name}}" type="button" class="btn btn-default btn-xs" data-container="body" data-html="true" data-trigger='hover focus' data-toggle="popover" data-content='
<img src="{{profile.profile_picture}}" class="img-responsive img-rounded center-block" style="max-width:4em" />
<dl class="dl-vertical">
<dt>Email</dt>
<dd>{{profile.email}}</dd>
<dt>Phone</dt>
<dd>{{profile.phone}}</dd>
</dl>
'>{{profile.first_name}}</button>

View File

@@ -0,0 +1,150 @@
{% extends request.is_ajax|yesno:"base_ajax.html,base_rigs.html" %}
{% block title %}RIGS Profile {{object.pk}}{% endblock %}
{% block js %}
<script>
$(document).ready(function() {
$('#urlParamForm').change(function(){
url = "?";
$('#urlParamForm *').filter(':input').each(function(index, value){
defaultVal = $(value).data('default');
param = $(value).val();
val = $(value).prop('checked');
if(val != defaultVal){
url = url+param+"="+val+"&";
}
});
ics_url = $('#cal-url').data('url') + url.substring(0, url.length - 1);
$('#cal-url').text(ics_url);
gcal_url = $('#gcal-link').data('url') + encodeURIComponent(url.substring(0, url.length - 1));
$('#gcal-link').attr('href',gcal_url);
});
$('#urlParamForm').change(); //Do the initial setting
});
</script>
{% endblock %}
{% block content %}
<h3>Profile: {{object.name}}</h3>
{% if not request.is_ajax %}
<div class="row py-3">
{% if object.pk == user.pk %}
<div>
<div class="btn-group">
<a href="{% url 'profile_update_self' %}" class="btn btn-light">
Edit Profile <i class="fas fa-edit"></i>
</a>
<a href="{% url 'password_change' %}" class="btn btn-light">
Change Password <span class="fas fa-lock"></span>
</a>
</div>
</div>
{% endif %}
</div>
{% endif %}
<div class="row">
<div class="col">
<div class="card">
<div class="row no-gutters">
<div class="col-md-3">
<img src="{{object.profile_picture}}" class="card-img img-fluid" />
</div>
<div class="col-md-9">
<div class="card-body">
<dl class="row">
<dt class="col-5">First Name</dt>
<dd class="col-7">{{object.first_name}}</dd>
<dt class="col-5">Last Name</dt>
<dd class="col-7">{{object.last_name}}</dd>
<dt class="col-5">Email</dt>
<dd class="col-7">{{object.email}}</dd>
<dt class="col-5">Last Login</dt>
<dd class="col-7">{{object.last_login|date:"d/m/Y H:i"}}</dd>
<dt class="col-5">Date Joined</dt>
<dd class="col-7">{{object.date_joined|date:"d/m/Y H:i"}}</dd>
<dt class="col-5">Initials</dt>
<dd class="col-7">{{object.initials}}</dd>
<dt class="col-5">Phone</dt>
<dd class="col-7">{% if object.phone %}}<a href="tel:{{ object.phone }}">{% endif %}{{object.phone}}{% if object.phone %}}</a>{% endif %}</dd>
</dl>
</div>
</div>
</div>
</div>
</div>
{% if not request.is_ajax and object.pk == user.pk %}
<div class="col">
<div class="card">
<div class="card-body">
<h4>Personal iCal Details</h4>
<dl class="row">
<dt class="col-4">API Key</dt>
<dd class="col-5">
{% if user.api_key %}
{{user.api_key}}
{% else %}
No API Key Generated
{% endif %}
</dd>
<a href="{% url 'reset_api_key' %}" class="btn btn-secondary col-3">
{% if user.api_key %}Reset API Key{% else %}Generate API Key{% endif %}
<span class="fas fa-redo"></span>
</a>
<dt class="col-4">Calendar Options</dt>
<dd class="col-8">
<form class="form-inline" id="urlParamForm">
<div class="form-group">
<label class="checkbox-inline mr-3">
<input type="checkbox" value="rig" data-default="true" checked> Rigs
</label>
<label class="checkbox-inline mx-3">
<input type="checkbox" value="non-rig" data-default="true" checked> Non-Rigs
</label>
<label class="checkbox-inline mx-3">
<input type="checkbox" value="dry-hire" data-default="true" checked> Dry-Hires
</label>
<label class="checkbox-inline mx-3">
<input type="checkbox" value="cancelled" data-default="false" > Cancelled
</label>
<label class="checkbox-inline mx-3">
<input type="checkbox" value="provisional" data-default="true" checked> Provisional
</label>
<label class="checkbox-inline mx-3">
<input type="checkbox" value="confirmed" data-default="true" checked> Confirmed/Booked
</label>
</div>
</form>
</dd>
<dt class="col-4">Calendar URL</dt>
<dd class="col-8">
{% if user.api_key %}
<pre id="cal-url" data-url="http{{ request.is_secure|yesno:"s,"}}://{{ request.get_host }}{% url 'ics_calendar' api_pk=user.pk api_key=user.api_key %}"></pre>
<small><a id="gcal-link" data-url="https://support.google.com/calendar/answer/37100" href="">Click here</a> for instructions on adding to google calendar.<br/>
To sync from google calendar to mobile device, visit <a href="https://www.google.com/calendar/syncselect" target="_blank">this page</a> on your device and tick "RIGS Calendar".</small>
{% else %}
<pre>No API Key Generated</pre>
{% endif %}
</dd>
</dl>
</div>
</div>
</div>
{% endif %}
</div>
<h4>Events</h4>
{% with object.latest_events as events %}
{% include 'event_table.html' %}
{% endwith %}
{% endblock %}

View File

@@ -0,0 +1,71 @@
{% extends 'base_rigs.html' %}
{% load widget_tweaks %}
{% block title %}Update Profile {{object.name}}{% endblock %}
{% block content %}
<div class="row">
<div class="col-sm-10 col-sm-offset-1">
{% include 'form_errors.html' %}
<h3>Update Profile</h3>
<div class="col-sm-6">
<form action="{{form.action|default:request.path}}" method="post" class="form-horizontal">{% csrf_token %}
<div class="form-group">
<label for="{{form.first_name.id_for_label}}" class="col-sm-4 control-label">{{form.first_name.label}}</label>
<div class="col-sm-8">
{% render_field form.first_name class+="form-control" placeholder=form.first_name.label %}
</div>
</div>
<div class="form-group">
<label for="{{form.last_name.id_for_label}}" class="col-sm-4 control-label">{{form.last_name.label}}</label>
<div class="col-sm-8">
{% render_field form.last_name class+="form-control" placeholder=form.last_name.label %}
</div>
</div>
<div class="form-group">
<label for="{{form.email.id_for_label}}" class="col-sm-4 control-label">{{form.email.label}}</label>
<div class="col-sm-8">
{% render_field form.email type="email" class+="form-control" placeholder=form.email.label %}
</div>
</div>
<div class="form-group">
<label for="{{form.initials.id_for_label}}" class="col-sm-4 control-label">{{form.initials.label}}</label>
<div class="col-sm-8">
{% render_field form.initials class+="form-control" placeholder=form.initials.label %}
</div>
</div>
<div class="form-group">
<label for="{{form.phone.id_for_label}}" class="col-sm-4 control-label">{{form.phone.label}}</label>
<div class="col-sm-8">
{% render_field form.phone type="tel" class+="form-control" placeholder=form.phone.label %}
</div>
</div>
<div class="form-group">
<input class="btn btn-primary pull-right" type="submit"/>
</div>
</form>
</div>
<div class="col-sm-3 col-sm-offset-2">
<div class="center-block">
<a href="https://gravatar.com/">
<img src="{{object.profile_picture}}" class="img-responsive img-rounded" />
<div class="text-center">
Images hosted by Gravatar
</div>
</a>
</div>
</div>
</div>
</div>
{% endblock %}

View File

@@ -0,0 +1,10 @@
{% extends 'base_rigs.html' %}
{% block title %}Activation Complete{% endblock %}
{% block content %}
<div class="alert alert-success">
<h2>Activation Complete</h2>
<p>Your user account is now awaiting administrator approval. Won't be long!</p>
</div>
{% endblock %}

View File

@@ -0,0 +1,7 @@
Welcome {{ user }},
Thank you for registering on {{ site }}
To continue the registration process please visit http://{{ site.domain }}{% url 'registration_activate' activation_key=activation_key %}.
This link will be active for the next {{ expiration_days }} days.

View File

@@ -0,0 +1 @@
{{ user|safe }} activation required

View File

@@ -0,0 +1,10 @@
{% extends 'base_rigs.html' %}
{% block title %}Logout Successful{% endblock %}
{% block content %}
<div class="alert alert-success">
<h2>Logout Successful</h2>
<p>You have successfully been logged out of RIGS</p>
</div>
{% endblock %}

View File

@@ -0,0 +1,10 @@
{% extends 'base_rigs.html' %}
{% block title %}Login{% endblock %}
{% block content %}
<div class="text-center">
<h1>R<small>ig</small> I<small>nformation</small> G<small>athering</small> S<small>ystem</small></h1>
</div>
{% include 'registration/loginform.html' %}
{% endblock %}

View File

@@ -0,0 +1,34 @@
{% extends 'base_embed.html' %}
{% load widget_tweaks %}
{% block title %}Login{% endblock %}
{% block content %}
<div class="text-center">
<h1>R<small>ig</small> I<small>nformation</small> G<small>athering</small> S<small>ystem</small></h1>
</div>
{% include 'form_errors.html' %}
<div class="col-sm-6 col-sm-offset-3 col-lg-4 col-lg-offset-4">
<form id="loginForm" action="" method="post" role="form" target="_self">{% csrf_token %}
<div class="form-group">
<label for="id_username">{{ form.username.label }}</label>
{% render_field form.username class+="form-control" placeholder=form.username.label %}
</div>
<div class="form-group">
<label for="{{ form.password.id_for_label }}">{{ form.password.label }}</label>
{% render_field form.password class+="form-control" placeholder=form.password.label %}
</div>
<div class="text-right">
<input type="submit" value="Login" class="btn btn-primary"/>
</div>
<input type="hidden" name="next" value="{{ next }}"/>
</form>
</div>
{% endblock %}

View File

@@ -0,0 +1,20 @@
{% load widget_tweaks %}
{% include 'form_errors.html' %}
<div class="col-sm-6 offset-sm-3 col-lg-4 offset-lg-4">
<form action="{% url 'login' %}" method="post" role="form" target="_self">{% csrf_token %}
<div class="form-group">
<label for="id_username">{{ form.username.label }}</label>
{% render_field form.username class+="form-control" placeholder=form.username.label autofocus="" %}
</div>
<div class="form-group">
<label for="{{ form.password.id_for_label }}">{{ form.password.label }}</label>
{% render_field form.password class+="form-control" placeholder=form.password.label %}
</div>
<div class="text-right">
<a href="{% url 'registration_register' %}" class="btn">Register</a>
<a href="{% url 'password_reset' %}" class="btn">Forgotten Password</a>
<input type="submit" id="id_submit" value="Login" class="btn btn-primary"/>
<input type="hidden" name="next" value="{{ next }}"/>
</div>
</form>
</div>

View File

@@ -0,0 +1,20 @@
{% extends "base_rigs.html" %}
{% load i18n %}
{% block breadcrumbs %}
<div class="breadcrumbs">
<a href="{% url 'admin:index' %}">{% trans 'Home' %}</a>
&rsaquo; {% trans 'Password reset' %}
</div>
{% endblock %}
{% block title %}{% trans 'Password change successful' %}{% endblock %}
{% block content %}
<div class="col-sm-12">
<h1>{% trans 'Password change successful' %}</h1>
<p>{% trans "Your password has been changed" %}</p>
</div>
{% endblock %}

View File

@@ -0,0 +1,33 @@
{% extends "base_rigs.html" %}
{% load widget_tweaks %}
{% block title %}Change Password{% endblock %}
{% block content %}
<div class="col-sm-10 col-sm-offset-1">
<h3>Change Password</h3>
{% if form.errors or supplement_form.errors %}
<div class="alert alert-danger">
Please correct the error(s) below.
{{form.errors}}
{{supplement_form.errors}}
</div>
{% endif %}
<p>Please enter your old password, for security's sake, and then enter your new password twice so we can verify you typed it in correctly.</p>
<div class="col-sm-8 col-sm-offset-2">
<form action="" method="post" class="form-horizontal" role="form">{% csrf_token %}
{% for field in form %}
<div class="form-group">
<label for="{{ field.id_for_label }}" class="control-label col-sm-4">{{ field.label }}</label>
<div class="controls col-sm-8">
{% render_field field class+="form-control" placeholder=field.label %}
</div>
</div>
{% endfor %}
<p><input type="submit" value="Change Password" class="btn btn-primary pull-right"></p>
</form>
</div>
{% endblock %}

View File

@@ -0,0 +1,23 @@
{% extends "base_rigs.html" %}
{% load i18n %}
{% block breadcrumbs %}
<div class="breadcrumbs">
<a href="{% url 'admin:index' %}">{% trans 'Home' %}</a>
&rsaquo; {% trans 'Password reset' %}
</div>
{% endblock %}
{% block title %}{% trans 'Password reset complete' %}{% endblock %}
{% block content %}
<div class="col-sm-12">
<h1>{% trans 'Password reset complete' %}</h1>
<p>{% trans "Your password has been set. You may go ahead and log in now." %}</p>
<p><a href="{{ login_url }}">{% trans 'Log in' %}</a></p>
</div>
{% endblock %}

View File

@@ -0,0 +1,61 @@
{% extends "base_rigs.html" %}
{% load i18n %}
{% load widget_tweaks %}
{% block breadcrumbs %}
<div class="breadcrumbs">
<a href="{% url 'admin:index' %}">{% trans 'Home' %}</a>
&rsaquo; {% trans 'Password reset confirmation' %}
</div>
{% endblock %}
{% block title %}{% trans 'Password reset' %}{% endblock %}
{% block content %}
{% if validlink %}
<div class="col-sm-12">
<h1>{% trans 'Enter new password' %}</h1>
<p>{% trans "Please enter your new password twice so we can verify you typed it in correctly." %}</p>
<div class="col-sm-8 col-sm-offset-2 well">
<form action="" method="post" role="form" class="form-horizontal">{% csrf_token %}
{% if form.errors %}
{% include 'form_errors.html' %}
{% endif %}
<div class="form-group">
<label for="{{form.new_password1.id_for_label}}" class="col-sm-4 control-label">{{form.new_password1.label}}</label>
<div class="col-sm-8">
{% render_field form.new_password1 class+="form-control" %}
</div>
</div>
<div class="form-group">
<label for="{{form.new_password2.id_for_label}}" class="col-sm-4 control-label">{{form.new_password2.label}}</label>
<div class="col-sm-8">
{% render_field form.new_password2 class+="form-control" %}
</div>
</div>
<div class="col-sm-12">
<div class="pull-right">
<div class="form-group">
<input type="submit" value="{% trans 'Change my password' %}" class="btn btn-primary" />
</div>
</div>
</div>
</form>
</div>
{% else %}
<h1>{% trans 'Password reset unsuccessful' %}</h1>
<p>{% trans "The password reset link was invalid, possibly because it has already been used. Please request a new password reset." %}</p>
</div>
{% endif %}
{% endblock %}

View File

@@ -0,0 +1,20 @@
{% extends "base_rigs.html" %}
{% load i18n %}
{% block breadcrumbs %}
<div class="breadcrumbs">
<a href="{% url 'admin:index' %}">{% trans 'Home' %}</a>
&rsaquo; {% trans 'Password reset' %}
</div>
{% endblock %}
{% block title %}{% trans 'Password reset successful' %}{% endblock %}
{% block content %}
<div class="col-sm-12">
<h1>{% trans 'Password reset successful' %}</h1>
<p>{% trans "We've e-mailed you instructions for setting your password to the e-mail address you submitted. You should be receiving it shortly." %}</p>
</div>
{% endblock %}

View File

@@ -0,0 +1,14 @@
{% load i18n %}{% autoescape off %}
{% blocktrans %}You're receiving this e-mail because you requested a password reset for your user account at {{ site_name }}.{% endblocktrans %}
{% trans "Please go to the following page and choose a new password:" %}
{% block reset_link %}
{{ protocol }}://{{ domain }}{% url 'auth_password_reset_confirm' uidb64=uid token=token %}
{% endblock %}
{% trans "Your username, in case you've forgotten:" %} {{ user.username }}
{% trans "Thanks for using our site!" %}
{% blocktrans %}The {{ site_name }} team{% endblocktrans %}
{% endautoescape %}

View File

@@ -0,0 +1,35 @@
{% extends 'base_rigs.html' %}
{% load i18n %}
{% load widget_tweaks %}
{% block title %}Password reset{% endblock %}
{% block content %}
<div class="col-sm-12">
<h1>Password Reset</h1>
<p>{% trans "Forgotten your password? Enter your e-mail address below, and we'll e-mail instructions for setting a new one." %}</p>
<div class="col-sm-8 col-sm-offset-2 well">
<form action="" method="POST" role="form" class="form-horizontal">{% csrf_token %}
{% if form.errors %}
{% include 'form_errors.html' %}
{% endif %}
<div class="form-group">
<label for="{{form.email.id_for_label}}" class="col-sm-2 control-label">{{form.email.label}}</label>
<div class="col-sm-10">
{% render_field form.email type="email" class+="form-control" %}
</div>
</div>
<div class="form-group">
<div class="col-sm-10 col-md-8 col-md-offset-2">
{{ form.captcha }}
</div>
<div class="col-sm-2 text-right">
<input type="submit" value="Submit" class="btn btn-primary" />
</div>
</div>
</form>
</div>
</div>
{% endblock %}

View File

@@ -0,0 +1,10 @@
{% extends 'base_rigs.html' %}
{% block title %}Registration complete{% endblock %}
{% block content %}
<div class="alert alert-success">
<h2>Thanks for registering</h2>
<p>Thanks for registering with RIGS, further details will be emailed to you.</p>
</div>
{% endblock %}

View File

@@ -0,0 +1,29 @@
{% extends 'base_rigs.html' %}
{% load widget_tweaks %}
{% block title %}Registration{% endblock %}
{% block content %}
<div class="col-sm-10 col-sm-offset-1">
<h3>New User Registration</h3>
{% if form.errors or supplement_form.errors %}
<div class="alert alert-danger">
{{form.errors}}
{{supplement_form.errors}}
</div>
{% endif %}
<div class="col-sm-8 col-sm-offset-2">
<form action="" method="post" class="form-horizontal" role="form">{% csrf_token %}
{% for field in form %}
<div class="form-group">
<label for="{{ field.id_for_label }}" class="control-label col-sm-4">{{ field.label }}</label>
<div class="controls col-sm-8">
{% render_field field class+="form-control" placeholder=field.label %}
</div>
</div>
{% endfor %}
<p><input type="submit" value="Register" class="btn btn-primary pull-right"></p>
</form>
</div>
</div>
{% endblock %}

29
users/urls.py Normal file
View File

@@ -0,0 +1,29 @@
from django.urls import path
from django.conf.urls import include, url
from django.contrib import admin
from django.contrib.staticfiles.urls import staticfiles_urlpatterns
from django.contrib.auth.decorators import login_required
from django.conf import settings
from django.views.decorators.clickjacking import xframe_options_exempt
from django.contrib.auth.views import LoginView
from registration.backends.default.views import RegistrationView
from PyRIGS.decorators import permission_required_with_403
from users import regbackend, forms, views
urlpatterns = [
path('user/', include('django.contrib.auth.urls')),
path('user/', include('registration.backends.default.urls')),
path('user/register/', RegistrationView.as_view(form_class=forms.ProfileRegistrationFormUniqueEmail),
name="registration_register"),
path('user/login/', LoginView.as_view(authentication_form=forms.CheckApprovedForm), name='login'),
path('user/login/embed/', xframe_options_exempt(views.LoginEmbed.as_view()), name='login_embed'),
# User editing
path('user/edit/', login_required(views.ProfileUpdateSelf.as_view()),
name='profile_update_self'),
path('user/reset_api_key', login_required(views.ResetApiKey.as_view(permanent=False)),
name='reset_api_key'),
path('user/', login_required(views.ProfileDetail.as_view()), name='profile_detail'),
path('user/<pk>/',
permission_required_with_403('RIGS.view_profile')(views.ProfileDetail.as_view()),
name='profile_detail'),
]

79
users/views.py Normal file
View File

@@ -0,0 +1,79 @@
from django.core.exceptions import PermissionDenied
from django.http.response import HttpResponseRedirect
from django.http import HttpResponse
from django.urls import reverse_lazy, reverse, NoReverseMatch
from django.views import generic
from django.contrib.auth.views import LoginView
from django.db.models import Q
from django.shortcuts import get_object_or_404
from django.core import serializers
from django.conf import settings
import simplejson
from django.contrib import messages
import datetime
import pytz
import operator
from registration.views import RegistrationView
from django.views.decorators.csrf import csrf_exempt
from RIGS import models, forms
from assets import models as asset_models
from functools import reduce
# This view should be exempt from requiring CSRF token.
# Then we can check for it and show a nice error
# Don't worry, django.contrib.auth.views.login will
# check for it before logging the user in
class LoginEmbed(LoginView):
template_name = 'registration/login_embed.html'
@csrf_exempt
def dispatch(self, request, *args, **kwargs):
if request.method == "POST":
csrf_cookie = request.COOKIES.get('csrftoken', None)
if csrf_cookie is None:
messages.warning(request, 'Cookies do not seem to be enabled. Try logging in using a new tab.')
request.method = 'GET' # Render the page without trying to login
return super().dispatch(request, *args, **kwargs)
class ProfileDetail(generic.DetailView):
template_name = "profile_detail.html"
model = models.Profile
def get_queryset(self):
try:
pk = self.kwargs['pk']
except KeyError:
pk = self.request.user.id
self.kwargs['pk'] = pk
return self.model.objects.filter(pk=pk)
class ProfileUpdateSelf(generic.UpdateView):
template_name = "profile_form.html"
model = models.Profile
fields = ['first_name', 'last_name', 'email', 'initials', 'phone']
def get_queryset(self):
pk = self.request.user.id
self.kwargs['pk'] = pk
return self.model.objects.filter(pk=pk)
def get_success_url(self):
url = reverse_lazy('profile_detail')
return url
class ResetApiKey(generic.RedirectView):
def get_redirect_url(self, *args, **kwargs):
self.request.user.api_key = self.request.user.make_api_key()
self.request.user.save()
return reverse_lazy('profile_detail')