#36078 closed Cleanup/optimization (fixed)
Document that Postgres normalizes a range field with no points normalizes to empty
Reported by: | Mapiarz | Owned by: | Clifford Gama |
---|---|---|---|
Component: | contrib.postgres | Version: | 4.2 |
Severity: | Normal | Keywords: | postgres |
Cc: | Triage Stage: | Ready for checkin | |
Has patch: | yes | Needs documentation: | no |
Needs tests: | no | Patch needs improvement: | no |
Easy pickings: | no | UI/UX: | no |
Description (last modified by )
This short test exemplifies the problem:
now = timezone.now() empty_range = psycopg2_range.DateTimeTZRange(empty=True) test_model = TestModel(range=psycopg2_range.DateTimeTZRange(now, now)) self.assertFalse(test_model.range.isempty) self.assertNotEqual(test_model.range, empty_range) test_model.save() test_model.refresh_from_db() self.assertTrue(test_model.range.isempty) self.assertEqual(test_model.range, empty_range)
I would expect the field value not to change after saving, and it should continue to have the lower, upper and boundary fields set.
Repro project: https://github.com/Mapiarz/django_postgres_range_repro/tree/master
The example is for DateTimeRange, but DateRange Is also affected. Other ranges possibly too, I haven't tested.
Change History (14)
comment:1 by , 8 months ago
Description: | modified (diff) |
---|
comment:2 by , 8 months ago
Resolution: | → invalid |
---|---|
Status: | new → closed |
comment:3 by , 8 months ago
Read also https://forum.djangoproject.com/t/baserangefield-and-equal-lower-upper-values/35160
I think this should be at least documented.
comment:4 by , 8 months ago
Resolution: | invalid |
---|---|
Status: | closed → new |
Summary: | Postgres date and time range fields change after saving object → Document that Postgres normalizes a range field with no points normalizes to empty |
Triage Stage: | Unreviewed → Accepted |
Type: | Bug → Cleanup/optimization |
Yeah, I think it was surprising and would help to be pointed out in the docs
comment:5 by , 8 months ago
I agree. There should be an explicit warning about this. It's really easy to be caught of guard by this and lose data.
I do not understand why psycopg opted out of normalizing the value to empty, just like postgres does. I have raised this issue here: https://github.com/psycopg/psycopg/discussions/993
comment:6 by , 8 months ago
I agree. There should be an explicit warning about this. It's really easy to be caught of guard by this and lose data.
You are already losing data, because if you are calling DateTimeTZRange('2025-01-01', '2025-01-31')
to mean the whole month of January you are losing a day, because the default bounds are [)
so the 31st is not in the range, the same way that 4 is not in the [4,4)
range. The data loss comes from using the wrong bounds, not from the normalization rules, which are the same whether implemented by Python or by Postgres.
In the above example, DateTimeTZRange(now, now, '[]')
might have been what you wanted.
I do not understand why psycopg opted out of normalizing the value to empty, just like postgres does. I have raised this issue here: https://github.com/psycopg/psycopg/discussions/993
I have left a response on the psycopg tracker. I am -0.5 to introduce normalization in Python, but, regardless from that, I don't think it would have resulted in any difference w.r.t. the issue explained here. If we had returned what the OP expected to receive it would have meant we have a bug as Psycopg normalization would disagree with Postgres one.
comment:7 by , 8 months ago
Owner: | set to |
---|---|
Status: | new → assigned |
comment:9 by , 6 months ago
Patch needs improvement: | set |
---|
comment:10 by , 6 months ago
Patch needs improvement: | unset |
---|
comment:11 by , 6 months ago
Triage Stage: | Accepted → Ready for checkin |
---|
Thank you for the careful report I was able to replicate and had a look into this
From what I can tell, this is Postgres behavior and so I don't think this is a bug in Django
See https://www.postgresql.org/docs/current/rangetypes.html#RANGETYPES-IO