Limit Access to Only Superusers in Django
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 the DetailView
- 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!