SoFunction
Updated on 2024-12-20

Implementation of using apscheduler to execute scheduled tasks in django

Planning tasks for the future is an essential tool for any software developer. While much of the programming we create is intended to respond to explicit triggers or user events, background processes that execute periodically are just as important.

"Results are updated every Monday morning."

"Orders are placed in batches every night."

Even third-party APIs with daily request limits implicitly require this behavior.

"We can only request updates every five minutes."

Fortunately, many smart people have solved this problem and it's not hard to find python native solutions. Advanced Python Scheduler (APS) is a great option with a simple, intuitive API and some of the best documentation in its class.

For this project, we'll focus on integrating the scheduling technology provided by APS with your regular Django application: the Los Angeles Weather app, which periodically polls a third-party weather api for model updates.

The goal is to explore deeper than the Django tutorials while not getting bogged down in any direction.

I. Installation of APS and other dependencies

In your project directory, create a virtual environment and activate it

virtualenv env
. env/bin/activate

Install and configure PostgreSQL according to this guide. At this stage, we just need to get SQL Manager up and running on your computer.

Also, I found it helpful to use the PgAdmin PostgreSQL GUI. Details on setting it up on your computer can be found here (using Python3).

Use pip to install all required packages (note that psycopg2 works for PostgreSQL):

pip install apscheduler django psycopg2 requests
II. Build your app
Create a new Django project:

 startproject advancedScheduler
cd advancedScheduler
python  startapp weather

In this new directory (the root directory) you will see another folder called advancedScheduler. This is the Django project directory.

To avoid the confusion of having two places with the same name, we will just refer to the "root directory" as the "root directory". Let the following diagram serve as a roadmap for our folder-hopping adventure.

[ super_project_directory/ ]
|
+----[ env/ ] <-- Virtualenv stuff
|
+----[ advancedScheduler/ ] <-- the Root Directory
 |
 +----[ advancedScheduler/ ] <-- the Django Project Directory
 |
 +----[ weather/ ] <-- the Django App Directory

While primarily focused on demonstrating the functionality of the scheduler, let's take a moment to connect the Django application.

We'll start by adding the weather app to the project's INSTALLED_APPS. This is located in the advancedScheduler / file.

INSTALLED_APPS = [
'',
'',
'',
'',
'',
'',
'weather'
]

Next, add the new URL format to the advancedScheduler / file:

path('', include(''))

Not surprisingly, our next step will be to add the file to the weather app directory. Include the following code in weather /:

from  import url
from weather import views
urlpatterns = [
 url(r'^$', .as_view())
]

Create a template folder in the Weather app directory. Add files to this new folder.

Check out our MTV below.

mould

from  import models
from datetime import datetime 
 
class Forecast():
 timestamp = ()
 temperatue = (max_digits=12,decimal_places=2)
 description = (max_length=150)
 city = (max_length=150)
 
 def save(self, *args, **kwargs):
 if not :
   = ()
 return super(Forecast, self).save(*args, **kwargs)
Template
<div style="text-align: center">
 <h5>Currently in</h5>
 <h3>{{city}}</h3>
 <h4>{{temperature_in_f}} F | {{temperature_in_c}} C</h4>
 <h4>{{desctiprion}}</h4>
 <p><em>Last updated {{utc_update_time}} GMT</em></p>
</div>
View
import decimal
from datetime import datetime 
from  import render
from  import TemplateView
from  import Forecast 
class MainPage(TemplateView):
 def get(self, request, **kwargs):
 
 latest_forecast = ('timestamp')
 city = latest_forecast.city
 temperature_in_c = latest_forecast.temperatue
 temperature_in_f = (latest_forecast.temperatue * (1.8)) + 32
 description = latest_forecast.
 timestamp = "{}/{:02d}/{:02d} - {:02d}:{:02d}:{:02d}".format( t=latest_forecast.timestamp)
 return render(
  request, 
  '', 
  {
  'city':city,
  'temperature_in_c': temperature_in_c,
  'temperature_in_f': round(temperature_in_f,2),
  'desctiprion': description,
  'utc_update_time': timestamp})

III. Establishing database connections and migrating models

In advancedScheduler /, change the DATABASES value to:

DATABASES = {
 'default': {
 'ENGINE': '.postgresql_psycopg2',
 'NAME': 'advancedScheduler',
 'USER': 'some_user_name',
 'PASSWORD': 'some_password',
 'HOST': 'localhost',
 'PORT': '',
 }
}

You should know the values for USER, PASSWORD and PORT from the PostgreSQL configuration guides above (here and here).

After establishing a connection with PostgreSQL, it's time to migrate our model. Navigate to the "root directory" and type:

python  makemigrations
python  migrate

With this, our model should be mapped to the database. Go ahead and check everything. Don't worry, I'll be here when you get back.

IV. Forecasting APIs

Time for the fun part. I'm pulling my forecast data from OpenWeatherMap, a free weather API that grants you an access token with a valid email address.

Now, since it is conceptually different from our representation layer, let's create a new ForecastUpdater folder in the root directory. In it, we will add two files: a blank __init__.py file and a file. See the roadmap for reference.

[ super_project_directory/ ]
|
+----[ env/ ]
|
+----[ advancedScheduler/ ] <-- the Root Directory
 |
 +----[ advancedScheduler/ ] 
 |
 +----[ weather/ ] 
 |
 +----[ forecastUpdater/ ] <-- the new Updater Module
  |
  +----< __init__.py > <--+
  |    |-- two new Python files
  +----<  > <--+
import requests
from  import Forecast
 
def _get_forecast_json():
 url = '/data/2.5/weather'
 encoded_city_name = 'Los%20Angeles'
 country_code = 'us'
 access_token = 'your_access_token'
 
 r = ('{0}?q={1},{2}&APPID={3}'.format(
 url, 
 encoded_city_name, 
 country_code, 
 access_token))
 
 try:
 r.raise_for_status()
 return ()
 except:
 return None
 
 
def update_forecast():
 json = _get_forecast_json()
 if json is not None:
 try:
  new_forecast = Forecast()
  
  # open weather map gives temps in Kelvin. We want celsius.  
  temp_in_celsius = json['main']['temp'] - 273.15
  new_forecast.temperatue = temp_in_celsius
  new_forecast.description = json['weather'][0]['description']
  new_forecast.city = json['name']
  new_forecast.save()
  print("saving...\n" + new_forecast)
 except:
  pass

There are a few things to keep in mind here. Exception handling is far from robust. Errors are just thrown away-excessive silence is the only sign that something is wrong.

Next, we specify Los Angeles in the code. Configure your server to any desired location.

It's also important to note that update_forecast() takes no arguments. As we will soon see, our advanced python planner has strict no-argument rules. Even methods with a lone self argument won't fly.

V. Advanced Python Planning Programs

We have built the model. We can update the data by calling the API. Now all we need to do is specify how often that API is accessed so that we can provide reasonably up-to-date information without exceeding data access limits.

In the ForecastUpdater module, add a file. Here, we'll use Advanced Python Scheduler to set the cadence of our forecast updates.

OpenWeatherMaps Terms of Use allows 60 calls to be held in an hour to maintain the free tier; updates every five minutes are sufficient.

from datetime import datetime
from  import BackgroundScheduler
from forecastUpdater import forecastApi
 
def start():
 scheduler = BackgroundScheduler()
 scheduler.add_job(forecastApi.update_forecast, 'interval', minutes=5)
 ()

This is probably the simplest implementation of APS you can find. If you check out their website or the few working examples on GitHub, then you will find a full toolbox of features and settings that you can use to time it to be as detailed as possible.

After configuring the scheduler the way we want it, we can connect it to the Django application.

Ideally, we want to press play once on a scheduler and then have it perform its task. We need a consistent and reliable way to initialize the schedule once and only once. For us, Django is exactly the place for this type of runtime initialization logic.

In the weather / file, you'll find a stub for a class called WeatherConfig, which inherits from Django's AppConfig class.

class WeatherConfig(AppConfig):
 name = 'weather'

To let Django know that it needs to start the updater on startup, we override the () method.

from  import AppConfig
class WeatherConfig(AppConfig):
 name = 'weather'
 def ready(self):
 from forecastUpdater import updater
 ()

It's important to remember that due to the complexity of inheritance, any import of this override must be located within the body of the ready() method. Django also warns against interacting directly with the database in our override; production, debugging, rain or shine, will execute this code every time the weather application is launched.

Finally, we now need to update the INSTALLED_APPS variable again in advancedScheduler /. Django needs to know that we're going to use a custom configuration to run the weather app.

INSTALLED_APPS = [
'',
'',
'',
'',
'',
'',
'' 
]

Put it all together.

Only. At this point, we can start our application and then let the updater perform its actions.

python runserver --noreload

The -noreload flag prevents Django from launching a second instance of the weather app - this is the default behavior in debug mode. The second instance means that all our scheduled tasks will trigger twice.

Initially, our results looked incomplete. Since we scheduled the updater logic to run every five minutes, we kept twitching ...... To keep things interesting, it might be wise to shorten the interval between prudent refreshes, or call update_forecast() once at initialization time.

Seven. Final thoughts

We did it! Our weather app is ready to be shared with the world (check out my info here).

Advanced Python Scheduler is a great tool that any Python developer knows about. It hides the complexity of very common business requirements behind an intuitive API. Consider that the installer uses only three lines of code.

The real trick of the project is interacting with the Django framework-configuring, migrating, initializing. Then task automation becomes an afterthought. In five minutes you are done.

/kmhoran/la-weather-app

The above is a small introduction to the use of apscheduler in django in the implementation of scheduled tasks , I hope to help you !