24
I’m trying to make a query in Django with several joins, and I came across this mistake that I don’t know what it means:
Tag.objects.filter(dset__descendant__entities__entity=e)
Fielderror: Relation Fields do not support nested lookups
My models on this query sane Tag
, Entity
, TaggedEntity
and EntityClosure
(is an EAV model, full code here). They represent the following:
Entity
is an entity. All it has are "name" and "details":class Entity(models.Model): name = models.TextField(null=True, blank=True, default=None) details = models.TextField(null=True, blank=True, default=None)
EntityClosure
is a table of relationships (entities form a tree):class EntityClosure(Model): ancestor = models.ForeignKey(Entity, related_name="dset") descendant = models.ForeignKey(Entity, related_name="aset") depth = models.PositiveSmallIntegerField()
Tag
is a "tag" associated with an entity. Each entity can have multiple tags, and the tags themselves are entities (i.e. they also form a tree):class Tag(Entity): pass class TaggedEntity(Model): entity = models.ForeignKey(Entity,related_name="tags") tag = models.ForeignKey(Tag,related_name="entities")
My query desired is: "select all tags associated with an entity"...
Tag.objects.filter(entities__entity=e) # Funciona
..."but taking into account the hierarchy" (i.e. also search for tags that are ancestors of tags associated with the entity):
Tag.objects.filter(dset__descendant__entities__entity=e) # Erro
I don’t see anything wrong with this query... but some problem is happening between the descendant
and the entities
which does not allow it to be done. The strange thing is that if I do two queries he accepts in a good:
>>> conj = [x.ancestors for x in Tag.objects.filter(entities__entity=d).annotate(ancestors=F('aset__ancestor'))]
>>> conj
[3, 1, 3, 1]
>>> Tag.objects.filter(pk__in=conj)
[<Tag: TipoDocumento>, <Tag: Recibo Fiscal>]
Here is a MCVE, if you want to test (the actual code is much more extensive).
Note also that - as this example in Sqlfiddle shows - the query I want to do is nothing big, I just don’t know why Django is having trouble putting it together:
select t.*
from tagged_entity te
join entity_closure ec on te.tag = ec.descendant
join entity t on t.id = ec.ancestor
where te.entity = 1;
So... what does this mean FieldError
presented, and how can I bypass it to do what I want with a single query?
Updating: after upgrading to Django 1.11 the error message has changed:
Fielderror: Related Field got invalid lookup: entities
Which is a bit more descriptive than the previous message. Possibly the error is in using __entities
- a relationship of Tag
- shortly after __descendant
- a relationship of EntityClosure
- since EntityClosure
does not relate to Tag
, but rather with Entity
. This would also explain why the two-way solution works: because the result of the first would be a set of entities, not of tags...
You’re trying to access
dset
directly fromTag
no relationship, or I’m wrong?– Paulo
@Orion I don’t understand.
dset
is therelated_name
countryEntityClosure.ancestor
. I can access it normally (ex.:Tag.objects.filter(dset__descendant=4)
returns all id tag ancestors4
). The problem is between thedescendant
and theentities
according to my tests, but I could be wrong.– mgibsonbr
I didn’t know that inheriting created this relationship, so I wondered.
– Paulo
@Orion Yes. From what I could see, if a model
Tag
inherits from a modelEntity
, then the table representing the tag will have instead of a fieldid
, a fieldentity_ptr
or something like that, which is both a primary key and a foreign key to the mother table. Thus, all relations of other tables with the mother table are also applicable to the child model, since it is represented by a row in the mother table and a row in the daughter table (having all columns of both), with the same primary key.– mgibsonbr
@mgibsonbr, reading the last comment on the creation of the mother table, if you did not want this table to be created, you should put a Meta class: Abstract = True in Entity. In this case Django does not create the mother table and all fields of it would be in the tag table in your case. This would decrease the number of joins to be made. But I would have to review the modeling in Entityclosure that has the foreignkey for Entity
– Puam Dias
@Puamdias Unfortunately it is important to me that the Entity table exists (because it is elements of it that will receive the tag). And anyway my goal is not to reduce the number of joins (my real query has even more joins rsrs), but to solve this problem of not being able to mount the query. If I have no solution, beauty, I can live with two queries instead of one, but the ideal would be to make the whole query in one query (and as you can see in the example SQL, it is nothing of the other world...).
– mgibsonbr