I searched how to write a single file Django app so I can test Django features fast. I searched the internet and found the safest single-file Django application is shared here, but I couldn’t find any example that includes Django models, so I made some additions to this code.
To make makemigrations
run, we need to understand Python’s way of searching the modules in the sys.path
list.
According to the doc, sys.path is “a list of strings that specifies the search path for modules. If the script directory is not available (e.g. if the interpreter is invoked interactively or if the script is read from standard input), path[0] is the empty string.” So if we use Python through a command line then the first element will be an empty string that indicates the current running directory.
Django uses the same way to find installed apps. After we create a project django-admin startproject project_name
and run server, we can see that the sys.path[0]
is assigned to the project root folder like /home/nurettin/PycharmProjects/project_name
in my case. In that directory, Django is able to find the apps whose name is stated in INSTALLED_APPS
. But in our case it won’t work, so we need to:
- treat that single file’s parent folder as a project root folder -> Append parent directory to
sys.path
- provide the file’s directory as an app folder -> We will make this in
makemigrations
command - treat that single file as an app -> initiate an app label in the model’s meta and put this in
INSTALLED_APPS
- Normally,
Django
knows these apps and their configuration via theirAppConfig
s, but if you look at the documentation of app_label, it says “if a model is defined outside of an application inINSTALLED_APPS
, it must declare which app it belongs to”, so we can define an app name in the model’sMeta.app_label
.
- Normally,
I made the necessary changes, created an example City
model, and gave the app label inside its Meta
class. The final code is as below:
import html import os import sys from django.conf import settings from django.core.wsgi import get_wsgi_application from django.http import HttpResponse from django.urls import path BASE_DIR = os.path.dirname(os.path.abspath(__file__)) sys.path.append(os.path.dirname(BASE_DIR)) APP_LABEL = os.path.basename(BASE_DIR) settings.configure( DEBUG=(os.environ.get("DEBUG", "") == "1"), ALLOWED_HOSTS=["*"], SECRET_KEY="supersecretkey", ROOT_URLCONF=__name__, MIDDLEWARE=["django.middleware.common.CommonMiddleware"], DEFAULT_AUTO_FIELD="django.db.models.BigAutoField", INSTALLED_APPS=[APP_LABEL], DATABASES={ "default": { "ENGINE": "django.db.backends.sqlite3", "NAME": os.path.join(BASE_DIR, "db.sqlite3"), } }, ) def index(request): name = request.GET.get("name", "World") return HttpResponse(f"Hello, {html.escape(name)}!") urlpatterns = [ path("", index), ] app = get_wsgi_application() from django.db import models class City(models.Model): name = models.CharField( max_length=128, blank=False, null=False, unique=True ) value = models.IntegerField(default=-1, blank=False, null=False) class Meta: app_label = APP_LABEL if __name__ == "__main__": from django.core.management import execute_from_command_line execute_from_command_line(sys.argv)
Running the code
Our settings are ready. All we need to do is to run migration commands. While we run makemigrations
and migrate
commands, we will provide the APP_LABEL
value which was the root folder name, and we need to run our command inside the project folder:
nurettin@laptop:~/PycharmProjects/project_name$ pwd /home/nurettin/PycharmProjects/project_name nurettin@laptop:~/PycharmProjects/project_name$ python main.py makemigrations project_name Migrations for 'project_name': migrations/0001_initial.py - Create model City nurettin@laptop:~/PycharmProjects/project_name$ python main.py migrate project_name Operations to perform: Apply all migrations: project_name Running migrations: Applying project_name.0001_initial... OK
We successfully created the migration file and corresponding table inside the project folder.
Importing the models in shell_plus
nurettin@laptop:~/PycharmProjects/project_name$ python main.py shell_plus >>> from django.apps import apps >>> city_model = apps.get_app_config('project_name').models['city'] >>> city_model.objects.all()
Now it’s time to test fast learn fast.