> It looks like you went even further than that :D.
yeah didn't want to step on your toes but I got very excited about trying it out 😅
> Should we still add Q.check() (which will be as you said before), then refactor BaseConstraint.validate() to use it?
I think it would still be worth doing to avoid the Query(None), add_annotation, except FieldError, get_compiler() boilerplate but me might need to change the planed function signature.
What do you think of
def check(self, instance, exclude=None, using=DEFAULT_DB_ALIAS):
query = Query(None)
for field in instance._meta.local_concrete_fields:
if exclude and field.name in exclude:
continue
value = getattr(instance, field.attname)
query.add_annotation(Value(value, field), field.name, select=False)
try:
query.add_annotation(ExpressionWrapper(self, BooleanField()), '_check')
except FieldError:
# Check is referencing an excluded field
return
None
compiler = query.get_compiler(using=using)
return bool(compiler.execute_sql(SINGLE)[0])
Looks like it would deal with most of the boilerplate while still being flexible enough for our use case. Maybe we don't want to push down the model/exclude field notion this far though and require literals to be passed directly instead.
def check(self, against, using=DEFAULT_DB_ALIAS):
query = Query(None)
for name, value in against.items():
if not hasattr('resolve_expression'):
value = Value(value)
query.add_annotation(value, name, select=False)
try:
query.add_annotation(ExpressionWrapper(self, BooleanField()), '_check')
except FieldError:
# Check is referencing a missing field
return
None
compiler = query.get_compiler(using=using)
return bool(compiler.execute_sql(SINGLE)[0])
I have a slight preference for the second option as it seems like it could be used in other context than constraints[0] but I'm curious about your thoughts? Looking at [0] in more details I also feel like matches() could be a better name than check().
Simon