So in my other life as a strength and conditioning coach, I have a few online mentees. They basically email me questions and I answer them.
I’m using Django to make an interface for them to submit questions and see all the other questions that other members have asked. Plus adding some searching so the old stuff doesn’t just die.
I am using the generic UpdateView
to answer the questions. This view is marking the question “published” and storing my answer in the database.
My issue is that I don’t want the users to be able to see this because then they could be adding answers to questions and people are paying for ME to answer the question.
This entails two major parts:
- Adding a link to the
UpdateView
in theDetailView
- Blocking permissions to access the form with
UserPassesTestMixin
Let’s walk through it.
Add link to UpdateView
Order doesn’t particularly matter here. You’ll have to do each step. I like to start with the URL, so first, we need to add the URL to our UpdateView to our urls.py
file.
# app_name/urls.py
from .views import AppNameUpdateView # make sure to import your view
urlpatterns = [
# other URL patterns
path('<int:pk>/update/', AppNameUpdateView.as_view(), name='model_name_update'),
]
Once we have that, let’s go ahead and make this UpdateView
in our views.py
.
# app_name/views.py
from .models import ModelName
class AppNameUpdateView(UpdateView):
model = ModelName
fields = ['field_1', 'field_2',']
template_name = 'app_name/model_name_update.html'
Now we just need to get the template running.
{# app_name/model_name_update.html #}
<h2>Update</h2>
<form action="{% url 'model_name_update' pk=obj.pk %}" method="post">
{% csrf_token %}
{{ form.as_p}}
<button type="submit">Update</button>
</form>
Check to make sure it works before moving on.
Once that skeleton is working, let’s link to this page on the DetailView
page. Remember we only want admins to be able to get to this UpdateView
.
{# app_name/templates/app_name/model_name_update.html #}
<!-- meat of the page above this line -->
{% if user.is_superuser %}
<a href="{% url 'model_name_update' pk=obj.pk %}">
Update
</a>
{% endif %}
Awesome, now we have a working link to the UpdateView
so we don’t have to hard type it directly into our browser.
Adding permissions
Now what if a user finds or guesses the link? They’ll still be able to see the UpdateView
because we’ve only authenticated the vision of the link to the page, not the view and its logic.
Fortunately, Django makes this super easy. You just have to know about the UserPassesTestMixin
. Here’s a link to the documentation.
UserPassesTestMixin
is designed to work with class-based views. All we need to do is
- define the test as a function within the view by overriding
test_func()
- define the
login_url
attribute to tell Django where to redirect an unauthorized user
# app_name/views.py
from .models import ModelName
class AppNameUpdateView(UpdateView):
model = ModelName
fields = ['field_1', 'field_2',']
template_name = 'app_name/model_name_update.html'
login_url = reverse_lazy('login') # new
def test_func(self): # new
return self.request.user.is_superuser
And there we go!