django-tagging으로 태그가없는 물체를 검색합니다
-
10-07-2019 - |
문제
내가 찾고있는 것은 태그가없는 객체를 포함하는 쿼리 세트입니다.
지금까지 내가 생각 해낸 솔루션은 나에게 지나치게 복잡해 보입니다.
# 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)
개선을위한 아이디어가 있습니까? 감사!
해결책
이 게시물에는 좋은 정보가 있으므로 삭제해야한다고 생각하지 않지만 훨씬 간단한 솔루션이 있습니다.
나는 django-tagging의 소스 코드를 빠르게 들여다 보았다. ContentType 프레임 워크와 일반적인 관계를 사용하여이를 끌어 올리는 것처럼 보입니다.
이 때문에 일반적인 역 관계 아직 수행하지 않은 경우 주어진 위치에 대한 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)
위의 코드는 제한된 테스트 케이스에서 저에게 효과적이지만 모델에 다른 'Taggable'객체가 있으면 버그가 될 수 있습니다. 그 이유는 그것이 확인하지 않기 때문입니다 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)"""]
)
이것은 SQL에 추가 WHER 절을 추가합니다.
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을 사용하지 않으면 그에 따라 조정하십시오.
다른 팁
이 시도:
[location for location in Location.objects.all() if location.tags.count() == 0]
당신의 가정 Location
클래스를 사용합니다 tagging.fields.TagField
공익사업.
from tagging.fields import TagField
class Location(models.Model):
tags = TagField()
당신은 이것을 할 수 있습니다 :
Location.objects.filter(tags='')