Unrecognized field type GeneratedField
roniemartinez opened this issue · 12 comments
I've got this error which is caused by using a GeneratedField. Before migrating to GeneratedField, we used to use a migration script to create the generated field and django-filter has no issue with it.
AssertionError: MyFilter resolved field 'my_field' with 'exact' lookup to an unrecognized field type GeneratedField. Try adding an override to 'Meta.filter_overrides'. See: https://django-filter.readthedocs.io/en/main/ref/filterset.html#customise-filter-generation-with-filter-overrides
I think it should be possible to know what filter to use since GeneratedField
has an output_field
.
@roniemartinez Could you put together a proof-of-concept showing what you mean here, perhaps with tests showing different examples? Thanks.
If you have a field in your model like:
code_full = models.GeneratedField(
expression=If(Q(code_number__isnull=True), Value(None), Concat("code_prefix", "code_number")),
db_persist=True,
output_field=models.CharField(blank=True, null=True, max_length=15),
)
Then an error like @roniemartinez posted above will be raised.
This can be fixed with overriding filter_for_field()
(note the # Handle GeneratedFields
block):
@classmethod
def filter_for_field(cls, field, field_name, lookup_expr=None):
if lookup_expr is None:
lookup_expr = settings.DEFAULT_LOOKUP_EXPR
# Handle GeneratedFields
if isinstance(field, GeneratedField):
new_field = field.output_field
new_field.model = field.model
field = new_field
field, lookup_type = resolve_field(field, lookup_expr)
default = {
"field_name": field_name,
"lookup_expr": lookup_expr,
}
filter_class, params = cls.filter_for_lookup(field, lookup_type)
default.update(params)
assert filter_class is not None, (
"%s resolved field '%s' with '%s' lookup to an unrecognized field "
"type %s. Try adding an override to 'Meta.filter_overrides'. See: "
"https://django-filter.readthedocs.io/en/main/ref/filterset.html"
"#customise-filter-generation-with-filter-overrides"
) % (cls.__name__, field_name, lookup_expr, field.__class__.__name__)
return filter_class(**default)
It might not be the perfect fix, but it's worked well for me in my testing.
So there's two parts to this.
The is the error. That should be addressed by #1675, which will enable skipping unknown fields — if you haven't used filter_overrides.
The second is the generated field handling itself:
# Handle GeneratedFields
if isinstance(field, GeneratedField):
new_field = field.output_field
new_field.model = field.model
field = new_field
Is it as simple as that? Can we add built-in support?
Disclaimer: I haven't used GeneratedField yet, so haven't looked into what works and doesn't here at all.
Is it as simple as that? Can we add built-in support?
Built-in support would be great! I can't find a problem with this solution so far, and it's been deployed to production today, so 🤞
I'll let you know if we find any issues, but I think this is all that's needed.
OK, but the correct place to add this would be to the FILTER_FOR_DBFIELD_DEFAULTS
, probably with a specific Filter subclass.
If someone wants to take that on as an addition (with docs and tests) that would be very welcome.