In the previous entry, “Bixly How-To: dynamically adding models for testing in django”, we created a separate app containing the models for testing and then added that app dynamically on running the test management command. I realized that there could be a strong potential on dynamic apps, toggling apps on/off on the fly, in a project. To do this, it first came to my mind that it would be best to have a single app installed managing all the other apps. This is how:
Step 1: Creating the app manager
Create an app in your project, let’s say, dynapp, and then add this to the INSTALLED_APPS in your settings. In dynapp, create a model that will be containing the apps it will manage.
class Application(models.Model): name = models.CharField(max_length=32) active = models.BooleanField(default=True)
Notice the fields of the model. In this simple model, I just put in a CharField for the name of the app you want to include and a BooleanField to say if it’s active or inactive. You can opt to remove the boolean field if you want to just delete a row from the model to disable an app. You can also add additional fields like an IntegerField for determining load_order to let’s say decide the loading order of the apps and so on.
You’ll then want to do a syncdb to create the model.
Now that we have the Application model and inserted some apps into the table, the problem arises — how do we use the apps if its not in INSTALLED_APPS when the server is run?
Easy. Proceed to step 2.
Step 2: Loading the additional apps on server startup
In the Application model, override the save method with:
def save(self, *args, **kwargs): super(Application, self).save() from django.conf import settings from django.core.urlresolvers import clear_url_caches import os # Note1 file = open(os.path.dirname(__file__) + "/apps.py", "wb") file.write("apps = " + list(Application.objects.filter(active=True).values_list('name', flat=True)).__str__()) file.close() # Note2 reload(import_module(settings.ROOT_URLCONF)) clear_url_caches() # Note3 apps = list(Application.objects.filter(active=True).values_list('name', flat=True)) settings.INSTALLED_APPS = list(settings.ORIGINAL_INSTALLED_APPS) + apps
This override will create a module inside dynapp called apps.py which will store an array of the app names currently active in the Application model. It’s a bit hackish, I know.
This will reload the the loaded url patterns and clear the url resolver cache.
This updates the loaded settings.INSTALLED_APPS in case some other module will need to reference it.
Next, in your settings.py, you rename your INSTALLED_APPS into something else like ORIGINAL_INSTALLED_APPS. then you add this below:
INSTALLED_APPS = ORIGINAL_INSTALLED_APPS try: from dynapp.apps import apps INSTALLED_APPS += tuple(apps) except: print "The module dynapp.apps does not exist."
These changes to your settings will allow you to include the currently active apps and allow syncdb to create the tables for those apps.
At this point, you might want to run syncdb again and then you are DONE.
Since you are editing your project settings on the fly, this method of doing it is MOST LIKELY NOT THREAD SAFE. Also, it would be best to keep the module dynapp.apps in mind especially if there are importing problems or inconsistencies.