contenttype实现评论

Posted by Shallow Dreameron July 7, 2024

好的,让我们更详细地解释和分步骤实施使用Django的ContentType实现通用的评论系统。

1. 创建评论模型

首先,我们需要创建一个通用的评论模型。这个模型将使用ContentType和GenericForeignKey来实现与任意模型的关联。

# models.py
from django.db import models
from django.contrib.contenttypes.models import ContentType
from django.contrib.contenttypes.fields import GenericForeignKey

class Comment(models.Model):
    user = models.ForeignKey('auth.User', on_delete=models.CASCADE)  # 记录评论的用户
    content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)  # 记录被评论对象的类型
    object_id = models.PositiveIntegerField()  # 记录被评论对象的ID
    content_object = GenericForeignKey('content_type', 'object_id')  # 通过content_type和object_id生成实际对象的引用
    content = models.TextField()  # 评论内容
    created_at = models.DateTimeField(auto_now_add=True)  # 评论创建时间

    def __str__(self):
        return f'Comment by {self.user} on {self.content_object}'

2. 为模型添加获取评论的方法

在你希望添加评论功能的模型中,添加一个方法来获取相关的评论。例如,在一个博客文章模型中:

# models.py
class BlogPost(models.Model):
    title = models.CharField(max_length=255)  # 博客标题
    content = models.TextField()  # 博客内容
    author = models.ForeignKey('auth.User', on_delete=models.CASCADE)  # 博客作者
    created_at = models.DateTimeField(auto_now_add=True)  # 创建时间

    def get_comments(self):
        content_type = ContentType.objects.get_for_model(self)  # 获取当前模型的ContentType
        return Comment.objects.filter(content_type=content_type, object_id=self.id)  # 获取所有相关的评论

    def __str__(self):
        return self.title

3. 创建评论的表单和视图

表单

创建一个用于提交评论的表单:

# forms.py
from django import forms
from .models import Comment

class CommentForm(forms.ModelForm):
    class Meta:
        model = Comment
        fields = ['content']  # 评论表单只包含内容字段

视图

创建一个视图来显示文章详情及其评论,并处理评论表单的提交:

# views.py
from django.shortcuts import render, get_object_or_404, redirect
from django.contrib.contenttypes.models import ContentType
from .models import BlogPost, Comment
from .forms import CommentForm

def post_detail(request, post_id):
    post = get_object_or_404(BlogPost, id=post_id)  # 获取博客文章
    comments = post.get_comments()  # 获取相关评论

    if request.method == 'POST':
        form = CommentForm(request.POST)
        if form.is_valid():
            comment = form.save(commit=False)
            comment.user = request.user  # 设置评论用户
            comment.content_type = ContentType.objects.get_for_model(BlogPost)  # 设置ContentType
            comment.object_id = post.id  # 设置被评论对象的ID
            comment.save()
            return redirect('post_detail', post_id=post.id)  # 提交评论后重定向到文章详情页
    else:
        form = CommentForm()

    return render(request, 'post_detail.html', {'post': post, 'comments': comments, 'form': form})

4. 创建模板

创建一个模板来显示文章详情、相关评论和评论表单:

<!-- templates/post_detail.html -->
<!DOCTYPE html>
<html>
<head>
    <title></title>
</head>
<body>
    <h1></h1>
    <p></p>
    <p>By  on </p>

    <h2>Comments</h2>
    <ul>
        
    </ul>

    <h2>Leave a comment</h2>
    <form method="post">
        
        
        <button type="submit">Add comment</button>
    </form>
</body>
</html>

详细解释

  1. 评论模型
    • Comment模型通过content_typeobject_id两个字段来引用被评论对象。content_object是一个GenericForeignKey,它结合content_typeobject_id生成一个实际的对象引用。
  2. 获取评论的方法
    • BlogPost模型中,我们定义了一个get_comments方法,它通过ContentType获取当前模型的类型,然后查询Comment模型中对应的评论。
  3. 表单和视图
    • CommentForm是一个简单的表单,仅包含评论内容字段。
    • post_detail视图处理评论表单的提交,并将评论与博客文章关联。视图首先获取博客文章,然后获取其相关的评论,并处理表单提交。
  4. 模板
    • 模板显示博客文章的详细信息、所有相关评论以及一个用于提交新评论的表单。

使用方法

  1. 创建和迁移数据库

    • models.py中定义完模型后,运行以下命令创建和迁移数据库:

      python manage.py makemigrations
      python manage.py migrate
      
  2. 更新urls.py

    • urls.py中添加一个URL模式来处理文章详情视图:

      # urls.py
      from django.urls import path
      from . import views
           
      urlpatterns = [
          path('post/<int:post_id>/', views.post_detail, name='post_detail'),
      ]
      
  3. 启动服务器

    • 运行开发服务器:

      python manage.py runserver
      
  4. 访问和测试

    • 在浏览器中访问文章详情页,查看文章、相关评论,并提交新评论。

通过这些步骤,你可以在Django项目中使用ContentType实现一个通用的评论系统,方便地将评论功能扩展到不同的模型。


好的,下面是一个完整的Django项目示例,用于实现通用的评论系统,并包括详细的解释。

项目结构

假设我们的项目名为 myproject,应用名为 blog。项目结构如下:

myproject/
│
├── myproject/
│   ├── __init__.py
│   ├── settings.py
│   ├── urls.py
│   ├── wsgi.py
│   └── asgi.py
│
├── blog/
│   ├── __init__.py
│   ├── admin.py
│   ├── apps.py
│   ├── models.py
│   ├── views.py
│   ├── forms.py
│   ├── urls.py
│   ├── templates/
│   │   └── post_detail.html
│   └── migrations/
│       └── __init__.py
│
└── manage.py

1. 创建Django项目和应用

首先,创建一个新的Django项目和一个应用:

django-admin startproject myproject
cd myproject
django-admin startapp blog

2. 修改settings.py

myproject/settings.py中,添加应用blog

# myproject/settings.py
INSTALLED_APPS = [
    ...
    'django.contrib.contenttypes',
    'blog',
]

3. 定义模型

blog/models.py中,定义博客文章模型和评论模型:

# blog/models.py
from django.db import models
from django.contrib.contenttypes.models import ContentType
from django.contrib.contenttypes.fields import GenericForeignKey

class BlogPost(models.Model):
    title = models.CharField(max_length=255)
    content = models.TextField()
    author = models.ForeignKey('auth.User', on_delete=models.CASCADE)
    created_at = models.DateTimeField(auto_now_add=True)

    def get_comments(self):
        content_type = ContentType.objects.get_for_model(self)
        return Comment.objects.filter(content_type=content_type, object_id=self.id)

    def __str__(self):
        return self.title

class Comment(models.Model):
    user = models.ForeignKey('auth.User', on_delete=models.CASCADE)
    content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
    object_id = models.PositiveIntegerField()
    content_object = GenericForeignKey('content_type', 'object_id')
    content = models.TextField()
    created_at = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return f'Comment by {self.user} on {self.content_object}'

4. 创建和应用迁移

创建迁移文件并应用迁移:

python manage.py makemigrations
python manage.py migrate

5. 创建评论表单

blog/forms.py中,定义评论表单:

# blog/forms.py
from django import forms
from .models import Comment

class CommentForm(forms.ModelForm):
    class Meta:
        model = Comment
        fields = ['content']

6. 创建视图

blog/views.py中,创建文章详情视图:

# blog/views.py
from django.shortcuts import render, get_object_or_404, redirect
from django.contrib.contenttypes.models import ContentType
from .models import BlogPost, Comment
from .forms import CommentForm

def post_detail(request, post_id):
    post = get_object_or_404(BlogPost, id=post_id)
    comments = post.get_comments()

    if request.method == 'POST':
        form = CommentForm(request.POST)
        if form.is_valid():
            comment = form.save(commit=False)
            comment.user = request.user
            comment.content_type = ContentType.objects.get_for_model(BlogPost)
            comment.object_id = post.id
            comment.save()
            return redirect('post_detail', post_id=post.id)
    else:
        form = CommentForm()

    return render(request, 'post_detail.html', {'post': post, 'comments': comments, 'form': form})

7. 配置URL

blog/urls.py中,定义URL模式:

# blog/urls.py
from django.urls import path
from . import views

urlpatterns = [
    path('post/<int:post_id>/', views.post_detail, name='post_detail'),
]

myproject/urls.py中,包含blog应用的URL:

# myproject/urls.py
from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', include('blog.urls')),
]

8. 创建模板

blog/templates/目录下,创建post_detail.html模板:

<!-- blog/templates/post_detail.html -->
<!DOCTYPE html>
<html>
<head>
    <title></title>
</head>
<body>
    <h1></h1>
    <p></p>
    <p>By  on </p>

    <h2>Comments</h2>
    <ul>
        
    </ul>

    <h2>Leave a comment</h2>
    <form method="post">
        
        
        <button type="submit">Add comment</button>
    </form>
</body>
</html>

9. 运行服务器并测试

启动开发服务器:

python manage.py runserver

在浏览器中访问 http://127.0.0.1:8000/post/1/ (假设有一个ID为1的博客文章)查看文章详情、相关评论,并提交新评论。

详细解释

  1. 模型定义
    • BlogPost模型表示博客文章,包含标题、内容、作者和创建时间。
    • Comment模型使用content_typeobject_id实现与任意模型的通用关联,content_object是一个GenericForeignKey,它结合content_typeobject_id生成实际对象的引用。
  2. 获取评论的方法
    • BlogPost模型中,定义了一个get_comments方法,通过ContentType获取当前模型的类型,然后查询Comment模型中对应的评论。
  3. 表单和视图
    • CommentForm是一个简单的表单,仅包含评论内容字段。
    • post_detail视图处理评论表单的提交,将评论与博客文章关联,并在页面上显示文章及其评论。
  4. 模板
    • post_detail.html模板显示博客文章的详细信息、所有相关评论以及一个用于提交新评论的表单。

通过这些步骤,你已经创建了一个完整的Django项目,能够使用ContentType实现通用的评论系统。这个系统可以方便地扩展到其他模型,如产品、新闻文章等,只需在相应模型中添加类似的获取评论的方法,并确保在视图中正确处理评论表单的提交。


好的,让我们详细解释每一部分的代码及其作用,确保每条代码的用途和功能都清晰明了。

1. 创建Django项目和应用

首先,创建一个新的Django项目和应用:

django-admin startproject myproject
cd myproject
django-admin startapp blog

2. 修改settings.py

myproject/settings.py中,添加应用blog

# myproject/settings.py
INSTALLED_APPS = [
    ...
    'django.contrib.contenttypes',  # 必需的Django组件,用于支持ContentType框架
    'blog',  # 添加我们的应用
]

3. 定义模型

blog/models.py中,定义博客文章模型和评论模型。

博客文章模型 BlogPost

# blog/models.py
from django.db import models
from django.contrib.contenttypes.models import ContentType
from django.contrib.contenttypes.fields import GenericForeignKey

class BlogPost(models.Model):
    title = models.CharField(max_length=255)  # 博客标题
    content = models.TextField()  # 博客内容
    author = models.ForeignKey('auth.User', on_delete=models.CASCADE)  # 关联到用户模型,表示作者
    created_at = models.DateTimeField(auto_now_add=True)  # 创建时间,自动添加

    def get_comments(self):
        content_type = ContentType.objects.get_for_model(self)  # 获取当前模型的ContentType
        return Comment.objects.filter(content_type=content_type, object_id=self.id)  # 获取所有相关的评论

    def __str__(self):
        return self.title  # 返回博客标题,方便在管理界面中显示

评论模型 Comment

class Comment(models.Model):
    user = models.ForeignKey('auth.User', on_delete=models.CASCADE)  # 关联到用户模型,表示评论者
    content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)  # 记录被评论对象的类型
    object_id = models.PositiveIntegerField()  # 记录被评论对象的ID
    content_object = GenericForeignKey('content_type', 'object_id')  # 通过content_type和object_id生成实际对象的引用
    content = models.TextField()  # 评论内容
    created_at = models.DateTimeField(auto_now_add=True)  # 评论创建时间,自动添加

    def __str__(self):
        return f'Comment by {self.user} on {self.content_object}'  # 返回评论信息,方便在管理界面中显示

4. 创建和应用迁移

创建迁移文件并应用迁移:

python manage.py makemigrations
python manage.py migrate

5. 创建评论表单

blog/forms.py中,定义评论表单:

# blog/forms.py
from django import forms
from .models import Comment

class CommentForm(forms.ModelForm):
    class Meta:
        model = Comment
        fields = ['content']  # 表单包含的字段,这里只包含评论内容

6. 创建视图

blog/views.py中,创建文章详情视图:

# blog/views.py
from django.shortcuts import render, get_object_or_404, redirect
from django.contrib.contenttypes.models import ContentType
from .models import BlogPost, Comment
from .forms import CommentForm

def post_detail(request, post_id):
    post = get_object_or_404(BlogPost, id=post_id)  # 根据ID获取博客文章,若不存在则返回404
    comments = post.get_comments()  # 获取该文章的所有相关评论

    if request.method == 'POST':  # 如果是通过POST方法提交表单
        form = CommentForm(request.POST)  # 将提交的数据绑定到表单
        if form.is_valid():  # 检查表单是否有效
            comment = form.save(commit=False)  # 创建评论对象,但不保存到数据库
            comment.user = request.user  # 设置评论的用户
            comment.content_type = ContentType.objects.get_for_model(BlogPost)  # 设置ContentType
            comment.object_id = post.id  # 设置被评论对象的ID
            comment.save()  # 保存评论到数据库
            return redirect('post_detail', post_id=post.id)  # 重定向到文章详情页面,避免重复提交表单
    else:
        form = CommentForm()  # 如果不是通过POST方法提交表单,则创建一个空的表单

    return render(request, 'post_detail.html', {'post': post, 'comments': comments, 'form': form})  # 渲染模板并传递上下文

7. 配置URL

blog/urls.py中,定义URL模式:

# blog/urls.py
from django.urls import path
from . import views

urlpatterns = [
    path('post/<int:post_id>/', views.post_detail, name='post_detail'),  # 文章详情页面的URL模式
]

myproject/urls.py中,包含blog应用的URL:

# myproject/urls.py
from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),  # 管理员界面的URL模式
    path('', include('blog.urls')),  # 包含blog应用的URL模式
]

8. 创建模板

blog/templates/目录下,创建post_detail.html模板:

<!-- blog/templates/post_detail.html -->
<!DOCTYPE html>
<html>
<head>
    <title></title>
</head>
<body>
    <h1></h1>
    <p></p>
    <p>By  on </p>

    <h2>Comments</h2>
    <ul>
        
    </ul>

    <h2>Leave a comment</h2>
    <form method="post">
          <!-- 跨站请求伪造保护 -->
          <!-- 以段落形式渲染表单字段 -->
        <button type="submit">Add comment</button>  <!-- 提交按钮 -->
    </form>
</body>
</html>

9. 运行服务器并测试

启动开发服务器:

python manage.py runserver

在浏览器中访问 http://127.0.0.1:8000/post/1/ (假设有一个ID为1的博客文章)查看文章详情、相关评论,并提交新评论。

详细解释

1. BlogPost 模型

  • title: 标题字段,最大长度为255字符。
  • content: 内容字段,用于存储博客文章内容。
  • author: 外键字段,关联到用户模型,用于表示文章作者。
  • created_at: 日期时间字段,自动添加创建时间。
  • get_comments: 方法,用于获取当前博客文章的所有评论。
  • __str__: 方法,返回博客标题,方便在管理界面中显示。

2. Comment 模型

  • user: 外键字段,关联到用户模型,用于表示评论者。
  • content_type: 外键字段,指向ContentType模型,用于记录被评论对象的类型。
  • object_id: 整数字段,记录被评论对象的ID。
  • content_object: GenericForeignKey字段,通过content_typeobject_id生成实际对象的引用。
  • content: 内容字段,用于存储评论内容。
  • created_at: 日期时间字段,自动添加创建时间。
  • __str__: 方法,返回评论信息,方便在管理界面中显示。

3. 评论表单 CommentForm

  • CommentForm: 表单类,用于创建和验证评论表单。
  • Meta: 内部类,用于指定表单的模型和字段。

4. 视图 post_detail

  • post_detail: 视图函数,用于处理文章详情页面的请求。
    • get_object_or_404: 获取博客文章,若不存在则返回404错误。
    • get_comments: 获取该文章的所有相关评论。
    • request.method == 'POST': 检查请求方法是否为POST。
    • CommentForm(request.POST): 将提交的数据绑定到表单。
    • form.is_valid(): 检查表单是否有效。
    • form.save(commit=False): 创建评论对象,但不保存到数据库。
    • comment.save(): 保存评论到数据库。
    • redirect('post_detail', post_id=post.id): 重定向到文章详情页面。
    • render(request, 'post_detail.html', {'post': post, 'comments': comments, 'form': form}): 渲染模板并传递上下文。

5. URL配置

  • blog/urls.py: 定义应用的URL模式。
    • `path(‘post//', views.post_detail, name='post_detail

’)`: 文章详情页面的URL模式。

  • myproject/urls.py: 包含应用的URL模式。
    • path('', include('blog.urls')): 包含blog应用的URL模式。

6. 模板 post_detail.html

  • 显示博客文章的详细信息、所有相关评论以及一个用于提交新评论的表单。
  • ``: 跨站请求伪造保护。
  • ``: 以段落形式渲染表单字段。

通过这些步骤,你已经创建了一个完整的Django项目,能够使用ContentType实现通用的评论系统。这个系统可以方便地扩展到其他模型,如产品、新闻文章等,只需在相应模型中添加类似的获取评论的方法,并确保在视图中正确处理评论表单的提交。


好的,下面我将详细讲解如何将评论系统作为一个独立的Django应用,并解释每段代码的作用。

项目结构

首先,让我们看一下整个项目的结构:

myproject/
│
├── myproject/
│   ├── __init__.py
│   ├── settings.py
│   ├── urls.py
│   ├── wsgi.py
│   └── asgi.py
│
├── blog/
│   ├── __init__.py
│   ├── admin.py
│   ├── apps.py
│   ├── models.py
│   ├── views.py
│   └── migrations/
│       └── __init__.py
│
├── comments/
│   ├── __init__.py
│   ├── admin.py
│   ├── apps.py
│   ├── models.py
│   ├── views.py
│   ├── forms.py
│   ├── urls.py
│   └── migrations/
│       └── __init__.py
│
└── manage.py

1. 创建一个新的Django应用

在你的Django项目根目录下创建一个新的应用comments

python manage.py startapp comments

2. 定义通用的评论模型

comments/models.py 中定义通用的评论模型:

# comments/models.py
from django.db import models
from django.contrib.contenttypes.models import ContentType
from django.contrib.contenttypes.fields import GenericForeignKey

class Comment(models.Model):
    user = models.ForeignKey('auth.User', on_delete=models.CASCADE)  # 关联用户
    content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)  # 关联的内容类型
    object_id = models.PositiveIntegerField()  # 关联的对象ID
    content_object = GenericForeignKey('content_type', 'object_id')  # 通用外键
    content = models.TextField()  # 评论内容
    created_at = models.DateTimeField(auto_now_add=True)  # 创建时间

    def __str__(self):
        return f'Comment by {self.user} on {self.content_object}'

解释:

  • user: 这是一个外键,指向Django内置的用户模型,用于标识评论的作者。
  • content_type: 这是一个外键,指向ContentType模型,用于标识评论的对象类型。
  • object_id: 这是一个正整数字段,用于存储评论的对象ID。
  • content_object: 这是一个通用外键,结合content_typeobject_id,用于指向被评论的对象。
  • content: 这是一个文本字段,用于存储评论的内容。
  • created_at: 这是一个日期时间字段,用于存储评论的创建时间,自动设置为当前时间。

3. 定义评论表单

comments/forms.py 中定义评论表单:

# comments/forms.py
from django import forms
from .models import Comment

class CommentForm(forms.ModelForm):
    class Meta:
        model = Comment
        fields = ['content']  # 评论内容

解释:

  • CommentForm:这是一个基于ModelForm的表单,用于创建和验证评论数据。
  • Meta:这是一个元类,用于指定表单相关的元数据。
  • model:指定表单对应的模型为Comment
  • fields:指定表单包含的字段,这里只包含content字段。

4. 创建视图

comments/views.py 中创建视图:

# comments/views.py
from django.shortcuts import render, redirect
from django.contrib.contenttypes.models import ContentType
from .models import Comment
from .forms import CommentForm

def add_comment(request, content_type_id, object_id):
    content_type = ContentType.objects.get(id=content_type_id)
    model_class = content_type.model_class()
    obj = model_class.objects.get(id=object_id)

    if request.method == 'POST':
        form = CommentForm(request.POST)
        if form.is_valid():
            comment = form.save(commit=False)
            comment.user = request.user
            comment.content_type = content_type
            comment.object_id = obj.id
            comment.save()
            return redirect(request.META.get('HTTP_REFERER', '/'))
    else:
        form = CommentForm()

    comments = Comment.objects.filter(content_type=content_type, object_id=obj.id)
    return render(request, 'comments/comment_list.html', {'form': form, 'comments': comments, 'object': obj})

解释:

  • add_comment:这是一个视图函数,用于处理添加评论的请求。
  • content_type_idobject_id:这些是URL参数,用于标识被评论的对象。
  • content_type:获取对应的ContentType实例。
  • model_class:获取对应的模型类。
  • obj:获取被评论的对象实例。
  • if request.method == 'POST':处理POST请求。
    • form.is_valid():验证表单数据。
    • comment.user:设置评论的作者。
    • comment.content_typecomment.object_id:设置评论的对象。
    • comment.save():保存评论。
    • redirect(request.META.get('HTTP_REFERER', '/')):重定向到前一个页面。
  • else:处理非POST请求,创建一个空的表单实例。
  • comments = Comment.objects.filter(content_type=content_type, object_id=obj.id):获取所有与该对象关联的评论。
  • render(request, 'comments/comment_list.html', {'form': form, 'comments': comments, 'object': obj}):渲染评论列表模板。

5. 配置URL

comments/urls.py 中配置URL:

# comments/urls.py
from django.urls import path
from .views import add_comment

urlpatterns = [
    path('add_comment/<int:content_type_id>/<int:object_id>/', add_comment, name='add_comment'),
]

解释:

  • path('add_comment/<int:content_type_id>/<int:object_id>/', add_comment, name='add_comment'):配置URL模式,调用add_comment视图函数。

在主项目的 urls.py 中包含评论应用的URL:

# myproject/urls.py
from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('comments/', include('comments.urls')),  # 包含评论应用的URL
]

解释:

  • path('comments/', include('comments.urls')):包含comments应用的URL配置。

6. 在主项目中集成评论系统

在你希望集成评论系统的模型中,例如 BlogPost 模型,添加一个获取评论的辅助方法:

# blog/models.py
from django.db import models
from django.contrib.contenttypes.models import ContentType
from comments.models import Comment

class BlogPost(models.Model):
    title = models.CharField(max_length=255)
    content = models.TextField()
    author = models.ForeignKey('auth.User', on_delete=models.CASCADE)
    created_at = models.DateTimeField(auto_now_add=True)

    def get_comments(self):
        content_type = ContentType.objects.get_for_model(self)
        return Comment.objects.filter(content_type=content_type, object_id=self.id)

    def get_content_type_id(self):
        return ContentType.objects.get_for_model(self).id

    def __str__(self):
        return self.title

解释:

  • get_comments:获取与当前BlogPost实例关联的所有评论。
  • get_content_type_id:获取BlogPost模型的ContentType ID。

在你的视图中加载和显示评论:

# blog/views.py
from django.shortcuts import render, get_object_or_404
from .models import BlogPost
from comments.forms import CommentForm

def post_detail(request, post_id):
    post = get_object_or_404(BlogPost, id=post_id)
    comments = post.get_comments()
    form = CommentForm()
    return render(request, 'blog/post_detail.html', {'post': post, 'comments': comments, 'form': form})

解释:

  • post_detail:这是一个视图函数,用于显示博客文章的详细信息以及相关的评论。
  • post = get_object_or_404(BlogPost, id=post_id):获取BlogPost实例,如果找不到则返回404错误。
  • comments = post.get_comments():获取与该博客文章关联的所有评论。
  • form = CommentForm():创建一个新的评论表单实例。
  • render(request, 'blog/post_detail.html', {'post': post, 'comments': comments, 'form': form}):渲染博客文章详情模板。

总结

通过上述步骤,你可以将评论系统作为一个独立的Django应用,并在其他项目中重复使用。只需包含comments应用,并根据需要配置模型和视图,即可在任何Django项目中实现评论功能。


下面是一个详细注释的完整代码示例,包含了 comments 应用和两个示例应用 blogproduct,每行代码都附有详细的注释。

项目结构

myproject/
│
├── myproject/
│   ├── __init__.py
│   ├── settings.py
│   ├── urls.py
│   ├── wsgi.py
│   └── asgi.py
│
├── comments/
│   ├── __init__.py
│   ├── models.py
│   ├── admin.py
│   ├── views.py
│   ├── urls.py
│   └── migrations/
│       └── __init__.py
│
├── blog/
│   ├── __init__.py
│   ├── admin.py
│   ├── apps.py
│   ├── models.py
│   ├── views.py
│   ├── urls.py
│   ├── forms.py
│   └── migrations/
│       └── __init__.py
│
├── product/
│   ├── __init__.py
│   ├── admin.py
│   ├── apps.py
│   ├── models.py
│   ├── views.py
│   ├── urls.py
│   ├── forms.py
│   └── migrations/
│       └── __init__.py
│
└── manage.py

comments 应用

comments/models.py

# comments/models.py
from django.db import models  # 导入Django的模型类
from django.contrib.contenttypes.models import ContentType  # 导入ContentType
from django.contrib.contenttypes.fields import GenericForeignKey  # 导入GenericForeignKey

class BaseComment(models.Model):  # 定义一个抽象的基类评论模型
    user = models.ForeignKey('auth.User', on_delete=models.CASCADE)  # 关联评论用户
    content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)  # 关联内容类型
    object_id = models.PositiveIntegerField()  # 存储关联对象的ID
    content_object = GenericForeignKey('content_type', 'object_id')  # 定义一个通用外键
    content = models.TextField()  # 评论内容
    created_at = models.DateTimeField(auto_now_add=True)  # 创建时间,自动设置为当前时间

    class Meta:
        abstract = True  # 将该模型设为抽象模型,不会生成数据库表

    def __str__(self):
        return f'Comment by {self.user} on {self.content_object}'  # 定义模型的字符串表示

blog 应用

blog/models.py

# blog/models.py
from django.db import models  # 导入Django的模型类
from django.contrib.contenttypes.models import ContentType  # 导入ContentType
from comments.models import BaseComment  # 导入基类评论模型

class BlogPost(models.Model):  # 定义博客文章模型
    title = models.CharField(max_length=255)  # 文章标题
    content = models.TextField()  # 文章内容
    author = models.ForeignKey('auth.User', on_delete=models.CASCADE)  # 关联作者
    created_at = models.DateTimeField(auto_now_add=True)  # 创建时间

    def get_comments(self):  # 获取文章的所有评论
        content_type = ContentType.objects.get_for_model(self)  # 获取ContentType实例
        return BlogPostComment.objects.filter(content_type=content_type, object_id=self.id)  # 过滤评论

    def get_content_type_id(self):  # 获取ContentType ID
        return ContentType.objects.get_for_model(self).id

    def __str__(self):
        return self.title  # 定义模型的字符串表示

class BlogPostHistory(models.Model):  # 定义博客文章历史记录模型
    original_post = models.ForeignKey(BlogPost, on_delete=models.CASCADE)  # 关联原始文章
    title = models.CharField(max_length=255)  # 历史记录标题
    content = models.TextField()  # 历史记录内容
    author = models.ForeignKey('auth.User', on_delete=models.CASCADE)  # 关联作者
    created_at = models.DateTimeField(auto_now_add=True)  # 创建时间

    def get_comments(self):  # 获取历史记录的所有评论
        content_type = ContentType.objects.get_for_model(self)  # 获取ContentType实例
        return BlogPostHistoryComment.objects.filter(content_type=content_type, object_id=self.id)  # 过滤评论

    def get_content_type_id(self):  # 获取ContentType ID
        return ContentType.objects.get_for_model(self).id

    def __str__(self):
        return self.title  # 定义模型的字符串表示

class BlogPostComment(BaseComment):  # 博客文章评论模型,继承自BaseComment
    class Meta:
        db_table = 'blog_post_comments'  # 指定数据库表名

class BlogPostHistoryComment(BaseComment):  # 博客文章历史评论模型,继承自BaseComment
    class Meta:
        db_table = 'blog_post_history_comments'  # 指定数据库表名

blog/forms.py

# blog/forms.py
from django import forms  # 导入Django的表单类
from .models import BlogPostComment, BlogPostHistoryComment  # 导入评论模型

class BlogPostCommentForm(forms.ModelForm):  # 博客文章评论表单
    class Meta:
        model = BlogPostComment  # 指定表单对应的模型
        fields = ['content']  # 表单包含的字段

class BlogPostHistoryCommentForm(forms.ModelForm):  # 博客文章历史评论表单
    class Meta:
        model = BlogPostHistoryComment  # 指定表单对应的模型
        fields = ['content']  # 表单包含的字段

blog/views.py

# blog/views.py
from django.shortcuts import render, redirect, get_object_or_404  # 导入Django的视图工具
from django.contrib.contenttypes.models import ContentType  # 导入ContentType
from .models import BlogPost, BlogPostHistory, BlogPostComment, BlogPostHistoryComment  # 导入博客模型
from .forms import BlogPostCommentForm, BlogPostHistoryCommentForm  # 导入评论表单

def add_blogpost_comment(request, content_type_id, object_id):  # 添加博客文章评论
    return handle_comment(request, BlogPostComment, BlogPostCommentForm, content_type_id, object_id)

def add_blogpost_history_comment(request, content_type_id, object_id):  # 添加博客文章历史评论
    return handle_comment(request, BlogPostHistoryComment, BlogPostHistoryCommentForm, content_type_id, object_id)

def handle_comment(request, CommentModel, CommentForm, content_type_id, object_id):  # 处理添加评论
    content_type = ContentType.objects.get(id=content_type_id)  # 获取ContentType实例
    model_class = content_type.model_class()  # 获取对应的模型类
    obj = model_class.objects.get(id=object_id)  # 获取对应的对象

    if request.method == 'POST':  # 如果请求方法是POST
        form = CommentForm(request.POST)  # 创建表单实例
        if form.is_valid():  # 如果表单有效
            comment = form.save(commit=False)  # 暂不保存评论实例
            comment.user = request.user  # 设置评论的用户
            comment.content_type = content_type  # 设置评论的内容类型
            comment.object_id = obj.id  # 设置评论的对象ID
            comment.save()  # 保存评论
            return redirect(request.META.get('HTTP_REFERER', '/'))  # 重定向到上一页面
    else:
        form = CommentForm()  # 如果请求方法不是POST,创建空表单实例

    comments = CommentModel.objects.filter(content_type=content_type, object_id=obj.id)  # 获取对象的所有评论
    return render(request, 'blog/comment_list.html', {'form': form, 'comments': comments, 'object': obj})  # 渲染评论列表页面

def post_detail(request, post_id):  # 博客文章详情视图
    post = get_object_or_404(BlogPost, id=post_id)  # 获取文章对象,如果不存在则返回404
    comments = post.get_comments()  # 获取文章的所有评论
    form = BlogPostCommentForm()  # 创建空的评论表单实例
    return render(request, 'blog/post_detail.html', {'post': post, 'comments': comments, 'form': form})  # 渲染文章详情页面

def post_history_detail(request, history_id):  # 博客文章历史详情视图
    post_history = get_object_or_404(BlogPostHistory, id=history_id)  # 获取历史记录对象,如果不存在则返回404
    comments = post_history.get_comments()  # 获取历史记录的所有评论
    form = BlogPostHistoryCommentForm()  # 创建空的历史评论表单实例
    return render(request, 'blog/post_history_detail.html', {'post_history': post_history, '

comments': comments, 'form': form})  # 渲染历史记录详情页面

blog/urls.py

# blog/urls.py
from django.urls import path  # 导入路径函数
from .views import add_blogpost_comment, add_blogpost_history_comment, post_detail, post_history_detail  # 导入视图函数

urlpatterns = [
    path('post/<int:post_id>/', post_detail, name='post_detail'),  # 博客文章详情页面URL
    path('post_history/<int:history_id>/', post_history_detail, name='post_history_detail'),  # 博客文章历史详情页面URL
    path('add_blogpost_comment/<int:content_type_id>/<int:object_id>/', add_blogpost_comment, name='add_blogpost_comment'),  # 添加博客文章评论URL
    path('add_blogpost_history_comment/<int:content_type_id>/<int:object_id>/', add_blogpost_history_comment, name='add_blogpost_history_comment'),  # 添加博客文章历史评论URL
]

product 应用

product/models.py

# product/models.py
from django.db import models  # 导入Django的模型类
from django.contrib.contenttypes.models import ContentType  # 导入ContentType
from comments.models import BaseComment  # 导入基类评论模型

class Product(models.Model):  # 定义产品模型
    name = models.CharField(max_length=255)  # 产品名称
    description = models.TextField()  # 产品描述
    price = models.DecimalField(max_digits=10, decimal_places=2)  # 产品价格
    created_at = models.DateTimeField(auto_now_add=True)  # 创建时间

    def get_comments(self):  # 获取产品的所有评论
        content_type = ContentType.objects.get_for_model(self)  # 获取ContentType实例
        return ProductComment.objects.filter(content_type=content_type, object_id=self.id)  # 过滤评论

    def get_content_type_id(self):  # 获取ContentType ID
        return ContentType.objects.get_for_model(self).id

    def __str__(self):
        return self.name  # 定义模型的字符串表示

class ProductHistory(models.Model):  # 定义产品历史记录模型
    original_product = models.ForeignKey(Product, on_delete=models.CASCADE)  # 关联原始产品
    name = models.CharField(max_length=255)  # 历史记录名称
    description = models.TextField()  # 历史记录描述
    price = models.DecimalField(max_digits=10, decimal_places=2)  # 历史记录价格
    created_at = models.DateTimeField(auto_now_add=True)  # 创建时间

    def get_comments(self):  # 获取历史记录的所有评论
        content_type = ContentType.objects.get_for_model(self)  # 获取ContentType实例
        return ProductHistoryComment.objects.filter(content_type=content_type, object_id=self.id)  # 过滤评论

    def get_content_type_id(self):  # 获取ContentType ID
        return ContentType.objects.get_for_model(self).id

    def __str__(self):
        return self.name  # 定义模型的字符串表示

class ProductComment(BaseComment):  # 产品评论模型,继承自BaseComment
    class Meta:
        db_table = 'product_comments'  # 指定数据库表名

class ProductHistoryComment(BaseComment):  # 产品历史评论模型,继承自BaseComment
    class Meta:
        db_table = 'product_history_comments'  # 指定数据库表名

product/forms.py

# product/forms.py
from django import forms  # 导入Django的表单类
from .models import ProductComment, ProductHistoryComment  # 导入评论模型

class ProductCommentForm(forms.ModelForm):  # 产品评论表单
    class Meta:
        model = ProductComment  # 指定表单对应的模型
        fields = ['content']  # 表单包含的字段

class ProductHistoryCommentForm(forms.ModelForm):  # 产品历史评论表单
    class Meta:
        model = ProductHistoryComment  # 指定表单对应的模型
        fields = ['content']  # 表单包含的字段

product/views.py

# product/views.py
from django.shortcuts import render, redirect, get_object_or_404  # 导入Django的视图工具
from django.contrib.contenttypes.models import ContentType  # 导入ContentType
from .models import Product, ProductHistory, ProductComment, ProductHistoryComment  # 导入产品模型
from .forms import ProductCommentForm, ProductHistoryCommentForm  # 导入评论表单

def add_product_comment(request, content_type_id, object_id):  # 添加产品评论
    return handle_comment(request, ProductComment, ProductCommentForm, content_type_id, object_id)

def add_product_history_comment(request, content_type_id, object_id):  # 添加产品历史评论
    return handle_comment(request, ProductHistoryComment, ProductHistoryCommentForm, content_type_id, object_id)

def handle_comment(request, CommentModel, CommentForm, content_type_id, object_id):  # 处理添加评论
    content_type = ContentType.objects.get(id=content_type_id)  # 获取ContentType实例
    model_class = content_type.model_class()  # 获取对应的模型类
    obj = model_class.objects.get(id=object_id)  # 获取对应的对象

    if request.method == 'POST':  # 如果请求方法是POST
        form = CommentForm(request.POST)  # 创建表单实例
        if form.is_valid():  # 如果表单有效
            comment = form.save(commit=False)  # 暂不保存评论实例
            comment.user = request.user  # 设置评论的用户
            comment.content_type = content_type  # 设置评论的内容类型
            comment.object_id = obj.id  # 设置评论的对象ID
            comment.save()  # 保存评论
            return redirect(request.META.get('HTTP_REFERER', '/'))  # 重定向到上一页面
    else:
        form = CommentForm()  # 如果请求方法不是POST,创建空表单实例

    comments = CommentModel.objects.filter(content_type=content_type, object_id=obj.id)  # 获取对象的所有评论
    return render(request, 'product/comment_list.html', {'form': form, 'comments': comments, 'object': obj})  # 渲染评论列表页面

def product_detail(request, product_id):  # 产品详情视图
    product = get_object_or_404(Product, id=product_id)  # 获取产品对象,如果不存在则返回404
    comments = product.get_comments()  # 获取产品的所有评论
    form = ProductCommentForm()  # 创建空的评论表单实例
    return render(request, 'product/product_detail.html', {'product': product, 'comments': comments, 'form': form})  # 渲染产品详情页面

def product_history_detail(request, history_id):  # 产品历史详情视图
    product_history = get_object_or_404(ProductHistory, id=history_id)  # 获取历史记录对象,如果不存在则返回404
    comments = product_history.get_comments()  # 获取历史记录的所有评论
    form = ProductHistoryCommentForm()  # 创建空的历史评论表单实例
    return render(request, 'product/product_history_detail.html', {'product_history': product_history, 'comments': comments, 'form': form})  # 渲染历史记录详情页面

product/urls.py

# product/urls.py
from django.urls import path  # 导入路径函数
from .views import add_product_comment, add_product_history_comment, product_detail, product_history_detail  # 导入视图函数

urlpatterns = [
    path('product/<int:product_id>/', product_detail, name='product_detail'),  # 产品详情页面URL
    path('product_history/<int:history_id>/', product_history_detail, name='product_history_detail'),  # 产品历史详情页面URL
    path('add_product_comment/<int:content_type_id>/<int:object_id>/', add_product_comment, name='add_product_comment'),  # 添加产品评论URL
    path('add_product_history_comment/<int:content_type_id>/<int:object_id>/', add_product_history_comment, name='add_product_history_comment'),  # 添加产品历史评论URL
]

配置项目URL

myproject/urls.py

# myproject/urls.py
from django.contrib import admin  # 导入Django的admin工具
from django.urls import path, include  # 导入路径和包含函数

urlpatterns = [
    path('admin/', admin.site.urls),  # 管理界面URL
    path('comments/', include('comments.urls')),  # 评论应用的URL
    path('blog/', include('blog.urls')),  # 博客应用的URL
    path('product/', include('product.urls')),  # 产品应用的URL
]

创建和迁移数据库

在项目的根目录下运行以下命令以创建和迁移数据库:

python manage.py makemigrations comments blog product  # 创建迁移文件
python manage.py migrate  # 迁移数据库

创建超级用户

运行以下命令创建超级用户:

python manage.py createsuperuser  # 创建超级用户

启动开发

服务器

运行以下命令启动开发服务器:

python manage.py runserver  # 启动开发服务器

访问管理界面

打开浏览器并访问 http://127.0.0.1:8000/admin,使用超级用户帐户登录以管理数据和评论。

总结

以上是一个详细注释的Django项目结构,包含了一个可复用的评论系统和两个示例应用 (blogproduct)。每个应用都有自己的评论模型和历史评论模型,确保了评论数据的独立和可管理性。通过这种方式,你可以轻松地扩展项目并添加更多具有独立评论功能的应用。