hat is the best way to create a nested comment section?
My current Views is setup is
class AddCommentView(CreateView):
model = Comment
form_class = CommentForm
template_name = 'add_comment.html'
# fields = '__all__'
def form_valid(self, form):
form.instance.post_id = self.kwargs['pk']
return super().form_valid(form)
success_url = reverse_lazy('home')
This is my current models is..
class Comment(models.Model):
post = models.ForeignKey(Post, related_name="comments", on_delete=models.CASCADE)
name = models.CharField(max_length=250)
body = models.TextField()
date_added = models.DateTimeField(auto_now_add=True)
def __str__(self):
return '%s - %s' % (self.post.title, self.name)
Any help on this would be greatly appreciated!
You need self-joined model to achieve this.
django-mptt ( Modified Preorder Tree Traversal) can be used for this purpose.
Assuming comment can be a response to a Post or to another comment something like
Post
Comment
Comment
Comment
Comment
Comment
Comment
this video is actually what you are looking for.
Learn Django 3 - Build MPTT Comments
Related
good evening! Hope everything is great!
Well, I have a little problem with redirect in Django, I was trying to make a comment section for my web app but after "posting" the comment, I can only redirect the user to my homepage (through return redirect ('/')
Models.py:
class Task(models.Model):
title = models.CharField(max_length=50)
content = models.TextField(blank=True)
date_created = models.DateTimeField(auto_now_add=True)
due_date = models.DateTimeField(default=timezone.now)
author = models.ForeignKey(User, null=True, on_delete=models.SET_NULL, default=User)
responsable = models.ForeignKey(User, null=True, on_delete=models.SET_NULL, related_name="author", default=author)
STATUS_CHOICES = [('D', 'Done'),('P','Doing'),('N','Not done')]
Status = models.CharField(max_length=1,choices=STATUS_CHOICES, default='N')
IMPORTANCE_CHOICES = [('H', 'High'),('M','Medium'),('L','Low')]
importance = models.CharField(max_length=1,choices=IMPORTANCE_CHOICES, default='M')
DEPARTAMENT_CHOICES = [('D', 'Dev'),('M','Marketing'),('H','HR'),('L','Legal'),('F','Finances'),('O','Others')]
departament = models.CharField(max_length=1,choices=DEPARTAMENT_CHOICES, default='M')
is_public = models.BooleanField(default=False)
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse("task-detail", kwargs={"pk": self.pk})
class Comment(models.Model):
task = models.ForeignKey(Task, on_delete=models.CASCADE, related_name='comments')
author = models.ForeignKey(User, null=True, on_delete=models.SET_NULL, default=User)
body = models.TextField(help_text='Add a comment')
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
active = models.BooleanField(default=True)
class Meta:
ordering = ('-created',)
def __str__(self):
return '%s - %s' % (self.task.title, self.author)
Views.py:
class CommentCreate(CreateView):
model = Comment
fields = ['body']
def form_valid(self, form):
form.instance.task_id = self.kwargs['pk']
form.instance.author = self.request.user
form.instance.created = timezone.now()
form.save()
return redirect('/')
urls.py
from django.urls import path, include
from . import views
from .views import (DashboardTaskAppView,
TaskDetailView,
TaskCreateView,
TaskUpdateView,
DashboardTaskAppViewPublic,
TaskDeleteView,
CommentCreate)
urlpatterns = [
path('', views.index_app, name="home-app"),
path('task/', DashboardTaskAppView.as_view(), name="task-home"),
path('task/public', DashboardTaskAppViewPublic.as_view(), name="task-home-public"),
path('task/new_task/', TaskCreateView.as_view(), name='task-create'),
path('task/<int:pk>/', TaskDetailView.as_view(), name='task-detail'),
path('task/<int:pk>/update', TaskUpdateView.as_view(), name='task-update'),
path('task/<int:pk>/delete', TaskDeleteView.as_view(), name='task-delete'),
path('task/<int:pk>/comment', CommentCreate.as_view(), name='task-comment'),
path('docs/', views.doc_taskapp, name="task-doc"),
]
What I would like to do is for after submit, to redirect the user for task detail where the comment lies.. I tried return redirect('task-details'), but didn't worked. After reading the docs, I think the problem is that I'm trying to access info(data) from Task(model) in Comment(model) and I'm not doing right, is possible to do that? And is possible to call class CommentCreate(CreateView) form in a modal? or is absolutely required to have a template.html?
Thanks in advance for your time and help!
Looks like you're looking for a redirect to the task detail page with the PK, so:
for function view:
return redirect('task-detail', pk=pk)
Update: for class based view:
return redirect('task-detail', self.kwargs['pk'])
Thanks to #Ed Kohler I found out, you gave me the correct line of thought! Thanks!
class CommentCreate(CreateView):
model = Comment
fields = ['body']
def form_valid(self, form):
form.instance.task_id = self.kwargs['pk']
form.instance.author = self.request.user
form.instance.created = timezone.now()
form.save()
return redirect('task-detail', pk=self.kwargs['pk'])
Thanks again!
I have a simple blog with 2 models: one for Post and one for Comment, like so:
class Post(models.Model):
title = models.CharField(max_length=100)
# content = models.TextField()
content = RichTextUploadingField(blank=True, null=True, config_name='claudiu_cfg')
date_posted = models.DateTimeField(default=timezone.now)
author = models.ForeignKey(User, on_delete=models.CASCADE)
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('post-detail', kwargs={'pk': self.pk})
class Comment(models.Model):
author = models.CharField(max_length=20)
text = models.TextField(max_length=350)
date_posted = models.DateTimeField(default=timezone.now)
post = models.ForeignKey(Post, on_delete=models.CASCADE)
def __str__(self):
return self.author
I want to display all posts (paginated) and how many comments each have.
My views.py:
class PostListView(ListView):
model = Post
template_name = 'blog/home.html'
context_object_name = 'posts'
ordering = ['-date_posted']
paginate_by = 5
Just like this i was getting all posts, but now i want to access the Comment table with as little selects as possible.
I know one way to do this is define a queryset and did it like this:
class PostListView(ListView):
model = Post
template_name = 'blog/home.html' # <app>/<model>_<viewtype>.html
context_object_name = 'posts'
ordering = ['-date_posted']
paginate_by = 5
queryset = Post.objects.all().prefetch_related()
However that does not access the Comment data.
Then i tried to overrite the get_queryset() function hopping to get the desired result. Still no success.
def get_queryset(self):
posts = Post.objects.all().prefetch_related('pk__comment').all()
comments = Comment.objects.all().prefetch_related('post')
print(dir(posts))
print('---------------------')
print(posts._prefetch_done)
return posts.order_by('-date_posted')
That still doesnt work and i cant understand where to go from here.
I did read the documentation, but that still didnt help me.
Please help, as i have nobody to ask for help when it comes to django.
It looks like the solution was closer than i expected, based on responses from
#dirkgroten i managed to fix my issue like so:
class PostListView(ListView):
model = Post
template_name = 'blog/home.html' # <app>/<model>_<viewtype>.html
context_object_name = 'posts'
ordering = ['-date_posted']
paginate_by = 5
queryset = Post.objects.all().prefetch_related('comment_set').all()
To display the number of comments in my template, in a for loop i added:
{{ post.comment_set.all|length }}
Thank you, without your help it would have taken me atleast a few more hours if not days.
I'm working with Django 2.2 and I'm facing a problem with redirecting in class-based success URL.
enter code here - view.py
class LetterFormView(CreateView):
template_name = 'post.html'
model = Letter
form_class = LetterForm
def get_success_url(self):
return reverse ('mainapp:user-profile')
and the url.py
enter code here- urls.py
path('profile/<username>/', views.UserProfilePage.as_view(), name='user-profile'),
class Letter(models.Model):
author = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, default=1)
subject = models.CharField(max_length=100, null=False, blank=False)
description = models.CharField(max_length=300, null=True, blank=True)
date_time = models.DateTimeField(auto_now_add=True)
Text = models.TextField()
This is how my model looks like
Now I'm not sure how to pass the username argument in here.
You can pass url parameters in reverse function using kwargs.
reverse ('mainapp:user-profile', kwargs={'username':self.object.username})
You seem to be missing a level of indentation on the get_success_url method; it needs to fall within class LetterFormView:
class LetterFormView(CreateView):
template_name = 'post.html'
model = Letter
form_class = LetterForm
def get_success_url(self):
return reverse ('mainapp:user-profile')
Also, be sure to indent with four spaces to adhere to PEP-8; you seem to be indenting with three spaces within the class.
you can try on view code
class LetterFormView(CreateView):
template_name = 'post.html'
model = Letter
form_class = LetterForm
def get_success_url(self):
return reverse('mainapp:user-profile', kwargs={'username': self.object.username')})
self.object.username please use how to condition with model structure on your project
and than on urls code don't forget to initilize data type on username argument
path('profile/<str:username>/', views.UserProfilePage.as_view(), name='user-profile'),
Okay, so first of all the situation is not quite easy as in the title. So I want to create form which can create object based on Cycle model and update only one field in Post model (field in question is 'cycle_title'). What is more its isn't only one post where this post have to be updated but there are several of it (all post's titles are saved in Cycle.post).
views
class CycleCreateView(LoginRequiredMixin, BSModalCreateView):
template_name = 'blog/cycle_form.html'
form_class = CycleForm
def get_form_kwargs(self):
kwargs = super().get_form_kwargs()
kwargs.update(user=self.request.user)
return kwargs
def form_valid(self, form, **kwargs):
form.instance.author = self.request.user
return super().form_valid(form)
def get_success_url(self):
reverse_user = self.request.user
return reverse('profile', kwargs={'username': reverse_user})
forms
class CycleForm(BSModalForm):
def __init__(self, *args, user=None, **kwargs):
super().__init__(*args, **kwargs)
if user is not None:
self.fields['posts'].queryset = Post.objects.filter(author=user)
class Meta:
model = Cycle
fields = ['title', 'description', 'posts']
widgets = {
'posts': forms.CheckboxSelectMultiple(),
}
models
class Post(models.Model):
title = models.CharField(max_length=100, unique=True)
content = MDTextField()
date_posted = models.DateTimeField(default=timezone.now)
author = models.ForeignKey(User, on_delete=models.CASCADE)
cycle_title = models.CharField(max_length=100, default='')
class Cycle(models.Model):
title = models.CharField(max_length=100, unique=True)
description = models.TextField(max_length=500, default="Brak opisu")
date_created = models.DateTimeField(default=timezone.now)
author = models.ForeignKey(User, on_delete=models.CASCADE)
posts = models.ManyToManyField(Post)
I was thinking about a solution like this:
for i in form.cleaned_data['posts']:
post = Post.objects.get(title=form.cleaned_data['title'][i])
post.cycle_title = form.cleaned_data['title']
post.save()
But I doubt if it is good way to resolve this issue
A package has been built just to handle this exact scenario, django-shapeshifter. You can find it here:
https://github.com/kennethlove/django-shapeshifter
The basic premise is to create two model forms, then include them in the same template. The example given shows how to update a User and a Profile model from the same view and form. It sounds like that is a match for your problem. Full disclosure, I'm a contributor to this package, but is was created exactly because of frustrations like your own!
I have a class Task with the following implementation:
class Task(models.Model):
author = models.ForeignKey(Author, unique=False)
name = models.CharField(max_length=255)
completed = models.BooleanField(default=False)
deadline = models.DateTimeField(null=True, blank=True)
pub_date = models.DateTimeField(auto_now_add=True, editable=False)
edit_date = models.DateTimeField(auto_now_add=True, auto_now=True, editable=False)
tag = models.ManyToManyField(Tag, related_name='tags', null=True, blank=True, default=None)
# group = models.ForeignKey(Group, blank=True, default=None)
def __str__(self):
return u'%s' % (self.name)
def toggle_complete(self):
self.completed = not self.completed
def is_past_deadline(self):
return timezone.now() > self.deadline
And I am trying to do a simple form that creates a new Task with a Title. But, as you can see, the author attribute can not be null (and don't want to, of course).
Author is implemented as follows:
class Author(models.Model):
user = models.OneToOneField(User, primary_key=True)
name = models.CharField(max_length=30)
def __str__(self):
return u'%s' % (self.user)
I tried and tried to hide the author field and, overriding methods like get_form_kwargs, form_valid, get_form to set it to the current logged user, but I always fail. Simply, the id is neither sent as post data (as seein in the debug trace), nor fetched from the view itself.
My best result has been showing the author field, creating the user correctly, but getting a "success_url" not found, even with the model having a get_absolute_url method declared.
The view I am working with is implemented like:
class HomeView(CreateView, MultipleObjectMixin):
# common
model = models.Task
template_name = 'home.html'
#form
form_class = TaskForm
# list
object_list = model.objects.all()
context_object_name = 'tasks'
paginate_by = 40
def dispatch(self, request, *args, **kwargs):
if not request.user.is_authenticated():
return HttpResponseRedirect(reverse('taskr:index'))
return super(HomeView, self).dispatch(request, *args, **kwargs)
def get_form_kwargs(self):
kwargs = super(HomeView, self).get_form_kwargs()
kwargs['initial']['author_id'] = self.request.user.id
return kwargs
def form_valid(self, form):
task = form.save(commit=False)
task.user = models.Author.objects.get(user=self.request.user) # use your own profile here
task.save()
return HttpResponseRedirect(self.get_success_url())
For the record, the MultipleObjectMixing part of the view works flawlessly.
I am desperate, is there any good resource for Django forms, one like http://ccbv.co.uk/? Thanks.
After a good night sleep, while cleaning up, I tried fixing the form_valid in the CreateView descendant and I got it right.
The trick is in
task.user = models.Author.objects.get(user=self.request.user)
and it failed to me because of desperate copy-pasting. The problem was that my Task model has no user attribute, but an author. So
task.author = models.Author.objects.get(user=self.request.user)
fixes it all.
Sorry for the stupid question.