2015-08-24 1 views
1

J'ai essayé de trouver une réponse à ma question pendant un moment, mais je ne trouve pas exactement ce dont j'ai besoin.Les objets filtrés par ManyToManyField_in ne contiennent pas le reste des valeurs de champs ManyToMany, comment les obtenir?

J'ai récemment commencé à jouer avec Django (j'ai recherché les docs), ce qui se passait bien jusqu'à maintenant. Je fais une application de galerie simple, l'idée est simple, chaque image a un tas de tags. Je veux construire une liste de tags (pas là de problème), puis en sélectionnant une étiquette - obtenir une liste des balises connexes à travers toutes les images - là, j'ai un problème, jetez un oeil à mon code de vue:

def getTagList(request): 
    images = Image.objects.all() 
    imgsWithTag = images.filter(tags__name__in=["space"]).only('tags') 
    allTags = imgsWithTag.values_list('tags__name', flat=True) 

ici, il est - je m'y attendais Alltags être une liste de toutes les balises d'image (y compris « espace » comme dans l'exemple) - au lieu que je reçois:

[u'space', u'space'] 

pour les deux images que j'ai dans mon DB. Chaque image contenant deux balises ('espace' étant le commun), mais seule la balise 'appariée' a été renvoyée pour autant que je sache.

Que se passe-t-il exactement ici & comment l'éviter? Y a-t-il une meilleure façon de faire ce que j'essaie de faire?

mes modèles sont les suivants:

class Tag(models.Model): 
    name = models.CharField(max_length=60, primary_key=True) 
    def __unicode__(self): 
     return self.name 

class Image(models.Model): 
    title = models.CharField(max_length=60) 
    image = models.ImageField(upload_to="images/") 
    tags = models.ManyToManyField(Tag, blank=True) 
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) 

    def thumbnail(self): 
     return '<img src="%s" height="40" />' % (self.image.url) 
    thumbnail.short_description = 'Image' 
    thumbnail.allow_tags = True 

    def get_tags(self): 
     return ", ".join([x.name for x in self.tags.all()]) 

    def url(self): 
     return self.image.url 

    def __unicode__(self): 
     return self.title 

Merci.

Répondre

0

Ceci est un comportement normal, car sous le capot django fait une requête SQL avec JOIN.

Pour obtenir tous les tags associés, vous pouvez utiliser (en supposant: votre modèle d'étiquette est nommé Tag et related_name pour ManyToMany joindre des images avec des étiquettes est images:

def getTagList(request): 
    images = Image.objects.all() 
    imgsWithTag = images.filter(tags__name__in=["space"]) 
    allTags = Tag.objects.filter(images__in=imgsWithTag).values_list('tags__name', flat=True) 

Au début, il ressemble django fera 2 requêtes : un pour obtenir toutes les images et ensuite le second pour obtenir toutes les balises associées à ces images, mais ce n'est pas le cas

Django est assez intelligent pour joindre ces requêtes ensemble, donc en résultat vous obtiendrez une requête avec select imbriqué sur WHERE Ainsi, vous ne rencontrerez que la base de données o nce. Si vous voulez qu'il soit appelé deux fois (parfois c'est mieux), vous pouvez faire:

def getTagList(request): 
    images = Image.objects.all() 
    imgsWithTag = images.filter(tags__name__in=["space"]).values_list('id', flat=True) 
    allTags = Tag.objects.filter(images__id__in=imgsWithTag).values_list('tags__name', flat=True) 
+0

Merci pour votre réponse. Cependant, je n'ai pas de champ associant chaque tag à une liste d'images - cette idée ne me semble pas juste. L'approche Id ne fonctionnera pas dans ce cas également. – Nisk

+0

Oui, sauf si vous avez spécifié 'related_name = '+'' dans le champ ManyToMany liant Image avec Tag. Regardez [docs] (https://docs.djangoproject.com/fr/1.8/ref/models/fields/#django.db.models.ManyToManyField.related_name) – GwynBleidD