我有两个分别称为Post
和Comment
的模型,以及一个可以对该文章或评论进行增票或减票的User
模型。我想限制每个用户只能对某个Post
或Comment
进行一次投票。
我可以在模型级别上对Post
和Comment
做些事情,取而代之的是对特定帖子或评论进行总投票。但是,我认为创建一个单独的Vote
模型具有更大的可扩展性,其中:
SELECT count(*) FROM votes where user_id=
,等等...)来计算总投票数unique_together
对每个用户投票一次目前我已经弄清楚了类似的东西:
import uuid
from django.db import models
from django.contrib.auth import get_user_model
from django.db import models
# Create your models here.
class Post(models.Model):
LINK = "link"
VIDEO = "video"
IMAGE = "image"
POST_TYPE_CHOICES = ((LINK, "Link"), (VIDEO, "Video"), (IMAGE, "Image"))
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
created = models.DateTimeField(auto_now_add=True)
title = models.CharField(max_length=300)
url = models.URLField(max_length=300)
category = models.CharField(max_length=50)
score = models.DecimalField(default=0, max_digits=20, decimal_places=2)
votes = models.IntegerField(default=0)
views = models.IntegerField(default=0)
post_type = models.CharField(max_length=5, choices=POST_TYPE_CHOICES, default=LINK)
text = models.CharField(max_length=40000)
owner = models.ForeignKey('users.User', related_name='posts', on_delete=models.CASCADE)
def __str__(self):
return self.title
class Meta:
ordering = ["-created"]
class PostVote(models.Model):
DOWNVOTE = -1
UPVOTE = 1
UNVOTE = 0
VOTE_TYPE_CHOICES = ((DOWNVOTE, "Downvote"), (UPVOTE, "Upvote"), (UNVOTE, "Unvote"))
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
created = models.DateTimeField(auto_now_add=True)
score = models.IntegerField(choices=VOTE_TYPE_CHOICES, default=UPVOTE)
voter = models.ForeignKey('users.User', related_name='post_votes', on_delete=models.CASCADE)
class Comment(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
created = models.DateTimeField(auto_now_add=True)
body = models.CharField(max_length=10000)
post = models.ForeignKey('Post', related_name='comments', on_delete=models.CASCADE)
owner = models.ForeignKey('users.User', related_name='comments', on_delete=models.CASCADE)
class Meta:
ordering = ["-created"]
class CommentVote(models.Model):
DOWNVOTE = -1
UPVOTE = 1
UNVOTE = 0
VOTE_TYPE_CHOICES = ((DOWNVOTE, "Downvote"), (UPVOTE, "Upvote"), (UNVOTE, "Unvote"))
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
created = models.DateTimeField(auto_now_add=True)
score = models.IntegerField(choices=VOTE_TYPE_CHOICES, default=UPVOTE)
voter = models.ForeignKey('users.User', related_name='comment_votes', on_delete=models.CASCADE)
def __str__(self):
return self.score
class Meta:
ordering = ["-created"]
[我只知道这是将投票分为PostVote
和CommentVote
的糟糕模型,而是想要一个更通用的Vote
模型,在该模型中我可以将投票指定为Post
或Comment
]。
[我不想要的是同时具有Vote
和post_id
的comment_id
模型,使得其中一个始终为null,因为对于Comment
始终是一票或一个Post
。
有帮助吗?
这可以通过使用Generic Relations
更好地解决。这篇https://simpleisbetterthancomplex.com/tutorial/2016/10/13/how-to-use-generic-relations.html是一篇不错的文章,可以理解这个概念,这里的示例与您的要求类似。
[我相信,在您的示例中,一个重要的缺失之处是投票不知道投票属于哪个Post
或Comment
。
您可以使用Vote
拥有单个generic relation模型,就像您在问题中已经猜到的那样。实际上,这是一个外键,可以指向不同的模型。
一个例子可能是:
from django.contrib.contenttypes.fields import GenericForeignKey
from django.contrib.contenttypes.models import ContentType
from django.db import models
class Vote(models.Model):
# ...
# your model stuff
# ...
content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
voted_object_id = models.UUIDField(default=uuid.uuid4)
voted_object = models.GenericForeignKey('content_type', 'voted_object_id')
并且在Post
和Comment
中,为了能够通过查询访问“投票对象”的票(不会自动生成通用外键的反向关系:]]
from django.db import models from django.contrib.contenttypes.fields import GenericRelation class Post(models.Model): # ... # other stuff # ... votes = GenericRelation(Vote, related_query_name='post') class Comment(models.Model): # ... # other stuff # ... votes = GenericRelation(Vote, related_query_name='comment')
通过这种方式,您可以通过以下方式访问“投票的对象”(因此是Post或Comment):
post = Post.objects.all()[0] vote_to_post = Vote.objects.create(**vote_data, voted_object=post) print(vote_to_post.voted_object) # <Post: (0)> print(post.votes) # <QuerySet [<Vote: 0>]>
我相信,但是我没有机会测试,也可以这样做:
print(vote_to_post.post) # <Post: (0)>