7

現在、基本的な時間記録アプリを構築しており、django-taggit を使用する todo モデルがあります。私の Todo モデルは次のようになります。

class Todo(models.Model):
    project = models.ForeignKey(Project)
    description = models.CharField(max_length=300)
    is_done = models.BooleanField(default=False)
    billable = models.BooleanField(default=True)
    date_completed = models.DateTimeField(blank=True, null=True)
    completed_by = models.ForeignKey(User, blank=True, null=True)
    tags = TaggableManager()

    def __unicode__(self):
        return self.description

プロジェクト内のすべての Todo の一意のタグのリストを取得しようとしていますが、集合内包表記を使用してこれを機能させることができましたが、プロジェクト内のすべての Todo について、データベースにクエリを実行してタグを取得する必要があります。私の集合理解は次のとおりです。

unique_tags = { tag.name.lower() for todo in project.todo_set.all() for tag in todo.tags.all() }

これは問題なく機能しますが、プロジェクト内のすべての todo に対して個別のクエリを実行して、すべてのタグを取得します。これらの重複したクエリを回避するために、 prefetch_related に似たことができる方法があるかどうか疑問に思っていました:

unique_tags = { tag.name.lower() for todo in project.todo_set.all().prefetch_related('tags') for tag in todo.tags.all() }

前のコードを実行すると、次のエラーが表示されます。

'tags' does not resolve to a item that supports prefetching - this is an invalid parameter to prefetch_related().

誰かがここで非常によく似た質問をしているのを見ました: Optimize django query to pull external key and django-taggit relationship, but it's like it'd like a strong answer. 誰かが私を助けてくれることを望んでいました。ありがとう!

4

4 に答える 4

3

少しハックなソリューション:

ct = ContentType.objects.get_for_model(Todo)
todo_pks = [each.pk for each in project.todo_set.all()]
tagged_items = TaggedItem.objects.filter(content_type=ct, object_id__in=todo_pks)   #only one db query
unique_tags = set([each.tag for each in tagged_items])

説明

taggit が内部で使用する TaggedItem と ContentType を使用する必要があったため、ハックだと言います。

Taggit は、特定のユース ケースに対応する方法を提供していません。理由はジェネリックだからです。taggit の意図は、任意のモデルの任意のインスタンスにタグを付けることができることです。したがって、そのために ContentType と GenericForeignKey を使用します。

taggit で内部的に使用されるモデルは、Tag と TaggedItem です。モデル タグには、タグの文字列表現のみが含まれます。TaggedItem は、これらのタグを任意のオブジェクトに関連付けるために使用されるモデルです。タグはどのオブジェクトにも関連付けられる必要があるため、TaggedItem はモデル ContentType を使用します。

tags.all()tags.add()などの taggitによって提供される API は、内部的にこのモデルの TaggedItem とフィルターを使用して、特定のインスタンスのタグを提供します。

したがって、あなたの要件は、taggit によって使用される内部クラスを利用しなければならなかったオブジェクトの特定のリストのすべてのタグを取得することです。

于 2013-03-27T10:21:16.390 に答える
1

django タグ付けとメソッド usage_for_modelを使用する

 def usage_for_model(self, model, counts=False, min_count=None, filters=None):
    """
    Obtain a list of tags associated with instances of the given
    Model class.

    If ``counts`` is True, a ``count`` attribute will be added to
    each tag, indicating how many times it has been used against
    the Model class in question.

    If ``min_count`` is given, only tags which have a ``count``
    greater than or equal to ``min_count`` will be returned.
    Passing a value for ``min_count`` implies ``counts=True``.

    To limit the tags (and counts, if specified) returned to those
    used by a subset of the Model's instances, pass a dictionary
    of field lookups to be applied to the given Model as the
    ``filters`` argument.
    """
于 2013-08-27T08:31:52.710 に答える
0

アクシャーの答えよりもハックの少ない答えですが、ほんのわずかです...

句を使用して、taged_item リレーションを自分でトラバースする限り、prefetch_related を使用できますprefetch_related('tagged_items__tag')。残念ながら、todo.tags.all()そのプリフェッチを利用することはできません - 「タグ」マネージャーは依然として独自のクエリを実行することになります - そのため、そこでも tagged_items リレーションをステップオーバーする必要があります。これは仕事をするはずです:

unique_tags = { tagged_item.tag.name.lower()
    for todo in project.todo_set.all().prefetch_related('tagged_items__tag')
    for tagged_item in todo.tagged_items.all() }
于 2013-10-03T15:44:38.613 に答える