Trying to convert a function based view to a generic detail view

Creating a comment system for a ticket django project. I’ve completed most of the functionality for the comment, except I’m not quite sure how to implement the POST method so that I can use the frontend to create comments. If I use the admin site, it works and shows up in the view, but I get an error when I try to submit using the frontend form. I’m not sure how to proceed, any help is much appreciated.

Traceback


Traceback (most recent call last):
  File "C:\Users\mikha\bug_env\lib\site-packages\django\db\backends\utils.py", line 85, in _execute
    return self.cursor.execute(sql, params)
  File "C:\Users\mikha\bug_env\lib\site-packages\django\db\backends\sqlite3\base.py", line 416, in execute
    return Database.Cursor.execute(self, query, params)

The above exception (NOT NULL constraint failed: tickets_comment.ticket_id) was the direct cause of the following exception:
  File "C:\Users\mikha\bug_env\lib\site-packages\django\core\handlers\exception.py", line 47, in inner
    response = get_response(request)
  File "C:\Users\mikha\bug_env\lib\site-packages\django\core\handlers\base.py", line 181, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "C:\Users\mikha\bug_env\lib\site-packages\django\views\generic\base.py", line 69, in view
    return self.dispatch(request, *args, **kwargs)
  File "C:\Users\mikha\bug_env\lib\site-packages\django\views\generic\base.py", line 101, in dispatch
    return handler(request, *args, **kwargs)
  File "C:\Users\mikha\issuetracker\tickets\views.py", line 73, in post
    user_comment.save()
  File "C:\Users\mikha\bug_env\lib\site-packages\mptt\models.py", line 1096, in save
    super().save(*args, **kwargs)
  File "C:\Users\mikha\bug_env\lib\site-packages\django\db\models\base.py", line 743, in save
    self.save_base(using=using, force_insert=force_insert,
  File "C:\Users\mikha\bug_env\lib\site-packages\django\db\models\base.py", line 780, in save_base
    updated = self._save_table(
  File "C:\Users\mikha\bug_env\lib\site-packages\django\db\models\base.py", line 885, in _save_table
    results = self._do_insert(cls._base_manager, using, fields, returning_fields, raw)
  File "C:\Users\mikha\bug_env\lib\site-packages\django\db\models\base.py", line 923, in _do_insert
    return manager._insert(
  File "C:\Users\mikha\bug_env\lib\site-packages\django\db\models\manager.py", line 85, in manager_method
    return getattr(self.get_queryset(), name)(*args, **kwargs)
  File "C:\Users\mikha\bug_env\lib\site-packages\django\db\models\query.py", line 1301, in _insert
    return query.get_compiler(using=using).execute_sql(returning_fields)
  File "C:\Users\mikha\bug_env\lib\site-packages\django\db\models\sql\compiler.py", line 1441, in execute_sql
    cursor.execute(sql, params)
  File "C:\Users\mikha\bug_env\lib\site-packages\django\db\backends\utils.py", line 99, in execute
    return super().execute(sql, params)
  File "C:\Users\mikha\bug_env\lib\site-packages\django\db\backends\utils.py", line 67, in execute
    return self._execute_with_wrappers(sql, params, many=False, executor=self._execute)
  File "C:\Users\mikha\bug_env\lib\site-packages\django\db\backends\utils.py", line 76, in _execute_with_wrappers
    return executor(sql, params, many, context)
  File "C:\Users\mikha\bug_env\lib\site-packages\django\db\backends\utils.py", line 80, in _execute
    with self.db.wrap_database_errors:
  File "C:\Users\mikha\bug_env\lib\site-packages\django\db\utils.py", line 90, in __exit__
    raise dj_exc_value.with_traceback(traceback) from exc_value
  File "C:\Users\mikha\bug_env\lib\site-packages\django\db\backends\utils.py", line 85, in _execute
    return self.cursor.execute(sql, params)
  File "C:\Users\mikha\bug_env\lib\site-packages\django\db\backends\sqlite3\base.py", line 416, in execute
    return Database.Cursor.execute(self, query, params)

Here’s the original function based view

def ticket_single(request, post):

    ticket = get_object_or_404(Ticket, slug=ticket, status='published')

    allcomments = ticket.comments.filter(status=True)
    
    user_comment = None

    if request.method == 'POST':
        comment_form = NewCommentForm(request.POST)
        if comment_form.is_valid():
            user_comment = comment_form.save(commit=False)
            user_comment.ticket = ticket
            user_comment.save()
            return HttpResponseRedirect('/' + ticket.slug)
    else:
        comment_form = NewCommentForm()
    return render(request, 'ticket_detail.html', {'ticket': ticket, 'comments':  user_comment, 'comments': comments, 'comment_form': comment_form, 'allcomments': allcomments, })

Here’s what I have so far in the DetailView

class TicketDetailView(DetailView):
    model = Ticket

    
    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['comments'] = Comment.objects.filter(ticket=self.object)
        context['comment_form'] = NewCommentForm()
        return context
    
    def post(self, request, *args, **kwargs):
        if request.method == 'POST':
            comment_form = NewCommentForm(request.POST)
            if comment_form.is_valid():
                user_comment = comment_form.save(commit=False)
                user_comment.save()
                return render(request, 'ticket_detail.html', {'comments': user_comment, 'comment_form': comment_form})
            

A DetailView is designed to be a Display view, not an Edit view.

This is the wrong view to be trying to use for this purpose. You probably can get this to work, but what you’re really managed to do here is force the round peg into a square hole.

Given that you’re working with two different models here, (Ticket and Comment), none of the Django supplied edit CBVs are really a good fit for this purpose.

First, I suggest you re-evaluate your decision as to why you think there’s a benefit to be gained by changing what you have, to using a CBV intended for a different purpose.

If you still think there’s a benefit there, then what you’re really trying to build here would be best serviced by an UpdateView containing a formset for Comment or just a FormView where you don’t end up fighting model-specific functionality.

Hello Ken,

You mention that you shouldn’t use the Django supplied Generic Views for two different models (If thats what you mean for CBV’s). However in the tutorial they use the two models Choice and Question when they introduce Generic Views. Could you clarify why it’s appropriate here to use the Generic View/Provide other resources?

Many Thanks
Georgia

That’s not quite what I said.

What I said in this thread (and numerous others) is that when you’re trying to edit two different models at the same time in the same view, I am of the opinion that the generic edit views provided by Django aren’t the best choice. It always seemed to me that I ended up doing more work than I was saving by trying to use them.

But more importantly, in this thread, the original post was trying to add form-handling functionality to a DetailView, which is the wrong base to use for that purpose.

Ah ok thank you for the clarification. Ah so by “edit CBVs” you mean generic edit views I understand now.

1 Like