我看过使用额外和/或注释进行查询但是无法获得我想要的结果。
我想获得一个产品列表,其中包含有效许可证以及可用许可证总数。活动许可证定义为在日期不过时,许可证数量减去分配的许可证数量(由manytomany字段上的计数定义)。
我定义的模型是:
class Vendor(models.Model):
name = models.CharField(max_length=200)
url = models.URLField(blank=True)
class Product(models.Model):
name = models.CharField(max_length=200)
vendor = models.ForeignKey(Vendor)
product_url = models.URLField(blank=True)
is_obsolete = models.BooleanField(default=False, help_text="Is this product obsolete?")
class License(models.Model):
product = models.ForeignKey(Product)
num_licenses = models.IntegerField(default=1, help_text="The number of assignable licenses.")
licensee_name = models.CharField(max_length=200, blank=True)
license_key = models.TextField(blank=True)
license_startdate = models.DateField(default=date.today())
license_enddate = models.DateField(null=True, blank=True)
is_obsolete = models.BooleanField(default=False, help_text="Is this licenses obsolete?")
licensees = models.ManyToManyField(User, blank=True)
我尝试过许可模型过滤。哪个有效,但我不知道如何整理/ GROUP BY /将返回的数据聚合到返回的单个查询集中。
当尝试通过procuct过滤时,我可以完全弄清楚我需要做的查询。我可以得到点点滴滴,并尝试使用.extra()
select=
查询来返回可用许可证的数量(这是我此时真正需要的),其中将有多个许可证与一个产品。
因此,我追求的最终答案是,如何检索Django中可用许可证数量的可用产品列表。我宁愿不尽可能地使用raw。
获取我想要的所有许可证详细信息的示例查询集,我无法获得该产品:
License.objects.annotate(
used_licenses=Count('licensees')
).extra(
select={
'avail_licenses': 'licenses_license.num_licenses - (SELECT count(*) FROM licenses_license_licensees WHERE licenses_license_licensees.license_id = licenses_license.id)'
}
).filter(
is_obsolete=False,
num_licenses__gt=F('used_licenses')
).exclude(
license_enddate__lte=date.today()
)
提前谢谢你。
编辑(2014-02-11): 我想我可能是以一种丑陋的方式解决了它。如果可以的话,我不想做太多的DB调用,所以我使用License查询获取所有信息,然后在Python中过滤它并从管理器类中返回所有信息。也许过度使用Dict和列表。无论如何,它可以工作,我可以在以后使用其他信息扩展它,而不会产生大量风险或自定义SQL。它还使用了我在模型类中定义的一些模型参数。
class LicenseManager(models.Manager):
def get_available_products(self):
licenses = self.get_queryset().annotate(
used_licenses=Count('licensees')
).extra(
select={
'avail_licenses': 'licenses_license.num_licenses - (SELECT count(*) FROM licenses_license_licensees WHERE licenses_license_licensees.license_id = licenses_license.id)'
}
).filter(
is_obsolete=False,
num_licenses__gt=F('used_licenses')
).exclude(
license_enddate__lte=date.today()
).prefetch_related('product')
products = {}
for lic in licenses:
if lic.product not in products:
products[lic.product] = lic.product
products[lic.product].avail_licenses = lic.avail_licenses
else:
products[lic.product].avail_licenses += lic.avail_licenses
avail_products = []
for prod in products.values():
if prod.avail_licenses > 0:
avail_products.append(prod)
return avail_products
编辑(2014-02-12): 好的,这是我决定采用的最终解决方案。使用Python过滤结果。减少缓存调用,并具有恒定数量的SQL查询。
这里的教训是,对于具有多级过滤的内容,最好尽可能多地获取,并在返回时使用Python进行过滤。
class ProductManager(models.Manager):
def get_all_available(self, curruser):
"""
Gets all available Products that are available to the current user
"""
q = self.get_queryset().select_related().prefetch_related('license', 'license__licensees').filter(
is_obsolete=False,
license__is_obsolete=False
).exclude(
license__enddate__lte=date.today()
).distinct()
# return a curated list. Need further information first
products = []
for x in q:
x.avail_licenses = 0
x.user_assigned = False
# checks licenses. Does this on the model level as it's cached so as to save SQL queries
for y in x.license.all():
if not y.is_active:
break
x.avail_licenses += y.available_licenses
if curruser in y.licensees.all():
x.user_assigned = True
products.append(x)
return q
答案 0 :(得分:1)
一种策略是从许可证查询集中获取所有产品ID:
productIDList = list(License.objects.filter(...).values_list(
'product_id', flat=True))
然后使用该ID列表查询产品:
Product.objects.filter(id__in=productIDList)