简体   繁体   中英

Filtering queryset by number of many-to-one relationships with a raw SQL query

I am creating a resources page, and I have the following models:

class Tag(models.Model):
    title = models.CharField(max_length=50, blank=False, unique=True, error_messages={'unique':"THis tag already exists."}) 

class Link(models.Model):
    title = models.CharField(max_length=100, blank=False)
    tag = models.ForeignKey(Tag, on_delete=models.CASCADE, null=False, blank=False)

Which have more fields but they are irrelevant to this matter.

The problem with this resources page was that Tags with no Link establishing relationship with them were still appearing, and I wanted only for tags with at least one Link under them to appear.

Now, I have managed to do this with this queryset filtering:

tags = Tag.objects.all().filter(link__isnull=False).order_by("title").distinct()

Which works alright in my views.py, and what I want is done already. Note that the "distinct" is necessary so that it doesn't return duplicate queries.

I have had very little experience with MySQL and only have built very simple applications with it and PHP. I have now idea how it would function under SQLite, nor would I even know how to this with MySQL.

Under my (very informal) logic, it would function something like this (please, forgive my killing the language):

SELECT * FROM Tags WHERE NUMBER_OF_RELATIONSHIPS IS GREATER THAN 0

My question is actually how could this be achieved through a Raw SQL query, be it under SQLite or MySQL?

You want to get all tags that have more than one relationship (ie Links), correct? If so, you can use this as your raw SQL:

SELECT *
FROM <Tag_TableName>
WHERE <Tag_PK_Field> IN (
  -- Get Tag Ids with more than one entry in Link table
  SELECT <Tag_FK_Field>
  FROM <Link_TableName>
  HAVING COUNT(*) >= 1 -- Only return rows that have multiple entries per same tag
  GROUP BY <Tag_FK_Field>
)

If that doesn't work, you may need to wrap the inner SELECT in another SELECT in order to filter out "tags" with more than one "link" entry.

I think Django defaults PK-field names to "id", so your actual raw query is probably closer to this:

SELECT *
FROM "<proj_name>_tag"
WHERE "id" IN (
  SELECT "id"
  FROM "<proj_name>_link"
  HAVING COUNT(*) >=1
  GROUP BY "id"
)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM