imsaiful
imsaiful

Reputation: 1614

TypeError: the first argument must be callable when I import a scheduler in my views.py file of django?

I am using Django 1.11. I want to use the scheduler in my application to run my script once in a day.

This is my view.py file

from __future__ import print_function
from django.shortcuts import render
from django.utils import timezone
from django.http import HttpResponse
from datetime import datetime, timedelta
import requests
import schedule
import time


def republic(request):
    return HttpResponse("<h1>Success Hindustan</h1>")


def indiatv(request):
    return HttpResponse("<h1>Success Hindustan</h1>")

def ndtv(request):
    return HttpResponse("<h1>Success NDTV</h1>")


schedule.every().day.at("17:19").do(republic(requests))
schedule.every().day.at("17:19").do(indiatv(requests))
schedule.every().day.at("17:19").do(ndtv(requests))

while 1:
    schedule.run_pending()
    time.sleep(1)

When I run the server I am getting following error

 File "/home/imsaiful/PiroProject/pironews/feed/urls.py", line 2, in <module>
    from . import views
  File "/home/imsaiful/PiroProject/pironews/feed/views.py", line 230, in <module>
    schedule.every().day.at("17:19").do(republic(requests))
  File "/home/imsaiful/anaconda3/lib/python3.6/site-packages/schedule/__init__.py", line 385, in do
    self.job_func = functools.partial(job_func, *args, **kwargs)
TypeError: the first argument must be callable

But when I remove the scheduler lines then the application running properly.

Upvotes: 2

Views: 6118

Answers (3)

willeM_ Van Onsem
willeM_ Van Onsem

Reputation: 476699

Fixing the scheduling call

By writing:

schedule.every().day.at("17:19").do(republic(requests))
schedule.every().day.at("17:19").do(republic(requests))
schedule.every().day.at("17:19").do(republic(requests))

You schedule the result of the republic(request) as job, not "calling republic with requests as job".

The do(job_func, *args, **kwargs) [schedule-doc] function however allows to provide parameters, you provide these after a reference to the job, so:

schedule.every().day.at("17:19").do(republic, requests)
schedule.every().day.at("17:19").do(republic, requests)
schedule.every().day.at("17:19").do(republic, requests)

Running the scheduler in a specific thread

You can not run the scheduler this way in Django, since it will mean that you keep running the command when loading the file. So loading the file will never terminate, and hence the server will never start.

You can run the scheduler asynchronous however, with:

from threading import Thread
from __future__ import print_function
from django.shortcuts import render
from django.utils import timezone
from django.http import HttpResponse
from datetime import datetime, timedelta
import requests
import schedule
import time


def republic(request):
    return HttpResponse("<h1>Success Hindustan</h1>")

schedule.every().day.at("17:19").do(republic, requests)
schedule.every().day.at("17:19").do(republic, requests)
schedule.every().day.at("17:19").do(republic, requests)

class SchedulerThread(Thread):
     @classmethod
     def run(cls):
         while True:
             schedule.run_pending()
             time.sleep(interval)

ScheduleThread().start()

Finally note that requests is not a HttpRequest object, so you should not write your functions as views, but as "vanilla" functions that perform certain work.

Upvotes: 1

Asif Mohammed
Asif Mohammed

Reputation: 1323

republic(requests) will return HttpResponse,

so the execution would be

schedule.every().day.at("17:19").do(HttpResponse)

inside do method you should mention function, not class instance. you can use one of the below

solution 1:

schedule.every().day.at("17:19").do(lambda: republic(requests))

solution 2.

schedule.every().day.at("17:19").do(republic, requests)

solution 3.

import functools
schedule.every().day.at("17:19").do(functools.partial(republic, requests))

Upvotes: 4

user8060120
user8060120

Reputation:

based on the schedule.Job.do, you should pass args after the function:

schedule.every().day.at("17:19").do(republic, requests)
schedule.every().day.at("17:19").do(indiatv, requests)
schedule.every().day.at("17:19").do(ndtv, requests)

do(job_func, *args, **kwargs)

Specifies the job_func that should be called every time the job runs. Any additional arguments are passed on to job_func when the job runs.

Upvotes: 2

Related Questions