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
所以在过去的几天里,我一直被一个设计问题所困扰,并且在其中投入了无数个小时,但无济于事。
我的问题是我希望 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