1

私が探しているのは、タグ付けされていないオブジェクトを含む QuerySet です。

これまでに思いついた解決策は、私には非常に複雑に見えます。

# Get all tags for model
tags = Location.tags.all().order_by('name')

# Get a list of tagged location id's
tag_list = tags.values_list('name', flat=True)
tag_names = ', '.join(tag_list)
tagged_locations = Location.tagged.with_any(tag_names) \
                                  .values_list('id', flat=True)

untagged_locations = []
for location in Location.objects.all():
    if location.id not in tagged_locations:
        untagged_locations.append(location)

改善のためのアイデアはありますか?ありがとう!

4

3 に答える 3

3

この投稿には良い情報がいくつかあるので、削除する必要はないと思いますが、もっと簡単な解決策があります

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 を使用していない場合は適宜調整してください。

于 2009-11-30T19:16:17.070 に答える
0

これを試して:

[location for location in Location.objects.all() if location.tags.count() == 0]
于 2009-11-30T19:10:20.393 に答える
0

Locationクラスがtagging.fields.TagFieldユーティリティを使用すると仮定します。

from tagging.fields import TagField
class Location(models.Model):
    tags = TagField()

これを行うことができます:

Location.objects.filter(tags='')
于 2009-12-01T16:50:19.823 に答える