Search code examples
djangodjango-viewsdjango-generic-views

Hiding user data of some users from others in django-generic-views


My goal is to allow users to create their own Project instances with nested Task instances. Other users should not have access to data that they did not create. How can I do this correctly? I wrote a custom query set for ProjectListView, but I have problems with other views. Maybe there is a common solution for that case?

models.py

class Project(models.Model):
    project_name = models.CharField(max_length=150, default='')
    user = models.ForeignKey(get_user_model(), null=True, on_delete=models.CASCADE)

class Task(models.Model):
    project = models.ForeignKey(Project, on_delete=models.CASCADE)
    task_name = models.CharField(max_length=250, default='')
    is_done = models.BooleanField(default=False)

views.py

class ProjectListView(LoginRequiredMixin, ListView):
    model = Project
    context_object_name = 'projects'

    def get_queryset(self):
        return Project.objects.filter(user=self.request.user)

class ProjectCreateView(LoginRequiredMixin, CreateView):
    model = Project
    fields = ('project_name',)
    success_url = reverse_lazy('projects')

class ProjectUpdateView(LoginRequiredMixin, UpdateView):
    model = Project
    fields = ('project_name',)
    template_name = 'backend/project_update_form.html'
    success_url = reverse_lazy('projects')

class ProjectDeleteView(LoginRequiredMixin, DeleteView):
    model = Project
    success_url = reverse_lazy('projects')

class TaskCreateView(LoginRequiredMixin, CreateView):
    model = Task
    fields = '__all__'
    success_url = reverse_lazy('projects')

class TaskUpdateView(LoginRequiredMixin, UpdateView):
    model = Task
    fields = ('task_name', 'is_done',)
    template_name = 'backend/task_update_form.html'
    success_url = reverse_lazy('projects')

class TaskDeleteView(LoginRequiredMixin, DeleteView):
    model = Task
    success_url = reverse_lazy('projects')

As result:

class TaskCreateView(LoginRequiredMixin, TaskMixin, CreateView):
    model = Task
    fields = '__all__'
    success_url = reverse_lazy('projects')

    def get_form(self, *args, **kwargs):
        form_class = super().get_form(form_class=None)

        form_class.fields['project'].choices =\
            [(project.pk, project) for project in Project.objects.filter(user=self.request.user)]
        
        return form_class

Solution

  • You can move that custom queryset up to a mixin:

    # mixins.py
    class ProjectMixin(object):
        def get_queryset(self):
            return Project.objects.filter(user=self.request.user)
    
    class TaskMixin(object):
        def get_queryset(self):
            return Task.objects.filter(project__user=self.request.user)
    
    # views.py
    class ProjectListView(LoginRequiredMixin, ProjectMixin, ListView):
        model = Project
        context_object_name = 'projects'
    
    class ProjectCreateView(LoginRequiredMixin, ProjectMixin, CreateView):
        model = Project
        fields = ('project_name',)
        success_url = reverse_lazy('projects')
    
    class ProjectUpdateView(LoginRequiredMixin, ProjectMixin, UpdateView):
        model = Project
        fields = ('project_name',)
        template_name = 'backend/project_update_form.html'
        success_url = reverse_lazy('projects')
    
    class ProjectDeleteView(LoginRequiredMixin, ProjectMixin, DeleteView):
        model = Project
        success_url = reverse_lazy('projects')
    
    class TaskCreateView(LoginRequiredMixin, TaskMixin, CreateView):
        model = Task
        fields = '__all__'
        success_url = reverse_lazy('projects')
    
    class TaskUpdateView(LoginRequiredMixin, TaskMixin, UpdateView):
        model = Task
        fields = ('task_name', 'is_done',)
        template_name = 'backend/task_update_form.html'
        success_url = reverse_lazy('projects')
    
    class TaskDeleteView(LoginRequiredMixin, TaskMixin, DeleteView):
        model = Task
        success_url = reverse_lazy('projects')