One of the advantages of starting with Django is wielding its out-of-the-box authentication system. I want students to access common resources, but also be entitled to their own materials, which should be out of bounds for every other user.
From the very beginning, my models inherited the default Django User
class. I could have expanded it — merging Student
and User
— but I haven't given it much thought.
from django.contrib.auth.models import User
from django.db import models
class Student(models.Model):
# the name generated in the TiddlyWiki platform
student_code = models.CharField(max_length=20, default="CHANGEME")
user = models.ForeignKey(User, on_delete=models.CASCADE, null=True, blank=True)
def __str__(self):
return self.student_code
class Stint(models.Model):
student_id = models.ForeignKey(Student, on_delete=models.CASCADE)
stint_code = models.CharField(max_length=20, default="CHANGEME")
pass
This allows for two different strategies in the backend:
@login_required
def protected(request):
[...]
Simple, function-based views are nice, and I knew these before. Using a decorator, some views are protected from unauthenticated users — but this, by itself, does not prevent users from accessing each other's material!
But I came across class-based views (CBV), which are handled differently:
class StudentCodeRequiredMixin(LoginRequiredMixin):
def dispatch(self, request, *args, **kwargs):
# Call the superclass's dispatch method to ensure the user is logged in
response = super().dispatch(request, *args, **kwargs)
# Extract the student_code from the URL parameters
student_code = kwargs.get('student_code')
# Check if the logged-in user is associated with the student_code
try:
student = Student.objects.get(student_code=student_code)
if student.user != request.user:
return HttpResponseForbidden("You do not have permission to access this page.")
except Student.DoesNotExist:
return HttpResponseForbidden("Student not found.")
return response
class StintFilesView(StudentCodeRequiredMixin, View):
def get(self, request, student_code, stint_code):
try:
student = Student.objects.get(student_code=student_code)
stint = Stint.objects.get(student_id=student, stint_code=stint_code)
files_dict = get_dict_of_files_in_stint(student, stint)
return JsonResponse({'stint': stint.stint_code, 'files': files_dict})
except Student.DoesNotExist:
return JsonResponse({'error': 'Student not found'}, status=404)
except Stint.DoesNotExist:
return JsonResponse({'error': 'Stint not found'}, status=404)
The example above uses some mixins to ensure not only user authentication, but that an user is not accessing other user's resources.