W1ldPo1nter / django-queryable-properties

Write Django model properties that can be used in database queries.
BSD 3-Clause "New" or "Revised" License
71 stars 1 forks source link

Bug on annotated queryable property over relation #2

Closed bpereto closed 3 years ago

bpereto commented 3 years ago

Hi,

run in this issue today. Any idea, why my approach doesn't work? In this case the annotated field seems to be interpreted as a lookup.

>>> Host.objects.filter(ansiblefacts__ansiblefactspackages__nevra="B")
Traceback (most recent call last):
  File "/usr/lib64/python3.6/code.py", line 91, in runcode
    exec(code, self.locals)
  File "<console>", line 1, in <module>
  File "/opt/app-root/lib/python3.6/site-packages/django/db/models/manager.py", line 82, in manager_method
    return getattr(self.get_queryset(), name)(*args, **kwargs)
  File "/opt/app-root/lib/python3.6/site-packages/django/db/models/query.py", line 892, in filter
    return self._filter_or_exclude(False, *args, **kwargs)
  File "/opt/app-root/lib/python3.6/site-packages/django/db/models/query.py", line 910, in _filter_or_exclude
    clone.query.add_q(Q(*args, **kwargs))
  File "/opt/app-root/lib/python3.6/site-packages/django/db/models/sql/query.py", line 1290, in add_q
    clause, _ = self._add_q(q_object, self.used_aliases)
  File "/opt/app-root/lib/python3.6/site-packages/django/db/models/sql/query.py", line 1318, in _add_q
    split_subq=split_subq, simple_col=simple_col,
  File "/opt/app-root/lib/python3.6/site-packages/django/db/models/sql/query.py", line 1251, in build_filter
    condition = self.build_lookup(lookups, col, value)
  File "/opt/app-root/lib/python3.6/site-packages/django/db/models/sql/query.py", line 1107, in build_lookup
    raise FieldError('Related Field got invalid lookup: {}'.format(lookup_name))
django.core.exceptions.FieldError: Related Field got invalid lookup: nevra

Model:

class AnsibleFactsPackages(AnsibleFactsBase):
    '''
    ansible facts package model object
    describes an package from a system
    '''
    sha = models.CharField(max_length=50, null=True, blank=True)
    name = models.CharField(max_length=255)
    epoch = models.CharField(max_length=6, null=True, blank=True)
    version = models.CharField(max_length=255)
    release = models.CharField(max_length=255)
    arch = models.CharField(max_length=20)
    group = models.CharField(max_length=100, null=True, blank=True)

    objects = QueryablePropertiesManager()

    def __str__(self):
        return self.nevra

    @queryable_property
    def nevra(self):
        '''
        return nevra notation of package
        '''
        return '{}-{}-{}.{}'.format(self.name, self.version, self.release, self.arch)

    @nevra.annotater
    @classmethod
    def nevra(cls):
        return Concat('name', V('-'), 'version', V('-'), 'release', V('.'), 'arch', output_field=models.CharField())
W1ldPo1nter commented 3 years ago

I'm somewhat suspicious about the lack of the presence of any queryable_properties source in the stack trace.

Since you are querying using the manager of a Host model (which is not part of your provided model definition), does this Host model also use a QueryablePropertiesManager? If not, then that's likely the issue right there. You need to use a QueryablePropertiesManager in all places that you want to make queries that utilize queryable properties from (even if the model itself does not define queryable properties and you only want to use queryable properties on other models via relations). This is because the property-resolving and auto-annotating logic is part of the queryset/query extension and is therefore only present if you explicitly use a QueryablePropertiesManager/QueryablePropertiesQuerySet.

The relevant part of the docs can be found here:

Using the special manager/queryset class may not only be important for models that define queryable properties. Since most features of queryable properties can also be used on related models in queryset operations, the manager is required whenever queryable property functionality should be offered, even if the corresponding model doesn’t implement its own queryable properties.

Let me know if this solves your problem or if there is another issue than the one I'm suspecting.

bpereto commented 3 years ago

Didn't notice the relevant part in the docs. With enabling QueryablePropertiesManager in the required (related) models it works like a charm.