Нет привязки комментария к каждому посту

models.py

from django.db import models
from django.contrib.contenttypes.models import ContentType
from django.contrib.contenttypes.fields import GenericRelation
from django.contrib.auth.models import User
from django.urls import reverse
from django.utils.text import slugify


class Post(models.Model):
    """Посты"""
    author = models.ForeignKey(User, verbose_name='Пользователь', on_delete=models.CASCADE)
    title = models.CharField(max_length=255, verbose_name='Название поста')
    text = models.TextField(max_length=2000, verbose_name='Текст')
    photo = models.ImageField(upload_to='None', height_field=None, width_field=None, max_length=100)
    slug = models.SlugField(max_length=50, verbose_name="URL поста", unique=True)
    # Не рекомендуется с версии 1.7
    comments = GenericRelation('comment')

    def __str__(self):
        return self.title

    def get_absolute_url(self):
        return reverse('post', kwargs={'game_post': self.slug})

    def save(self, *args, **kwargs):
        value = self.title
        self.slug = slugify(value, allow_unicode=True)
        super().save(*args, **kwargs)

    class Meta:
        verbose_name = 'Пост'
        verbose_name_plural = 'Посты'

class Comment(models.Model):
    """Комментарии к посту"""
    user = models.ForeignKey(User, verbose_name='Пользователь', on_delete=models.CASCADE, null=True, blank=True)
    text = models.TextField(verbose_name='Текст комментария')
    # При удалении будут уничтожены все связанные объекты
    parent = models.ForeignKey(
        'self',
        verbose_name='Родительский комментарий',
        blank=True,
        null=True,
        related_name='comment_children',
        on_delete=models.CASCADE
    )
    content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
    object_id = models.PositiveIntegerField()
    timestamp = models.DateTimeField(auto_now=True, verbose_name='Дата создания комментария')
    is_child = models.BooleanField(default=False)

    @property
    def get_parent(self):
        if not self.parent:
            return self.parent

    def __str__(self):
        return f'Пользователь {self.user} оставил комментарий: {self.text}'

    class Meta:
        verbose_name = 'Комментарий'
        verbose_name_plural = 'Комментарии'

views.py

Считаю, что проблема здесь пробовал менять значения contenttype, но все четно.

def comment(request):
    comments = Post.objects.first().comments.all()
    result = create_comments_tree(comments)
    comment_form = CommentForm(request.POST or None)
    return render(request, 'mainalithis/comment.html', {'comments': result, 'comment_form': comment_form})


def create_comment(request):
    comment_form = CommentForm(request.POST or None)
    if comment_form.is_valid():
        new_comment = comment_form.save(commit=False)
        new_comment.user = request.user
        new_comment.text = comment_form.cleaned_data['text']
        new_comment.content_type = ContentType.objects.get(model='post')
        new_comment.object_id = 1
        new_comment.parent = None
        new_comment.is_child = False
        new_comment.save()
    return HttpResponseRedirect('/comments')


@transaction.atomic()
def create_child_comment(request):
    user_name = request.POST.get('user')
    current_id = request.POST.get('id')
    text = request.POST.get('text')
    user = User.objects.get(username=user_name)
    content_type = ContentType.objects.get(model='post')
    parent = Comment.objects.get(id=int(current_id))
    is_child = False if not parent else True
    Comment.objects.create(
        user=user, text=text, content_type=content_type, object_id=True, parent=parent, is_child=is_child)

    comments_ = Post.objects.first().comments.all()
    comments_list = create_comments_tree(comments_)
    return render(request, 'mainalithis/comment.html', {'comments': comments_list})

utils.py

def get_children(qs_child):
    res = []
    for comment in qs_child:
        c = {
            'id': comment.id,
            'text': comment.text,
            'timestamp': comment.timestamp.strftime('%Y-%m-%d %H:%m'),
            'author': comment.user,
            'is_child': comment.is_child,
            'parent_id': comment.get_parent
        }
        if comment.comment_children.exists():
            c['children'] = get_children(comment.comment_children.all())
        res.append(c)
    return res
def create_comments_tree(qs):
    res = []
    for comment in qs:
        c = {
            'id': comment.id,
            'text': comment.text,
            'timestamp': comment.timestamp.strftime('%Y-%m-%d %H:%m'),
            'author': comment.user,
            'is_child': comment.is_child,
            'parent_id': comment.get_parent
        }
        if comment.comment_children:
            c['children'] = get_children(comment.comment_children.all())
        if not comment.is_child:
            res.append(c)
    return res

comments_tree.py

    from django.template import Library
    from django.utils.html import mark_safe
    register = Library()
    
    
    @register.filter()
    
    def comments_filter(comments_list):
        res = """
              <ul style = "list-style-type: none;">
                <div class = "col-md-12 mt-2">
                    {}
                </div>   
              </ul>
              """
    
        i = ''
        #mb - отступ снизу
        #mt - оступ сверху
        for comment in comments_list:
            i += """
                 <li>
                    <div class = "col-md-12 mb-2 mt-2 p-0"> 
                        <small>{author}</small> | опубликовано: {timestamp}
                        <hr>
                        <p>{text} | id={id}</p>
                        <a href="#" class = "reply" data-id = "{id}" data-parent = {parent_id}>Ответить</a>
                        <form action ="" method = "POST" class = "comment-form form-group" id = "form-{id}" style="display:none;">
                            <textarea type = "text" class = "form-control" name = "comment-text"></textarea><br>
                            <input type = "submit" class = "btn btn-primary submit-reply" data-id="{id}" data-submit-reply="{parent_id}" value = "Отправить">
                        </form>
                    </div>
                 </li>   
                 """.format(id=comment['id'], author=comment['author'], timestamp=comment['timestamp'], text=comment['text'], parent_id=comment['parent_id'])
            if comment.get('children'):
                i += comments_filter(comment['children'])
    
        

return mark_safe(res.format(i))

comment.html

{% load comments_tree %}
{% load crispy_forms_tags %}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" integrity="sha384-TX8t27EcRE3e/ihU7zmQxVncDAy5uIKz4rEkgIXeMed4M0jlfIDPvg6uqKI2xXr2" crossorigin="anonymous">

{% for post in posts %}
<div>
    <p>{{post.description}}</p>
    <p>{{ post.title }}</p>
    <p>{{ post.text }}</p>
    <p>{{ post.author }}</p>
    <p>{{ post.middle_star }}</p>
</div>
{% endfor %}


    <title>title</title>
</head>
<body>
<div class="row">
    <div class = "col-md-3">

    </div>

    <div class = "col-md-6">
        {{ comments|comments_filter }}
        <hr>
        <div class = "col-md-12">
            <form action="{%url 'comment_create' %}" method="POST" >
                {% csrf_token %}
                {{comment_form|crispy}}
                <input type="submit" class = "btn btn-primary" value="Отправить">
            </form>
        </div>
    </div>
    <div class = "col-md-6">
    </div>

</div>

</body>


<script
  src="https://code.jquery.com/jquery-3.5.1.js"
  integrity="sha256-QWo7LDvxbWT2tbbQ97B53yJnYU3WhH/C8ycbRAkjPDc="
  crossorigin="anonymous"></script>



<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js" integrity="sha384-ho+j7jyWK8fNQe+A12Hb8AhRq26LrZ/JpcUGGOn+Y7RsweNrtN/tE3MoK7ZeZDyx" crossorigin="anonymous"></script>


<script>
    $(document).ready(function(){
        $(".reply").on('click', function(){
            var commentId = $(this).attr('data-id')
            $("#form-"+commentId).fadeToggle();
        })
        $(".submit-reply").on('click', function(e){
            e.preventDefault()
            var parentId = $(this).attr('data-submit-reply')
            var id = $(this).attr('data-id')
            var text = $("#form-"+id).find('textarea[name="comment-text"]').val();
            function getCookie(name) {
            let cookieValue = null;
            if (document.cookie && document.cookie !== '') {
                const cookies = document.cookie.split(';');
                for (let i = 0; i < cookies.length; i++) {
                    const cookie = cookies[i].trim();
                    // Does this cookie string begin with the name we want?
                    if (cookie.substring(0, name.length + 1) === (name + '=')) {
                        cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
                        break;
                        }
                    }
                }
                return cookieValue;
            }
            const csrftoken = getCookie('csrftoken');
            data = {
                user: "{{ request.user.username }}",
                parentId: parentId,
                text: text,
                id: id,
                csrfmiddlewaretoken: csrftoken
            }
            $.ajax({
                method: "POST",
                data: data,
                url: "{% url 'comment_child_create' %}",
                success: function(data){
                    window.location.replace('/comments')
                 }
            })
        })
  })
</script>

<style>
    .textarea{
    width: 90%;
    height: 70px;
    resize: none;
    }
</style>
</html>

Ответы (1 шт):

Автор решения: Zhenia Kviatkivskyi

Привяжи в модели Comment посты через ForeignKey

 post = models.ForeignKey(Post,
                             verbose_name='пост',
                             on_delete=models.SET_NULL
                             )
urls.py

 path("post/<int:pk>/", views.PostDetailView.as_view()
views.py

class PostDetailView(View):
    """Вывод полной статьи"""
    
def get(self,request, **kwargs):
        
        template = 'app_name/your_page.html'
        
        post = get_object_or_404(Post, id= kwargs.get("pk"))

        
        return render(request, template, {'post': post})

А в теплейте через связь ForeignKey post.comments.all циклом вывести все прикрепленные к данной статье комменты.

<div class="post">
            <p>{{ post.title }}</p>
         
             <p>{{post.text }}</p>
      
           {% for comment in post.comment.all %}
         <p>{{ comment.text }}</p>
        

          {% endfor %}
        </div>

→ Ссылка