'How to access Django models from a scheduled script?
TL;DR:
I have an app that I want to run a routine every day at midnight (for that I'm using APScheduler). In the middle of this routine it's supposed to access data from a few Django models.
The logic is
Django is running > run apps.py > run scheduler.py > run routine.py > access models.py.
Exceptions raised at the bottom of the post.
- Here comes the details:
My directory is this:
myproject/
- manage.py
+ myproject/
-- settings.py
-- wsgi.py
-- ...
+ myapp/
-+ static/
-+ templates/
-- admin.py
-- apps.py
-- models.py
-- views.py
-- scheduler.py #<<<<<<<<<<
-- routine.py #<<<<<<<<<<
-- ...
myapp/models.py
class MyModel(models.Model):
field1 = models.DateField(auto_now=True)
field2 = models.DecimalField(max_digits=19, decimal_places=16)
...
myapp/apps.py
from django.apps import AppConfig
from .scheduler import ScheduledRoutine
class MyAppConfig(AppConfig):
name = 'myapp'
def ready(self):
routine = ScheduledRoutine()
routine.start()
myapp/scheduler.py
from .routine import MyRoutine
#from ..routine import MyRoutine # See error nr3 <<<<<<<<<
from apscheduler.schedulers.background import BackgroundScheduler
class ScheduledRoutine(object):
def start(self):
self.scheduler = BackgroundScheduler()
startdate = datetime.now() #For brevity assume datetime object
self.scheduler.add_job(self.routine, 'interval', days=1, start_date=startdate)
self.scheduler.start()
def routine(self):
data = MyRoutine()
myapp/routine.py
import os
os.environ["DJANGO_SETTINGS_MODULE"] = "myproject.settings"
import django
django.setup()
from .models import MyModel
#from myapp.models import MyModel # See error nr3 <<<<<<<<<
class MyRoutine(object):
def __init__(self, arg):
self.arg = arg
data = MyModel.objects.filter(reliable=True)
self.do_something(data)
- Exceptions and what didn't work
I've tried several things already, those are the ones that I remember:
- Error Nr1:
With the current state of things (as seen above) this is the error:
Watching for file changes with StatReloader
Exception in thread django-main-thread:
Traceback (most recent call last):
File "C:\Python\Python37\lib\threading.py", line 917, in _bootstrap_inner
self.run()
File "C:\Python\Python37\lib\threading.py", line 865, in run
self._target(*self._args, **self._kwargs)
File "C:\venv\lib\site-packages\django\utils\autoreload.py", line 54, in wrapper
fn(*args, **kwargs)
File "C:\venv\lib\site-packages\django\core\management\commands\runserver.py", line 109, in inner_run
autoreload.raise_last_exception()
File "C:\venv\lib\site-packages\django\utils\autoreload.py", line 77, in raise_last_exception
raise _exception[1]
File "C:\venv\lib\site-packages\django\core\management\__init__.py", line 337, in execute
autoreload.check_errors(django.setup)()
File "C:\venv\lib\site-packages\django\utils\autoreload.py", line 54, in wrapper
fn(*args, **kwargs)
File "C:\venv\lib\site-packages\django\__init__.py", line 24, in setup
apps.populate(settings.INSTALLED_APPS)
File "C:\venv\lib\site-packages\django\apps\registry.py", line 91, in populate
app_config = AppConfig.create(entry)
File "C:\venv\lib\site-packages\django\apps\config.py", line 90, in create
module = import_module(entry)
File "C:\Python\Python37\lib\importlib\__init__.py", line 127, in import_module
return _bootstrap._gcd_import(name[level:], package, level)
File "<frozen importlib._bootstrap>", line 1006, in _gcd_import
File "<frozen importlib._bootstrap>", line 983, in _find_and_load
File "<frozen importlib._bootstrap>", line 953, in _find_and_load_unlocked
File "<frozen importlib._bootstrap>", line 219, in _call_with_frames_removed
File "<frozen importlib._bootstrap>", line 1006, in _gcd_import
File "<frozen importlib._bootstrap>", line 983, in _find_and_load
File "<frozen importlib._bootstrap>", line 967, in _find_and_load_unlocked
File "<frozen importlib._bootstrap>", line 677, in _load_unlocked
File "<frozen importlib._bootstrap_external>", line 728, in exec_module
File "<frozen importlib._bootstrap>", line 219, in _call_with_frames_removed
File "C:\venv\myproject\myapp\apps.py", line 2, in <module>
from .scheduler import ScheduledRoutine
File "C:\venv\myproject\myapp\scheduler.py", line 6, in <module>
from .routine import MyRoutine
File "C:\venv\myproject\myapp\routine.py", line 10, in <module>
django.setup()
File "C:\venv\lib\site-packages\django\__init__.py", line 24, in setup
apps.populate(settings.INSTALLED_APPS)
File "C:\venv\lib\site-packages\django\apps\registry.py", line 83, in populate
raise RuntimeError("populate() isn't reentrant")
RuntimeError: populate() isn't reentrant
- Error Nr2:
Since routine.py is inside myapp/ folder, and it tries to reference myproject/settings.py I tried adding . (also tried ..) to the reference like this:
os.environ["DJANGO_SETTINGS_MODULE"] = ".myproject.settings"
and this:
os.environ["DJANGO_SETTINGS_MODULE"] = "..myproject.settings"
Both returned the same exceptions:
TypeError: the 'package' argument is required to perform a relative import for '.myproject.settings'
- Error Nr3:
Due to the same problem mentioned above (the reference to settings.py that is myproject/settings.py) I tried moving the routine.py to the parent folder, therefore I needed to change the imports in routine.py and scheduler.py to their commented versions. That led to this exception:
ValueError: attempted relative import beyond top-level package
- Error Nr4:
Also, running django with the following lines commented (in routine.py)
#os.environ["DJANGO_SETTINGS_MODULE"] = "myproject.settings"
#import django
#django.setup()
raises django.core.exceptions.AppRegistryNotReady: Apps aren't loaded yet. which is pretty much how things started.
Edit: - Error Nr5: Using this in routine.py raises the same error as Nr1.
from django.core.wsgi import get_wsgi_application
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'myproject.settings')
application = get_wsgi_application()
To me it's seems clear that the issue is the way I'm trying to access the models in routine.py and not the scheduler. What else can I try?
Solution 1:[1]
You can try creating a Django custom command, so you can create a command like python manage.py myroutine and use a scheduler (like cron) to run it. If you do it, can avoid all this in your code:
os.environ["DJANGO_SETTINGS_MODULE"] = "myproject.settings"
import django
django.setup()
I know it isn't a exactly solution to your problem, but it's a workaround.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | renatodvc |
