Building machine learning applications using Django

Machine Learning


Building machine learning applications using DjangoBuilding machine learning applications using Django
Images by the author | chatgpt

Machine learning has powerful applications across a variety of domains, but the use of web frameworks is often necessary to effectively deploy machine learning models in real-world scenarios.

DjangoPython's high-level web frameworks are particularly popular for creating scalable and secure web applications. When paired with a library like Scikit-LearnDjango allows developers to provide inference for machine learning models via APIs and build an intuitive web interface for user interaction with these models.

In this tutorial you will learn how to build a simple Django application that provides predictions from machine learning models. This step-by-step guide will show you the entire process, from initial model training to inference and testing of the API.

# 1. Project setup

First, create a base project structure and install the required dependencies.

Create a new project directory and migrate to it.

mkdir django-ml-app && cd django-ml-app

Install the required Python packages.

pip install Django scikit-learn joblib

Initialize a new Django project called mlapp Create a new app with a name predictor:

django-admin startproject mlapp .
python manage.py startapp predictor

Set the template directory for the HTML file of your app.

mkdir -p templates/predictor

After running the above command, the project folder should look like this:

django-ml-app/
├─ .venv/
├─ mlapp/
│  ├─ __init__.py
│  ├─ asgi.py
│  ├─ settings.py
│  ├─ urls.py
│  └─ wsgi.py
├─ predictor/
│  ├─ migrations/
│  ├─ __init__.py
│  ├─ apps.py
│  ├─ forms.py        <-- we'll add this later
│  ├─ services.py     <-- we'll add this later (model load/predict)
│  ├─ views.py        <-- we'll update
│  ├─ urls.py         <-- we'll add this later
│  └─ tests.py        <-- we'll add this later
├─ templates/
│  └─ predictor/
│     └─ predict_form.html
├─ manage.py
├─ requirements.txt
└─ train.py           <-- Machine learning training script

# 2. Training machine learning models

Next, create a model that the Django app will use for predictions. In this tutorial, we will use the classic Iris dataset included in Scikit-Learn.

Create a script with the name in the root directory of your project train.py. This script loads the IRIS dataset and divides it into training and test sets. Next, we train a random forest classifier on the training data. After training is complete, the trained model is saved with the metadata. This includes the feature name and target label. predictor/model/ Use a directory Joblib.

from pathlib import Path
import joblib
from sklearn.datasets import load_iris
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import train_test_split

MODEL_DIR = Path("predictor") / "model"
MODEL_DIR.mkdir(parents=True, exist_ok=True)
MODEL_PATH = MODEL_DIR / "iris_rf.joblib"

def main():
    data = load_iris()
    X, y = data.data, data.target

    X_train, X_test, y_train, y_test = train_test_split(
        X, y, test_size=0.2, random_state=42, stratify=y
    )

    clf = RandomForestClassifier(n_estimators=200, random_state=42)
    clf.fit(X_train, y_train)

    joblib.dump(
        {
            "estimator": clf,
            "target_names": data.target_names,
            "feature_names": data.feature_names,
        },
        MODEL_PATH,
    )
    print(f"Saved model to {MODEL_PATH.resolve()}")

if __name__ == "__main__":
    main()

Run the training script:

If everything is successful, you will be prompted to confirm that the model is saved.

# 3. Configure Django settings

Now that your apps and training scripts are ready, you need to configure Django so you know where to find new applications and templates.

open mlapp/settings.py Create the following update:

  • I'll register predictor app in INSTALLED_APPS. This will tell Django to include custom apps in the project's lifecycle (models, views, forms, etc.).
  • I'll add it templates/ Directory of TEMPLATES composition. This allows Django to load HTML templates that are not directly tied to a particular app, such as forms that you will build later.
  • set ALLOWED_HOSTS Accept all hosts during development. This makes it easy to run your project locally without host related errors.
from pathlib import Path

BASE_DIR = Path(__file__).resolve().parent.parent

INSTALLED_APPS = [
    "django.contrib.admin",
    "django.contrib.auth",
    "django.contrib.contenttypes",
    "django.contrib.sessions",
    "django.contrib.messages",
    "django.contrib.staticfiles",
    "predictor",  # <-- add
]

TEMPLATES = [
    {
        "BACKEND": "django.template.backends.django.DjangoTemplates",
        "DIRS": [BASE_DIR / "templates"],  # <-- add
        "APP_DIRS": True,
        "OPTIONS": {
            "context_processors": [
                "django.template.context_processors.debug",
                "django.template.context_processors.request",
                "django.contrib.auth.context_processors.auth",
                "django.contrib.messages.context_processors.messages",
            ],
        },
    },
]

# For dev
ALLOWED_HOSTS = ["*"]

# 4. Add the URL

When you register the app, the next step is URL Routing This allows users to access the pages and API endpoints. Django routes via http request urls.py file.

Configure two sets of routes.

  1. Project-level URL (mlapp/urls.py)) – Includes global routes like the admin panel and routes from predictor App.
  2. App-level URL (predictor/urls.py)) – Define specific routes for web forms and APIs.

open mlapp/urls.py Update as follows:

# mlapp/urls.py
from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path("admin/", admin.site.urls),
    path("", include("predictor.urls")),  # web & API routes
]

Create a new file Predictor/urls.py Defines app-specific routes.

# predictor/urls.py
from django.urls import path
from .views import home, predict_view, predict_api

urlpatterns = [
    path("", home, name="home"),
    path("predict/", predict_view, name="predict"),
    path("api/predict/", predict_api, name="predict_api"),
]

# 5. Create a form

To allow users to interact with the model via the web interface, an input form is required that allows them to enter flower measurements (sepal and Petal dimensions). Django makes this simple with its built-in form module.

Create a simple form class to capture the four numeric inputs that the IRIS classifier requires.

In you Predictors/ Create a new file called app forms.py Add the following code:

# predictor/forms.py
from django import forms

class IrisForm(forms.Form):
    sepal_length = forms.FloatField(min_value=0, label="Sepal length (cm)")
    sepal_width  = forms.FloatField(min_value=0, label="Sepal width (cm)")
    petal_length = forms.FloatField(min_value=0, label="Petal length (cm)")
    petal_width  = forms.FloatField(min_value=0, label="Petal width (cm)")

# 6. Load the model and predict

I trained and saved the Iris classifier, so I need the Django app method Load the model And use it for prediction. Place all prediction-related logic inside a dedicated internal to keep things organized services.py Files predictor App.

This makes our views clean and focus on request/response processing, whilst the prediction logic resides in reusable service modules.

in Predictor/Services.pyadd the following code:

# predictor/services.py
from __future__ import annotations
from pathlib import Path
from typing import Dict, Any
import joblib
import numpy as np

_MODEL_CACHE: Dict[str, Any] = {}

def get_model_bundle():
    """
    Loads and caches the trained model bundle:
    {
      "estimator": RandomForestClassifier,
      "target_names": ndarray[str],
      "feature_names": list[str],
    }
    """
    global _MODEL_CACHE
    if "bundle" not in _MODEL_CACHE:
        model_path = Path(__file__).resolve().parent / "model"https://www.kdnuggets.com/"iris_rf.joblib"
        _MODEL_CACHE["bundle"] = joblib.load(model_path)
    return _MODEL_CACHE["bundle"]

def predict_iris(features):
    """
    features: list[float] of length 4 (sepal_length, sepal_width, petal_length, petal_width)
    Returns dict with class_name and probabilities.
    """
    bundle = get_model_bundle()
    clf = bundle["estimator"]
    target_names = bundle["target_names"]

    X = np.array([features], dtype=float)
    proba = clf.predict_proba(X)[0]
    idx = int(np.argmax(proba))
    return {
        "class_index": idx,
        "class_name": str(target_names[idx]),
        "probabilities": {str(name): float(p) for name, p in zip(target_names, proba)},
    }

# 7. View

View It acts as an adhesive between user input, model, and final response (HTML or JSON). In this step, you will create three views.

  1. house – Renders prediction forms.
  2. predict_view – Process form submissions through the web interface.
  3. predict_api – Provides a JSON API endpoint for programmatic prediction.

in Predictor/Views.pyadd the following code:

from django.http import JsonResponse
from django.shortcuts import render
from django.views.decorators.http import require_http_methods
from django.views.decorators.csrf import csrf_exempt  # <-- add
from .forms import IrisForm
from .services import predict_iris
import json


def home(request):
    return render(request, "predictor/predict_form.html", {"form": IrisForm()})


@require_http_methods(["POST"])
def predict_view(request):
    form = IrisForm(request.POST)
    if not form.is_valid():
        return render(request, "predictor/predict_form.html", {"form": form})
    data = form.cleaned_data
    features = [
        data["sepal_length"],
        data["sepal_width"],
        data["petal_length"],
        data["petal_width"],
    ]
    result = predict_iris(features)
    return render(
        request,
        "predictor/predict_form.html",
        {"form": IrisForm(), "result": result, "submitted": True},
    )


@csrf_exempt  # <-- add this line
@require_http_methods(["POST"])
def predict_api(request):
    # Accept JSON only (optional but recommended)
    if request.META.get("CONTENT_TYPE", "").startswith("application/json"):
        try:
            payload = json.loads(request.body or "{}")
        except json.JSONDecodeError:
            return JsonResponse({"error": "Invalid JSON."}, status=400)
    else:
        # fall back to form-encoded if you want to keep supporting it:
        payload = request.POST.dict()

    required = ["sepal_length", "sepal_width", "petal_length", "petal_width"]
    missing = [k for k in required if k not in payload]
    if missing:
        return JsonResponse({"error": f"Missing: {', '.join(missing)}"}, status=400)

    try:
        features = [float(payload[k]) for k in required]
    except ValueError:
        return JsonResponse({"error": "All features must be numeric."}, status=400)

    return JsonResponse(predict_iris(features))

# 8. Template

Finally, we create an HTML template that acts as the user interface for the IRIS predictor.

This template is as follows:

  • Renders the previously defined Django form field.
  • It provides a clean, styled layout with responsive form input.
  • Displays prediction results if available.
  • Please refer to API endpoints for developers who prefer programmatic access.








Iris Predictor












Enter Iris flower measurements to get a prediction.

{% csrf_token %}

{{ form.sepal_length }}

{{ form.sepal_width }}

{{ form.petal_length }}

{{ form.petal_width }}

{% if submitted and result %}
Predicted class: {{ result.class_name }}
Probabilities:
    {% for name, p in result.probabilities.items %}
  • {{ name }}: {{ p|floatformat:3 }}
  • {% endfor %}
{% endif %}

API available at POST /api/predict/

# 9. Run the application

With everything in place, it's time to run your Django project and test both your web form and your API endpoint.

Run the following command to set the default django database (admin, session, etc.):

Start the Django development server:

python manage.py runserver

If everything is set up correctly, you will see a similar output.

Watching for file changes with StatReloader
Performing system checks...

System check identified no issues (0 silenced).
September 09, 2025 - 02:01:27
Django version 5.2.6, using settings 'mlapp.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CTRL-BREAK.

Open your browser and go to http://127.0.0.1:8000/ to use the web forms interface.

Building machine learning applications using DjangoBuilding machine learning applications using Django

Building machine learning applications using DjangoBuilding machine learning applications using Django

You can also use Curl to send POST requests to the API.

curl -X POST http://127.0.0.1:8000/api/predict/ \
  -H "Content-Type: application/json" \
  -d '{"sepal_length":5.1,"sepal_width":3.5,"petal_length":1.4,"petal_width":0.2}'

Expected response:

{
  "class_index": 0,
  "class_name": "setosa",
  "probabilities": {
    "setosa": 1.0,
    "versicolor": 0.0,
    "virginica": 0.0
  }
}

# 10. test

Before we close, we recommend that you make sure your application works as expected. Django offers built-in Test Framework It is integrated with Python's unittest Module.

I'll write some simple tests like this:

  1. Homepage rendering Correct and contains the title.
  2. API Endpoints Returns a valid predictive response.

in predictor/tests.pyadd the following code:

from django.test import TestCase
from django.urls import reverse

class PredictorTests(TestCase):
    def test_home_renders(self):
        resp = self.client.get(reverse("home"))
        self.assertEqual(resp.status_code, 200)
        self.assertContains(resp, "Iris Predictor")

    def test_api_predict(self):
        url = reverse("predict_api")
        payload = {
            "sepal_length": 5.0,
            "sepal_width": 3.6,
            "petal_length": 1.4,
            "petal_width": 0.2,
        }
        resp = self.client.post(url, payload)
        self.assertEqual(resp.status_code, 200)
        data = resp.json()
        self.assertIn("class_name", data)
        self.assertIn("probabilities", data)

Run the following command on the terminal:

You will see a similar output.

Found 2 test(s).
Creating test database for alias 'default'...
System check identified no issues (0 silenced).
..
----------------------------------------------------------------------
Ran 2 tests in 0.758s
                                                                                
OK
Destroying test database for alias 'default'...

Once these tests pass, you can be sure that your Django + Machine Learning app is working correctly end-to-end.

# summary

We created a complete machine learning application using the Django framework, bringing all our components together into a functional system.

Starting with training and saving, we integrated the model into Django Services for prediction. We also created a clean web form for user input and published the JSON API for programmatic access. Additionally, we implemented automated testing to ensure that the application runs.

The project focused on IRIS datasets, but it could extend the same structure to accommodate more complex models, larger datasets, or production-enabled APIs, providing a solid foundation for real machine learning applications.

Abid Ali Awan (@1abidaliawan) is a certified data scientist who loves building machine learning models. Currently he focuses on content creation and creates technical blogs on machine learning and data science technology. Abid holds a Masters degree in Technology Management and a Bachelor of Arts degree in Telecommunications Engineering. His vision is to build AI products using graph neural networks for students suffering from mental illness.



Source link

Leave a Reply

Your email address will not be published. Required fields are marked *