Django根据ForeignKey和manytomy子查询的结果进行过滤

2024-05-17 02:54:45 发布

您现在位置:Python中文网/ 问答频道 /正文

我曾经考虑过使用一个额外的和/或注释来执行查询,但是没有得到我想要的结果。在

我想得到一个产品列表,其中有活动的许可证和可用许可证的总数。活动许可证被定义为在日期内未过时,且许可证数减去分配的许可证数(由manytomy字段上的计数定义)。在

我定义的模型是:

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)

我试过按许可证模式过滤。这是可行的,但我不知道如何将返回的数据整理/分组/聚合到返回的单个查询集中。在

当尝试按product进行筛选时,我可以很好地找出需要执行的查询。我可以得到零碎的信息,并尝试使用.extra()select=查询返回可用许可证的数量(这正是我目前真正需要的),其中有多个许可证将与一个产品关联。在

因此,我想要的最终答案是,我如何检索一个可用产品列表以及Django中可用许可证的数量。我宁愿尽量不要用生的。在

示例queryset获取了我想要的所有许可证详细信息,但我无法获取产品:

^{pr2}$

提前谢谢你。在

编辑(2014-02-11): 我想我已经用一种丑陋的方式解决了它。如果可以的话,我不想进行太多的DB调用,所以我使用一个许可证查询获取所有信息,然后用Python对其进行过滤并从manager类中返回所有信息。可能是过度使用了Dict和list。不管怎么说,它是有效的,我可以在以后用附加信息扩展它,而不需要很大的风险或定制SQL。它还使用了我在model类中定义的一些models参数。在

类许可证管理器(模特。经理)公司名称:

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

Tags: infalsetrueislicensemodelsproductproducts
1条回答
网友
1楼 · 发布于 2024-05-17 02:54:45

一种策略是从许可证查询集中获取所有产品标识:

    productIDList = list(License.objects.filter(...).values_list(
        'product_id', flat=True))

然后使用该ID列表查询产品:

^{pr2}$

相关问题 更多 >