Why does django-q throw exception with arrow time

451 views Asked by At

I'm trying to create a Django-q schedule and following the documents to use arrow for the next run I get the following error with the schedule:

schedule(
        func='test.tasks.test_task',
        name='test_task_nightly',
        schedule_type=Schedule.DAILY,
        next_run=arrow.utcnow().replace(hour=23, minute=30),
        q_options={'timeout': 10800, 'max_attempts': 1},
    )
Traceback (most recent call last):
  File "/usr/lib/python3.8/code.py", line 90, in runcode
    exec(code, self.locals)
  File "<console>", line 1, in <module>
    schedule(
  File "/home/user/PycharmProjects/app/venv/lib/python3.8/site-packages/django_q/tasks.py", line 122, in schedule
    s.full_clean()
  File "/home/user/PycharmProjects/app/venv/lib/python3.8/site-packages/django/db/models/base.py", line 1209, in full_clean
    self.clean_fields(exclude=exclude)
  File "/home/user/PycharmProjects/app/venv/lib/python3.8/site-packages/django/db/models/base.py", line 1251, in clean_fields
    setattr(self, f.attname, f.clean(raw_value, self))
  File "/home/user/PycharmProjects/app/venv/lib/python3.8/site-packages/django/db/models/fields/__init__.py", line 650, in clean
    value = self.to_python(value)
  File "/home/user/PycharmProjects/app/venv/lib/python3.8/site-packages/django/db/models/fields/__init__.py", line 1318, in to_python
    parsed = parse_datetime(value)
  File "/home/user/PycharmProjects/app/venv/lib/python3.8/site-packages/django/utils/dateparse.py", line 107, in parse_datetime
    match = datetime_re.match(value)
TypeError: expected string or bytes-like object

Not sure why it's not accepting the time format similar to the example given in the django-q documentation page.

EDIT: The task being scheduled:

def test_task():
    print('Executed test task')

Nothing too complex just for testing purposes

1

There are 1 answers

0
Nexus On

The Django ORM (verion 3.2 at time of writing) won't accept an Arrow object in any DateTimeField.

Arrow objects emulate Python's datetime object interface, but they are not real datetime objects. So any code receiving an Arrow object will fail if it explicitly checks if your value is an honest-to-goodness datetime. Which is exactly what the code in django.db.models.fields.DateTimeField.to_python appears to be doing:

def to_python(self, value):
    if value is None:
        return value
    if isinstance(value, datetime.datetime):
        return value
    if isinstance(value, datetime.date):
        value = datetime.datetime(value.year, value.month, value.day)
    ...
    try:
        parsed = parse_datetime(value)

As you can see, when it doesn't match a datetime or date instance, Django hands it off to the parse_datetime() function to deal with, which expects a string. Which explains your error: TypeError: expected string or bytes-like object

You can get around this by getting the .datetime property, which will return a plain old python datetime, i.e.

schedule(
        func='test.tasks.test_task',
        name='test_task_nightly',
        schedule_type=Schedule.DAILY,
        next_run=arrow.utcnow().replace(hour=23, minute=30).datetime,
        q_options={'timeout': 10800, 'max_attempts': 1},
    )