Skip to content

Commit 2445f46

Browse files
authored
Merge pull request #6267 from stsewd/refactor-profiles-view
Refactor profile's views
2 parents cb56df4 + d27a8fd commit 2445f46

File tree

4 files changed

+79
-205
lines changed

4 files changed

+79
-205
lines changed

readthedocs/profiles/urls/private.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,17 +13,17 @@
1313
account_urls = [
1414
url(
1515
r'^edit/',
16-
views.edit_profile,
17-
{
18-
'form_class': UserProfileForm,
19-
'template_name': 'profiles/private/edit_profile.html',
20-
},
16+
views.ProfileEdit.as_view(),
2117
name='profiles_profile_edit',
2218
),
23-
url(r'^delete/', views.delete_account, name='delete_account'),
19+
url(
20+
r'^delete/',
21+
views.AccountDelete.as_view(),
22+
name='delete_account',
23+
),
2424
url(
2525
r'^advertising/$',
26-
views.account_advertising,
26+
views.AccountAdvertisingEdit.as_view(),
2727
name='account_advertising',
2828
),
2929
]

readthedocs/profiles/urls/public.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,7 @@
88
urlpatterns = [
99
url(
1010
r'^(?P<username>[+\[email protected]]+)/$',
11-
views.profile_detail,
12-
{'template_name': 'profiles/public/profile_detail.html'},
11+
views.ProfileDetail.as_view(),
1312
name='profiles_profile_detail',
1413
),
1514
]

readthedocs/profiles/views.py

Lines changed: 71 additions & 195 deletions
Original file line numberDiff line numberDiff line change
@@ -1,213 +1,88 @@
11
"""Views for creating, editing and viewing site-specific user profiles."""
22

3-
from django.contrib import messages
43
from django.contrib.auth import logout
5-
from django.contrib.auth.decorators import login_required
64
from django.contrib.auth.models import User
7-
from django.http import HttpResponseRedirect
8-
from django.shortcuts import get_object_or_404, redirect, render
5+
from django.contrib.messages.views import SuccessMessageMixin
96
from django.urls import reverse
107
from django.utils.translation import ugettext_lazy as _
11-
from django.views.generic import ListView
128
from rest_framework.authtoken.models import Token
9+
from vanilla import DetailView, FormView, ListView, UpdateView
1310

14-
from readthedocs.core.forms import UserAdvertisingForm, UserDeleteForm
11+
from readthedocs.core.forms import (
12+
UserAdvertisingForm,
13+
UserDeleteForm,
14+
UserProfileForm,
15+
)
1516
from readthedocs.core.mixins import PrivateViewMixin
17+
from readthedocs.core.models import UserProfile
1618

1719

18-
@login_required
19-
def edit_profile(
20-
request,
21-
form_class,
22-
success_url=None,
23-
template_name='profiles/private/edit_profile.html',
24-
extra_context=None,
25-
):
26-
"""
27-
Edit the current user's profile.
28-
29-
**Optional arguments:**
30-
31-
``extra_context``
32-
A dictionary of variables to add to the template context. Any
33-
callable object in this dictionary will be called to produce
34-
the end result which appears in the context.
35-
36-
``form_class``
37-
The form class to use for validating and editing the user
38-
profile. This form class must operate similarly to a standard
39-
Django ``ModelForm`` in that it must accept an instance of the
40-
object to be edited as the keyword argument ``instance`` to
41-
its constructor, and it must implement a method named
42-
``save()`` which will save the updates to the object.
43-
44-
``success_url``
45-
The URL to redirect to following a successful edit. If not
46-
specified, this will default to the URL of
47-
:view:`profiles.views.profile_detail` for the profile object
48-
being edited.
49-
50-
``template_name``
51-
The template to use when displaying the profile-editing
52-
form. If not specified, this will default to
53-
:template:`profiles/edit_profile.html`.
54-
55-
**Context:**
56-
57-
``form``
58-
The form for editing the profile.
59-
60-
``profile``
61-
The user's current profile.
62-
63-
**Template:**
64-
65-
``template_name`` keyword argument or
66-
:template:`profiles/edit_profile.html`.
67-
"""
68-
profile_obj = request.user.profile
69-
if success_url is None:
70-
success_url = reverse(
20+
class ProfileEdit(PrivateViewMixin, UpdateView):
21+
22+
"""Edit the current user's profile."""
23+
24+
model = UserProfile
25+
form_class = UserProfileForm
26+
template_name = 'profiles/private/edit_profile.html'
27+
context_object_name = 'profile'
28+
29+
def get_object(self):
30+
return self.request.user.profile
31+
32+
def get_success_url(self):
33+
return reverse(
7134
'profiles_profile_detail',
72-
kwargs={'username': request.user.username},
73-
)
74-
if request.method == 'POST':
75-
form = form_class(
76-
data=request.POST,
77-
files=request.FILES,
78-
instance=profile_obj,
35+
kwargs={'username': self.request.user.username},
7936
)
80-
if form.is_valid():
81-
form.save()
82-
return HttpResponseRedirect(success_url)
83-
else:
84-
form = form_class(instance=profile_obj)
85-
86-
if extra_context is None:
87-
extra_context = {}
88-
context = {
89-
key: value() if callable(value) else value
90-
for key, value in extra_context.items()
91-
}
92-
context.update({
93-
'form': form,
94-
'profile': profile_obj,
95-
'user': profile_obj.user,
96-
})
97-
return render(request, template_name, context=context)
98-
99-
100-
@login_required()
101-
def delete_account(request):
102-
form = UserDeleteForm()
37+
38+
39+
class AccountDelete(PrivateViewMixin, SuccessMessageMixin, FormView):
40+
41+
form_class = UserDeleteForm
10342
template_name = 'profiles/private/delete_account.html'
43+
success_message = _('You have successfully deleted your account')
10444

105-
if request.method == 'POST':
106-
form = UserDeleteForm(instance=request.user, data=request.POST)
107-
if form.is_valid():
108-
# Delete the user permanently
109-
# It will also delete some projects where the user is the only owner
110-
request.user.delete()
111-
logout(request)
112-
messages.info(request, 'You have successfully deleted your account')
113-
114-
return redirect('homepage')
115-
116-
return render(request, template_name, {'form': form})
117-
118-
119-
def profile_detail(
120-
request,
121-
username,
122-
public_profile_field=None,
123-
template_name='profiles/public/profile_detail.html',
124-
extra_context=None,
125-
):
126-
"""
127-
Detail view of a user's profile.
128-
129-
If the user does not exists, ``Http404`` will be raised.
130-
131-
**Required arguments:**
132-
133-
``username``
134-
The username of the user whose profile is being displayed.
135-
136-
**Optional arguments:**
137-
138-
``extra_context``
139-
A dictionary of variables to add to the template context. Any
140-
callable object in this dictionary will be called to produce
141-
the end result which appears in the context.
142-
143-
``public_profile_field``
144-
The name of a ``BooleanField`` on the profile model; if the
145-
value of that field on the user's profile is ``False``, the
146-
``profile`` variable in the template will be ``None``. Use
147-
this feature to allow users to mark their profiles as not
148-
being publicly viewable.
149-
150-
If this argument is not specified, it will be assumed that all
151-
users' profiles are publicly viewable.
152-
153-
``template_name``
154-
The name of the template to use for displaying the profile. If
155-
not specified, this will default to
156-
:template:`profiles/profile_detail.html`.
157-
158-
**Context:**
159-
160-
``profile``
161-
The user's profile, or ``None`` if the user's profile is not
162-
publicly viewable (see the description of
163-
``public_profile_field`` above).
164-
165-
**Template:**
166-
167-
``template_name`` keyword argument or
168-
:template:`profiles/profile_detail.html`.
169-
"""
170-
user = get_object_or_404(User, username=username)
171-
profile_obj = user.profile
172-
if (public_profile_field is not None and
173-
not getattr(profile_obj, public_profile_field)):
174-
profile_obj = None
175-
176-
if extra_context is None:
177-
extra_context = {}
178-
context = {
179-
key: value() if callable(value) else value
180-
for key, value in extra_context.items()
181-
}
182-
context.update({'profile': profile_obj})
183-
return render(request, template_name, context=context)
184-
185-
186-
@login_required
187-
def account_advertising(request):
188-
success_url = reverse(account_advertising)
189-
profile_obj = request.user.profile
190-
if request.method == 'POST':
191-
form = UserAdvertisingForm(
192-
data=request.POST,
193-
instance=profile_obj,
194-
)
195-
if form.is_valid():
196-
form.save()
197-
messages.info(request, _('Updated your advertising preferences'))
198-
return HttpResponseRedirect(success_url)
199-
else:
200-
form = UserAdvertisingForm(instance=profile_obj)
201-
202-
return render(
203-
request,
204-
'profiles/private/advertising_profile.html',
205-
context={
206-
'form': form,
207-
'profile': profile_obj,
208-
'user': profile_obj.user,
209-
},
210-
)
45+
def get_object(self):
46+
return self.request.user
47+
48+
def form_valid(self, form):
49+
self.request.user.delete()
50+
logout(self.request)
51+
return super().form_valid(form)
52+
53+
def get_form(self, data=None, files=None, **kwargs):
54+
kwargs['instance'] = self.get_object()
55+
return super().get_form(data, files, **kwargs)
56+
57+
def get_success_url(self):
58+
return reverse('homepage')
59+
60+
61+
class ProfileDetail(DetailView):
62+
63+
model = User
64+
template_name = 'profiles/public/profile_detail.html'
65+
lookup_field = 'username'
66+
67+
def get_context_data(self, **kwargs):
68+
context = super().get_context_data(**kwargs)
69+
context['profile'] = self.get_object().profile
70+
return context
71+
72+
73+
class AccountAdvertisingEdit(PrivateViewMixin, SuccessMessageMixin, UpdateView):
74+
75+
model = UserProfile
76+
form_class = UserAdvertisingForm
77+
context_object_name = 'profile'
78+
template_name = 'profiles/private/advertising_profile.html'
79+
success_message = _('Updated your advertising preferences')
80+
81+
def get_object(self):
82+
return self.request.user.profile
83+
84+
def get_success_url(self):
85+
return reverse('account_advertising')
21186

21287

21388
class TokenMixin(PrivateViewMixin):
@@ -230,4 +105,5 @@ def get_success_url(self):
230105

231106

232107
class TokenList(TokenMixin, ListView):
108+
233109
pass

readthedocs/rtd_tests/tests/test_profile_views.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
# -*- coding: utf-8 -*-
21
from django.contrib.auth.models import User
32
from django.test import TestCase
43
from django.urls import reverse

0 commit comments

Comments
 (0)