I have a Django app that exposes the following views: A,B,C...Z
. This app is deployed on MACHINE-1.
There was a new business requirement to deploy on MACHINE-2 the same Django app but with a subset of those views, let's say A,B, C
.
My simple solution was to set a config variable (RUN_ON_MACHINE_2=True
for the second case and False
for the first case). In conclusion, the urls.py
file looks something like below (Please do note that I assimilate the views with paths here just to make it shorter):
urlpatterns = [A, B, C, ... Z]
# If the env var is set to True then only load a subset of the views
if config.RUN_ON_MACHINE_2:
urlpatterns = [A, B, C]
The tests work fine cause they are always executed for A,B,C ... Z
which of course include A, B, C
.
However, some new business requirements are asking to make changes on view A. I could implement the changes in view A branching the code by the config.RUN_ON_MACHINE_2
variable but I reached the conclusion it's much cleaner to create a new view, A_ForMachine2
, which implements only the needed stuff for MACHINE_2. A_ForMachine2
must have the same interface as view A
and must be mounted to the same path. So the new urls.py
would look like:
urlpatterns = [A, B, C, ... Z]
# If the env var is set to True then only load a subset of the views
if config.RUN_ON_MACHINE_2:
# A_ForMachine2 is mounted on the same path as A
urlpatterns = [A_ForMachine2, B, C]
Now I have 2 views that are mounted to the same path so they have the same URL.
Now my api tests are testing only the view A
because by default the config.RUN_ON_MACHINE_2
is False.
I tried to patch config.RUN_ON_MACHINE_2
in my test case but from what I understand, when the tests are run the application is already loaded with all the urls A,B,..Z
so it wont load/mount [A_ForMachine2, B, C]
.
What's a clean way to have a Django app (in my case a DRF app) that loads different urls at startup (depending on some env var) having all its views easily testable?
Cheers!
CodePudding user response:
ROOT_URLCONF to the rescue
Here is the documentation
First thing never use this kind of if-else architecture for different machine environments.
Always add SETTINGS
variable in you .env file and create different settings file like for dev
, qa
, stage
, prod
or local
. In your case create different settings file for Machine-1
and Machine-2
.
Then in your settings you update ROOT_URLCONF
according to urls you need for different environment.
Your main urls.py
will convert to a urls
folder where you can add you machine named urls. Then add the path to ROOT_URLCONF
in your respective settings.
Your urls
structure will look like below:
urls
├── __init__.py
├── common.py
├── local.py
└── production.py
And your different settings file will have properties like this:
# settings/local.py
ROOT_URLCONF = 'urls.local'
and this
# settings/production.py
ROOT_URLCONF = 'urls.production'
The main change will happen in urlspattern
:
# urls/local.py
from .common.py import urlpatterns as common_urlpatterns
urlpatterns = common_urlpatterns [
url(r'^A-view-path/', include(app_1.urls_for_A)),
]
You can keep both views having slightly different functionality with different names. But add the same url to machine one with one view and machine two with another view.
Here is a similar post. I copied some reference about folder structure from here. I had a same problem as I need code profiling urls only on testing and local instance and not on dev, qa and prod instance. Then this helped a lot.
If you add if-else architecture now then you will have scalability issues in future. And if you left the company other new developers will scratch their heads most of the time as no one will know why this is happening like this.
Don't forget to add comments, so others know your exact intent.