问题
I have a rather peculiar requirement: The app should be able to display its own uptime as total hours. This means I need to step away from the request-response cycle and update the current timestamp in the relevant model.
With this in mind, I followed the instructions given here, putting the code in my app's ready()
method in apps.py
. The problem, of course, is that I ran into the Apps aren't loaded yet
error. How do I work around this?
Another approach that comes to mind is do away with models and write the timestamp to a file, but this is a brittle method that will not scale. What if I want to store extensive relational information at boot time?
Can someone suggest something, please?
======= UPDATE =========
The code I'm using is as follows (my project is called jremind
and my app is called remind
).
Here's my model instance:
class Monitor(models.Model):
# save automatically when object is saved()
app_init_timestamp = models.DateTimeField(null=False, auto_now=True)
The app's __init__
file:
default_app_config = 'remind.apps.RemindConfig'
The app's apps.py
file:
from django.apps import AppConfig
from remind.models import Monitor
class RemindConfig(AppConfig):
name = 'remind'
def ready(self):
# There's only one instance
monitor = Monitor.objects.get()[0]
#Auto-update timestamp
monitor.save()
And here's the full stack trace when I run ./manage.py runserver
:
(env) jremind$ ./manage.py runserver
Performing system checks...
System check identified no issues (0 silenced).
July 13, 2016 - 15:12:08
Django version 1.9, using settings 'jremind.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.
^C(env) jremind$ ./manage.py runserver
Traceback (most recent call last):
File "./manage.py", line 10, in <module>
execute_from_command_line(sys.argv)
File "/media/common/code/python/projects/jremind/env/lib/python3.4/site-packages/django/core/management/__init__.py", line 350, in execute_from_command_line
utility.execute()
File "/media/common/code/python/projects/jremind/env/lib/python3.4/site-packages/django/core/management/__init__.py", line 342, in execute
self.fetch_command(subcommand).run_from_argv(self.argv)
File "/media/common/code/python/projects/jremind/env/lib/python3.4/site-packages/django/core/management/__init__.py", line 176, in fetch_command
commands = get_commands()
File "/media/common/code/python/projects/jremind/env/lib/python3.4/functools.py", line 448, in wrapper
result = user_function(*args, **kwds)
File "/media/common/code/python/projects/jremind/env/lib/python3.4/site-packages/django/core/management/__init__.py", line 71, in get_commands
for app_config in reversed(list(apps.get_app_configs())):
File "/media/common/code/python/projects/jremind/env/lib/python3.4/site-packages/django/apps/registry.py", line 137, in get_app_configs
self.check_apps_ready()
File "/media/common/code/python/projects/jremind/env/lib/python3.4/site-packages/django/apps/registry.py", line 124, in check_apps_ready
raise AppRegistryNotReady("Apps aren't loaded yet.")
django.core.exceptions.AppRegistryNotReady: Apps aren't loaded yet.
回答1:
You need to import the models from inside the method:
def ready(self):
from remind.models import Monitor
However, you should also note the warning in the documentation:
Although you can access model classes as described above, avoid interacting with the database in your
ready()
implementation. This includes model methods that execute queries (save()
,delete()
, manager methods etc.)... Yourready()
method will run during startup of every management command. For example, even though the test database configuration is separate from the production settings,manage.py test
would still execute some queries against your production database!
Also:
In the usual initialization process, the ready method is only called once by Django. But in some corner cases, particularly in tests which are fiddling with installed applications, ready might be called more than once. In that case, either write idempotent methods, or put a flag on your AppConfig classes to prevent re-running code which should be executed exactly one time.
Putting a flag would be done like so:
class RemindConfig(AppConfig):
name = 'remind'
ready_has_run = False
def ready(self):
if self.ready_has_run:
return
# Do your stuff here, and then set the flag
self.ready_has_run = True
来源:https://stackoverflow.com/questions/38341793/perform-model-operations-only-once-at-server-init