Home > database >  Django problem with DeleteView deleting parent content
Django problem with DeleteView deleting parent content

Time:01-18

I have two models: a Project model and ProjectNotes. ProjectNotes are notes that are related to Projects. The ProjectNotes model has a project field that is a foreign key of the related Project.

The problem I have is that when I delete a note on a project, the entire project is deleted. Only the note should be deleted. I think I have on_delete=models.cascade set up correctly on the ProjectNotes model. So I think the issue is with the view.

The models:

class ProjectManager(models.Manager):
    def search(self, query=None):
        qs = self.get_queryset()
        if query is not None:
            or_lookup = (
                         Q(title__icontains=query) |
                         Q(description__icontains=query)
                         # Q(slug__icontains=query)
                        )
            qs = qs.filter(or_lookup).distinct() # distinct() is often necessary with Q lookups
        return qs

    def get_with_counted_notes_documents_todos(self):
        queryset = self.get_queryset().annotate(
            num_notes=Count('notes'),
            num_documents=Count('project_documents'),
            num_todos=Count('todo_group')
        )

        return queryset

class Project(models.Model):
    title = models.CharField(max_length= 200)
    description = tinymce_models.HTMLField()
    status = models.CharField(max_length=20, choices=PROJECT_CHOICES, default="active")
    date = models.DateTimeField(auto_now_add=True, null=True)
    created_by = models.ForeignKey(CustomUser, editable=False, null=True, blank=True, on_delete=models.RESTRICT)
    tags = tagulous.models.TagField(to=SiteWideTags, blank=True, related_name='projects_tags')

    objects = ProjectManager()

    def __str__(self):
        return self.title

    def get_absolute_url(self):
        return reverse('company_project:project_detail', args=[str(self.id)])


class ProjectNotesManager(models.Manager):
    def search(self, query=None):
        qs = self.get_queryset()
        if query is not None:
            or_lookup = (
                         Q(title__icontains=query) |
                         Q(body__icontains=query)
                         # Q(slug__icontains=query)
                        )
            qs = qs.filter(or_lookup).distinct() # distinct() is often necessary with Q lookups
        return qs

class ProjectNotes(models.Model):
    title = models.CharField(max_length=200)
    body = tinymce_models.HTMLField()
    project = models.ForeignKey(Project, default=0, blank=True, on_delete=models.CASCADE, related_name='notes')
    date = models.DateTimeField(auto_now_add=True, null=True)
    created_by = models.ForeignKey(CustomUser, editable=False, null=True, blank=True, on_delete=models.RESTRICT)

    def __str__(self):
        return self.title

    def get_absolute_url(self):
        return reverse('company_project:project_note_detail', args=[self.project_id, str(self.id)])

    objects = ProjectNotesManager()

The view:

class ProjectNoteDeleteView(DeleteBreadcrumbMixin, DeleteView):
    model = ProjectNotes
    template_name = 'company_accounts/delete_project_note.html'


    @cached_property
    def crumbs(self):
        project = self.get_object()
        relate_notes = self.get_note()
        bread_crumbs = [
            ("projects", reverse(
                "company_project:"   CompanyProjects.list_view_name, )
             ),
            (f"{project.title}",
             reverse(
                 "company_project:"   CompanyProjectsDetailView.detail_view_name,
                 kwargs={'pk': project.id})
             ),
            ("notes", reverse(
                "company_project:projectnotes_list",
                kwargs={'pk': project.id})
             ),
            (f"{relate_notes.title}",
             reverse(
                 "company_project:project_note_detail",
                 kwargs={'project_pk': project.id, 'pk': self.kwargs.get('pk')})
             ),
            (f"Delete: {relate_notes.title}",
             reverse(
                 "company_project:project_note_detail",
                 kwargs={'project_pk': project.id, 'pk': self.kwargs.get('pk')})
             )
        ]
        return bread_crumbs

    def get_object(self):
        return get_object_or_404(Project, id=self.kwargs.get('project_pk'))

    def get_note(self):
        project = self.get_object()
        return get_object_or_404(project.notes, id=self.kwargs.get('pk'))

    def related_project(self, **kwargs):
        project = get_object_or_404(Project, id=self.kwargs.get('project_pk'))
        return project

    def get_context_data(self, **kwargs):
        # Call the base implementation first to get a context
        context = super().get_context_data(**kwargs)
        context["note"] = self.get_note()
        context['project'] = self.get_object()
        return context

    def get_success_url(self):
        return reverse('company_project:project_detail', args=[(self.object.id)])

The URL:

path('project/<int:project_pk>/note/<int:pk>/delete', ProjectNoteDeleteView.as_view(), name='project_note_delete'),

The template:

<!-- templates/company_accounts/delete_project_note.html -->

{% extends 'base.html' %}
{% block content %}
<div >
  <div >
      <div >

        <form method="post">{% csrf_token %}
     
         <p>Are you sure you want to permanently delete project note "{{ note }}"?</p>
  
         <input type="submit"  value="Confirm">
        </form>
      </div>
    </div>
</div>
{% endblock content %}

CodePudding user response:

Look at the DeleteView implementation here.

As we can see DeleteView calls delete method for the result of calling get_object function, but your implementation of get_object return instance of Project model:

    def get_object(self):
        return get_object_or_404(Project, id=self.kwargs.get('project_pk'))

so this is a reason why your project deletes after you try to delete project note.

  • Related