Frage

class Tag(models.Model):
  name = models.CharField(maxlength=100)

class Blog(models.Model):
  name = models.CharField(maxlength=100)
  tags =  models.ManyToManyField(Tag)

Einfache Modelle nur meine Frage zu stellen.

Ich frage mich, wie kann ich Blogs mit Hilfe von Tags auf zwei verschiedene Arten abgefragt werden.

  • Blog-Einträge, die mit "tag1" oder "tag2" markiert sind: Blog.objects.filter(tags_in=[1,2]).distinct()
  • Blog-Objekte, die mit "tag1" markiert sind und "tag2":
  • Blog-Objekte, die mit genau "tag1" markiert sind und "tag2" und nichts anderes: ??

Tag und Blog ist nur ein Beispiel verwendet wird.

War es hilfreich?

Lösung

Sie könnten Q verwenden, um Objekte für # 1:

# Blogs who have either hockey or django tags.
from django.db.models import Q
Blog.objects.filter(
    Q(tags__name__iexact='hockey') | Q(tags__name__iexact='django')
)

Gewerkschaften und Kreuzungen, glaube ich, sind ein wenig außerhalb des Bereichs des Django ORM, aber seine möglich auf diese. Die folgenden Beispiele sind von einer gerufenen django-Tagging , die die Funktionalität bereitstellt. Linie 346 von models.py :

Für Teil zwei, die Sie für eine Vereinigung von zwei Abfragen suchen, im Grunde

def get_union_by_model(self, queryset_or_model, tags):
    """
    Create a ``QuerySet`` containing instances of the specified
    model associated with *any* of the given list of tags.
    """
    tags = get_tag_list(tags)
    tag_count = len(tags)
    queryset, model = get_queryset_and_model(queryset_or_model)

    if not tag_count:
        return model._default_manager.none()

    model_table = qn(model._meta.db_table)
    # This query selects the ids of all objects which have any of
    # the given tags.
    query = """
    SELECT %(model_pk)s
    FROM %(model)s, %(tagged_item)s
    WHERE %(tagged_item)s.content_type_id = %(content_type_id)s
      AND %(tagged_item)s.tag_id IN (%(tag_id_placeholders)s)
      AND %(model_pk)s = %(tagged_item)s.object_id
    GROUP BY %(model_pk)s""" % {
        'model_pk': '%s.%s' % (model_table, qn(model._meta.pk.column)),
        'model': model_table,
        'tagged_item': qn(self.model._meta.db_table),
        'content_type_id': ContentType.objects.get_for_model(model).pk,
        'tag_id_placeholders': ','.join(['%s'] * tag_count),
    }

    cursor = connection.cursor()
    cursor.execute(query, [tag.pk for tag in tags])
    object_ids = [row[0] for row in cursor.fetchall()]
    if len(object_ids) > 0:
        return queryset.filter(pk__in=object_ids)
    else:
        return model._default_manager.none()

Für einen Teil # 3 Ich glaube, du bist für eine Kreuzung suchen. Siehe Linie 307 von models.py

def get_intersection_by_model(self, queryset_or_model, tags):
    """
    Create a ``QuerySet`` containing instances of the specified
    model associated with *all* of the given list of tags.
    """
    tags = get_tag_list(tags)
    tag_count = len(tags)
    queryset, model = get_queryset_and_model(queryset_or_model)

    if not tag_count:
        return model._default_manager.none()

    model_table = qn(model._meta.db_table)
    # This query selects the ids of all objects which have all the
    # given tags.
    query = """
    SELECT %(model_pk)s
    FROM %(model)s, %(tagged_item)s
    WHERE %(tagged_item)s.content_type_id = %(content_type_id)s
      AND %(tagged_item)s.tag_id IN (%(tag_id_placeholders)s)
      AND %(model_pk)s = %(tagged_item)s.object_id
    GROUP BY %(model_pk)s
    HAVING COUNT(%(model_pk)s) = %(tag_count)s""" % {
        'model_pk': '%s.%s' % (model_table, qn(model._meta.pk.column)),
        'model': model_table,
        'tagged_item': qn(self.model._meta.db_table),
        'content_type_id': ContentType.objects.get_for_model(model).pk,
        'tag_id_placeholders': ','.join(['%s'] * tag_count),
        'tag_count': tag_count,
    }

    cursor = connection.cursor()
    cursor.execute(query, [tag.pk for tag in tags])
    object_ids = [row[0] for row in cursor.fetchall()]
    if len(object_ids) > 0:
        return queryset.filter(pk__in=object_ids)
    else:
        return model._default_manager.none()

Andere Tipps

Ich habe diese aus mit Django getestet 1.0:

Die "oder" Abfragen:

Blog.objects.filter(tags__name__in=['tag1', 'tag2']).distinct()

oder Sie können die Q-Klasse verwenden:

Blog.objects.filter(Q(tags__name='tag1') | Q(tags__name='tag2')).distinct()

Die "und" Abfrage:

Blog.objects.filter(tags__name='tag1').filter(tags__name='tag2')

ich über die dritten nicht sicher bin, müssen Sie wahrscheinlich auf SQL fallen, es zu tun.

Bitte nicht das Rad neu zu erfinden und verwenden django-Tagging-Anwendung , die waren genau für Ihren Anwendungsfall gemacht. Es kann alle Anfragen tun Sie beschreiben, und vieles mehr.

Wenn Sie benutzerdefinierte Felder zu Ihrem Tag Modell hinzufügen möchten, können Sie auch einen Blick auf meine Zweig der django-Tagging .

Damit wird der Trick für Sie tun

Blog.objects.filter(tags__name__in=['tag1', 'tag2']).annotate(tag_matches=models.Count(tags)).filter(tag_matches=2)
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top