6

TL;DR In my django project, where do I put my "start-a-thread" code to make a thread run as soon as the django server is up?

First of all, Happy New Year Everyone! This is a question from a newbie so it might seem dumb. Thank you for your patience and help in advance!

Background Information
Recently I am writing a MQTT forwarding program which receives MQTT message. I'd like user to set some rules to handle messages to be forwarded so I chose django to build a web server. User can edit the rules themselves on the web page.
For the MQTT client I need to have a thread to receive MQTT message no matter if the user opens a webpage or not, so I cannot write it in the view.py. If I write a shell to start django server and my script separately, I am not sure how to pass the users settings from django server to my mqtt client script.

Question
1. Is there a way to start a background thread as soon as I typed python manage.py runserver?
2. One of the user settings is the MQTT server host, so once the user change this entry on the webpage, the background MQTT client should restart and connect to the new host. Is there any ways to achieve that?

I did some research and some likely solutions I found were django-background-tasks or Celery but I am not sure those are the right way, and I think this problem is too small to import another heavy module to deal with. Please give me some advice! If you are unclear about anything feel free to ask me!
Thank you again!

Jin.J
  • 353
  • 1
  • 4
  • 13

1 Answers1

10

What I think you can do is to start the thread in the ready() method your AppConfig in apps.py

Here a simple example

# apps.py
from django.apps import AppConfig
from threading import Thread

class TestThread(Thread):

    def run(self):
        print('Thread running')



class StackConfig(AppConfig):
    name = 'stack'

    def ready(self):
        TestThread().start()

If you need code from other parts of your app be aware that you cannot import this on the module level, i.e. if you try something like this in your apps.py

#apps.py
from . models import Code

an exception will be raised:

django.core.exceptions.AppRegistryNotReady: Apps aren't loaded yet

In order to avoid this you need to do the import in your ready() method:

#apps.py
class StackConfig(AppConfig):
        name = 'stack'

        def ready(self):
            from stack.models import Code
            TestThread().start()

This is decribed in the docs.

In order for all this to work make sure you start your app in your settings.INSTALLED_APPS like so

'stack.apps.StackConfig',
Chris
  • 2,162
  • 1
  • 6
  • 17
  • Thank you for your reply! There is a problem when I tried to use this method: When I run "python manage.py runserver". There is an exception:django.core.exceptions.AppRegistryNotReady: Apps aren't loaded yet. I guess it is because that thread uses models from the App. Is there any thing I could do to avoid this exception? – Jin.J Jan 02 '20 at 03:49
  • @Jin.J I suspect this is caused by you trying to import something in the apps.py on module level. See my edited answer for a way to avoid this – Chris Jan 02 '20 at 05:44
  • Thank you! Now it works! I think I need some time to play with it a little bit to find what my next problem is. Nevertheless I have noticed that the thread is actually created and runned twice. Does it happen only in the development environment? Is there any way to avoid it? – Jin.J Jan 02 '20 at 06:53
  • @Jin.J I think it is a feature caused by the development server as described in [this](https://stackoverflow.com/questions/33814615/how-to-avoid-appconfig-ready-method-running-twice-in-django) post. You can use the --noreload option to avoid this when starting runserver – Chris Jan 02 '20 at 07:43