Storing a partial date#
PartialDateField stores a PartialDate in one text column. The column holds the ISO
form, so a row read by any other tool shows 1990, 1990-06 or 1990-06-14. This page shows how
to save and read a value, how the field treats text that is not a date and empty values, and how to
move dates in and out with fixtures. Installation covers adding the field to a
model.
The examples use this model.
from django.db import models
from partial_dates import PartialDate, PartialDateField
class Sample(models.Model):
collected = PartialDateField(null=True, blank=True)
Save and read a value#
The field takes a PartialDate or its ISO text, and gives back a PartialDate.
>>> from partial_dates import PartialDate
>>> sample = Sample.objects.create(collected=PartialDate(1990, 6))
>>> sample.refresh_from_db()
>>> sample.collected
PartialDate(year=1990, month=6, day=None)
Find a row by its exact value#
Filtering by a value, or by its ISO text, finds the rows that hold exactly that value. A row
holding 1990-06 is not found by 1990. Querying covers filters on a year, a month,
a day or a range.
>>> Sample.objects.filter(collected=PartialDate(1990, 6)).count()
1
>>> Sample.objects.filter(collected="1990-06").count()
1
Handle text that is not a date#
Text that is not a partial date is refused when it is saved. save(), create(), update(),
bulk_create() and bulk_update() raise ValueError, and a datetime.date or an integer is
refused the same way. full_clean() reports it as a ValidationError with the code invalid on
the field.
>>> Sample.objects.create(collected="1990-6")
Traceback (most recent call last):
...
ValueError: Not a partial date in ISO form: '1990-6'
Leave a date empty#
An empty value is no date. None and "" are both stored as NULL and read back as None, so a
field that may be empty is declared with null=True, and blank=True lets a form leave it out.
Without null=True the database refuses a missing date.
Rely on the check in the database#
The database checks the column itself. It refuses any text that is not YYYY, YYYY-MM or
YYYY-MM-DD naming a real date, whichever route writes it: raw SQL, a data migration or another
program. The check is created by the migration of the model that declares the field, and the
package asks for no extra constraint. On PostgreSQL the column is also byte-order collated, so a
database sort of the column is the same sort as sorted() on the values.
Dump and load fixtures#
dumpdata writes each value as its ISO form and an empty value as empty, and loaddata reads them
back unchanged, in JSON, JSON Lines and XML. A fixture can be loaded into the same database or into
the other supported one.
$ python manage.py dumpdata app.Sample --format jsonl > samples.jsonl
$ python manage.py loaddata samples.jsonl
{"model": "app.sample", "pk": 1, "fields": {"collected": "1990-06"}}
{"model": "app.sample", "pk": 2, "fields": {"collected": null}}
A fixture holding text that is not a partial date, such as 1990-6, is refused when it is loaded.
Why a partial date is one text column#
ADR 0001 records how the package stores a partial date and why.