この投稿には良い情報がいくつかあるので、削除する必要はないと思いますが、もっと簡単な解決策があります
django タグ付けのソース コードをざっと見てみました。ContentType フレームワークと一般的な関係を使用してそれを実現しているようです。
このため、 Location クラスで一般的な逆関係を作成して、特定の場所の TaggedItem オブジェクトに簡単にアクセスできるようにする必要があります (まだ行っていない場合)。
from django.contrib.contenttypes import generic
from tagging.models import TaggedItem
class Location(models.Model):
...
tagged_items = generic.GenericRelation(TaggedItem,
object_id_field="object_id",
content_type_field="content_type")
...
明確化
私の元の答えはこれを行うことを提案しました:
untagged_locs = Location.objects.filter(tagged_items__isnull=True)
これは「通常の結合」では機能しますが、実際にはここでは機能しません。これは、コンテンツ タイプ フレームワークが次content_type_id
の SQL に対して追加のチェックをスローするためですisnull
。
SELECT [snip] FROM `sotest_location`
LEFT OUTER JOIN `tagging_taggeditem`
ON (`sotest_location`.`id` = `tagging_taggeditem`.`object_id`)
WHERE (`tagging_taggeditem`.`id` IS NULL
AND `tagging_taggeditem`.`content_type_id` = 4 )
次のように逆にすることで、ハックアラウンドできます。
untagged_locs = Location.objects.exclude(tagged_items__isnull=False)
しかし、それはあまり正しくありません。
私もこれを提案しましたが、コンテンツ タイプ フレームワークでアノテーションが期待どおりに機能しないという指摘がありました。
from django.db.models import Count
untagged_locs = Location.objects.annotate(
num_tags=Count('tagged_items')).filter(num_tags=0)
上記のコードは私の限られたテスト ケースでは機能しますが、モデルに他の「タグ付け可能な」オブジェクトがある場合、バグが発生する可能性があります。その理由は、チケットcontent_type_id
で概説されているようにチェックしないためです。次の SQL が生成されました。
SELECT [snip], COUNT(`tagging_taggeditem`.`id`) AS `num_tags`
FROM `sotest_location`
LEFT OUTER JOIN `tagging_taggeditem`
ON (`sotest_location`.`id` = `tagging_taggeditem`.`object_id`)
GROUP BY `sotest_location`.`id` HAVING COUNT(`tagging_taggeditem`.`id`) = 0
ORDER BY NULL
Location
がタグ付け可能な唯一のオブジェクトである場合、上記は機能します。
提案された回避策
注釈メカニズムを機能させることはできませんが、当面は次のことを行います。
untagged_locs_e = Location.objects.extra(
where=["""NOT EXISTS(SELECT 1 FROM tagging_taggeditem ti
INNER JOIN django_content_type ct ON ti.content_type_id = ct.id
WHERE ct.model = 'location'
AND ti.object_id = myapp_location.id)"""]
)
これにより、追加の WHERE 句が SQL に追加されます。
SELECT [snip] FROM `myapp_location`
WHERE NOT EXISTS(SELECT 1 FROM tagging_taggeditem ti
INNER JOIN django_content_type ct ON ti.content_type_id = ct.id
WHERE ct.model = 'location'
AND ti.object_id = myapp_location.id)
django_content_type
タグ付け可能なモデル タイプが複数ある場合に、モデルに適切なコンテンツ タイプを表示していることを確認するために、テーブルに結合します。
myapp_location.id
テーブル名に合わせて変更してください。テーブル名をハードコーディングしないようにする方法はおそらくありますが、それが重要な場合はそれを理解することができます。
MySQL を使用していない場合は適宜調整してください。