I have a model that records changes to other models in my database.
I would like to query for changes that only add a newline character — n.
My changes model looks like this:
class Change(models.Model):
object_type = models.ForeignKey(
ContentType,
related_name="object_change_set",
on_delete=models.CASCADE,
)
object_id = models.CharField(max_length=255, db_index=True)
object = GenericForeignKey("object_type", "object_id")
old_fields = models.JSONField(encoder=JSONEncoder, null=True, blank=True)
new_fields = models.JSONField(encoder=JSONEncoder, null=True, blank=True)
Essentially, I want to find instances of Change where the difference between a value for a key in old_fields and new_fields is n.
Here’s what new_fields look like:
{
"body": "Had a great time on Wednesday.",
"created": "2022-06-15T19:49:06.784Z",
}
And similarly, old_fields:
{
"body": "Had a great time on Wednesday.n",
"created": "2022-06-15T19:49:06.784Z",
}
Note that new_fields and old_fields are both JSONFields.
Ultimately, I want to remove the Change instances that only add n to the body.
I realize that I could do this by iterating over the queryset and looking for these discrepancies, but I’m wondering if there’s a more elegant solution.
Advertisement
Answer
You can use the __startswith (along with an F() expression) and __endswith filters to check if the old_fields body starts with the new_fields body and ends with a :
from django.db.models import F
queryset = Change.objects.filter(
old_fields__body__startswith=F("new_fields__body"), old_fields__body__endswith="n"
)
This would, however, still match if the old_fields body had any string between the end of the new_fields body and the ending line break (e.g. "Had a great time on Wednesday.aaaaaaaaaaaaaaaaaan"). To fix that, you can also annotate the queryset with a Length function and check if the old_fields body’s length is equal to Length("new_fields__body") + 1:
from django.db.models import F
from django.db.models.functions import Length
queryset = Change.objects.annotate(old_body_len=Length("old_fields__body")).filter(
old_fields__body__startswith=F("new_fields__body"),
old_fields__body__endswith="n",
old_body_len=Length("new_fields__body") + 1,
)
This should work!