Combining DetailView and CreateView in Django 1.6
Asked Answered
D

2

6

I have 2 separate models, Post and Comment. I use DetailView to display Post contents and I want to use a CreateView to display comment creation form on the same page. What is the cleanest way to go about that?

The only thing that comes to mind is to use custom view which both gets an object and processes comment form, but this looks too dirty:

def post_detail(request, slug):
    post = get_object_or_404(Post, slug=slug)
    if request.POST:
        form = CommentForm(request.POST)
        # do comment form processing here
    return render(request, "post/post_detail.html", {
        "object": post, "comment_form": form})

Is there any clean way to do this using class based views? Or just some way to decouple post display code from comment processing code?

Despoliation answered 26/12, 2013 at 13:49 Comment(3)
What's dirty about this view? If it's the fact that you are having both GET and POST mixed in together, try django.views.generic.base.View. Or create templatetags to generate comment forms for objects (like old django.contrib.comments used to have).Borges
@kroolik What dirty is that my Comment and Post models logic is not decoupled. I do like the templatetags idea though. This way I can have separate view for creating comments.Despoliation
You can see how it's done hereBorges
A
3

One option would be to use the DetailView for the Post and a templatetag to display the comment form. Have the comment form submit to a Comment CreateView that redirects to the DetailView on success.

That said, it might get a little ugly if the form is invalid. In a pinch you can always call a DetailView or its methods from one of the CreateView methods. But IMO that introduces more coupling rather than less. Or you could have a separate utility function that you can call from the CreateView to display the Post if the comment form has errors.

Another option would be to use AJAX to process the comment form (in the separate CreateView) instead of a new page load.

In the end, regardless of language or framework, there's going to be a limit to how much one can decouple a view that needs to display one object type and create another.

Alan answered 30/12, 2013 at 15:53 Comment(6)
AJAX is a bit too nasty, but the template tag was my solution. I used an idea from here: github.com/django/django/blob/master/django/contrib/comments/… (thanks to @kroolik). Here's what I did at the end: github.com/ruslanosipov/rblog/blob/master/comment/templatetags/…Despoliation
I like that solution -- it might even be further generalizable to any model that needs to be edited in another's DetailView. So now I'm curious: how are you handling cases in which the comment form fails to validate?Alan
I take a user to the separate page where she/he can edit the comment: github.com/ruslanosipov/rblog/blob/master/comment/views.py. But not using AJAX is just a personal choice.Despoliation
Nothing wrong with that choice -- it's one I've also made at times in the past. I also like graceful degradation: even if you eventually add in AJAX, it's valuable to be able to handle the case where the user has javascript disabled.Alan
@RuslanOsipov the first two links you provided are no longer available.Protract
@AK12 To be honest, I don't even recall what it was 6 years later.Despoliation
G
4

It is possible to combine DetailView and CreateView. You use a class for DetailView and another class for CreateView, then you create a new class that inherits from View. This new class has a get and post method. The get method calls the DetailView while the post method calls the CreateView. Take note to use reverse_lazy for the success_url in CreateView. So basically your code should look something like this:

class PostView(DetailView):
    # your code 
    pass ;

class CommentView(CreateView):
    def get_success_url(self):
        return reverse_lazy('post_detail', kwargs={'pk': self.get_object(Post.objects.all().pk})

class PostCommentView(View):
    def get(self, request, *args, **kwargs):
         view = PostView.as_view()
         return view(request, *args, **kwargs) 

    def post(self, request, *args, **kwargs) :
         view = CommentView.as_view()
         return view(request, *args, **kwargs) 

So your urls.py will point to

PostCommentView 

I did an override of get_success_url because it will try to go to the detail view of the new comment which doesn't exist and is not what you want to do. So the override will take you to the DetailView of the post instead.

There is an explanation in the documentation.

Grove answered 3/5, 2018 at 9:10 Comment(0)
A
3

One option would be to use the DetailView for the Post and a templatetag to display the comment form. Have the comment form submit to a Comment CreateView that redirects to the DetailView on success.

That said, it might get a little ugly if the form is invalid. In a pinch you can always call a DetailView or its methods from one of the CreateView methods. But IMO that introduces more coupling rather than less. Or you could have a separate utility function that you can call from the CreateView to display the Post if the comment form has errors.

Another option would be to use AJAX to process the comment form (in the separate CreateView) instead of a new page load.

In the end, regardless of language or framework, there's going to be a limit to how much one can decouple a view that needs to display one object type and create another.

Alan answered 30/12, 2013 at 15:53 Comment(6)
AJAX is a bit too nasty, but the template tag was my solution. I used an idea from here: github.com/django/django/blob/master/django/contrib/comments/… (thanks to @kroolik). Here's what I did at the end: github.com/ruslanosipov/rblog/blob/master/comment/templatetags/…Despoliation
I like that solution -- it might even be further generalizable to any model that needs to be edited in another's DetailView. So now I'm curious: how are you handling cases in which the comment form fails to validate?Alan
I take a user to the separate page where she/he can edit the comment: github.com/ruslanosipov/rblog/blob/master/comment/views.py. But not using AJAX is just a personal choice.Despoliation
Nothing wrong with that choice -- it's one I've also made at times in the past. I also like graceful degradation: even if you eventually add in AJAX, it's valuable to be able to handle the case where the user has javascript disabled.Alan
@RuslanOsipov the first two links you provided are no longer available.Protract
@AK12 To be honest, I don't even recall what it was 6 years later.Despoliation

© 2022 - 2024 — McMap. All rights reserved.