DJANGO - 查询集和模型设计

DJANGO - Queryset and model design

所以在过去的几天里,我一直被一个设计问题所困扰,并且在其中投入了无数个小时,但无济于事。

我的问题是我希望 return 所有活跃的文章。我在模型中创建了一个方法,但是无法使用 .filter(is_active=True)这将是世界上最好的解决方案。

所以现在我在 ArticleManager 中将该方法变成了一个长过滤器,问题是我似乎无法找到一种对我有用的方式来计算当前点击次数。 (文章模型中的 current_clicks 方法是我的目标)。

Models.py

class ArticleManager(models.Manager):
    def get_queryset(self):
            return super(ArticleManager, self).get_queryset().filter(article_finish_date=None).filter(article_publish_date__lte=timezone.now()) 
#this is where i need something to the effect of .filter(article_max_clicks__gt=click_set.count())

    class Article(models.Model):
        article_name_text = models.CharField(max_length=200)
        article_max_clicks = models.IntegerField(default=0)
        article_creation_date = models.DateTimeField('date created')
        article_publish_date = models.DateTimeField('date published', null=True, blank=True)
        article_finish_date = models.DateTimeField('date finished', null=True, blank=True)
        def __str__(self):
            return self.article_name_text
        def is_active(self):
            if self.article_finish_date==None:
                if self.article_publish_date <= timezone.now():
                    return self.current_clicks() < self.article_max_clicks
                else:
                    return False
            else:
                return False
        def current_clicks(self):
            return self.click_set.count()
        is_active.boolean = True
        actives = ArticleManager()

class Click(models.Model):
    click_article = models.ForeignKey(Article, on_delete=models.CASCADE) 
    click_user = models.ForeignKey(User, on_delete=models.CASCADE)
    click_date = models.DateTimeField('date clicked')
    def __str__(self):
        return str(self.id) + " " + str(self.click_date)

如果有帮助的话,views.py 中的点击次数是这样产生的

article.click_set.create(click_article=article, click_user=user, click_date=timezone.now())

如果有人对我应该怎么做有任何想法,将不胜感激!

非常感谢,如果您需要更多信息,请告诉我!

Django 的 annotate functionality 非常适合在查询时添加属性。来自文档 -

可以使用 annotate() 子句生成每个对象的摘要。当指定 annotate() 子句时,QuerySet 中的每个对象都将使用指定的值进行注释。

为了保持您的查询性能,您可以在您的管理器中使用它,而不是为您的每个文章调用(可能非常慢)相关对象。一旦你有了带注释的 属性,你就可以在你的查询中使用它。由于 Django 仅在调用对象时执行您的查询,因此您可以使用此注释 而不是 计算 click_set,这将为每个相关项调用单独的查询。 current_clicks 方法可能对您仍然有用,但如果为多篇文章调用它,您的查询将很快加起来并导致很大的性能损失。

请注意 - 我在您的 click_article 字段中添加了一个 related_name of clicks 关键字参数,以便使用它代替 'click_set'。

此外,您会在下面的查询中看到 Q objects 的使用。这允许我们做的是将多个过滤器链接在一起。这些可以在使用 AND (,) / OR(|) 操作数时嵌套。因此,下面 Q 对象的读取将是:

查找文章发布日期早于现在且(文章没有完成日期或文章完成日期晚于现在)的所有文章

from django.db.models.query import Q,Count

class ArticleManager(models.Manager):
    def get_queryset(self):
        return super(ArticleManager, self).get_queryset().filter(
                    Q(article_publish_date__lte=timezone.now()),
                    ( Q(article_finish_date__isnull=True)|
                      Q(article_finish_date__gte=timezone.now())
                ).annotate(
                    click_count=Count('clicks')
                ).filter(
                    article_max_clicks__gt=click_count
                )

class Article(models.Model):
    actives = ArticleManager()
    def current_clicks(self):
        return self.clicks.count()

# Now you can call Article.actives.all() to get all active articles        

class Click(models.Model):
    click_article = models.ForeignKey(Article, on_delete=models.CASCADE, related_name='clicks') # added a related_name for more explicit calling of prefetch_related