--- title: "Combine Django, Vue.js, Vuetify and TypeScript" date: 2021-06-27T09:18:41+03:00 draft: false --- Let's combine Django, Vue.js, Vuetify and TypeScript into a complete toolchain for your next web project 🚀🚀 Benefits: * Automatic reloading of front-end (TypeScript, Vue.js, Vuetify) and back-end (Python, Django) during development for quick code iterations * [Django](https://www.djangoproject.com/) got you covered for web development, from excellent database tools to user authentication, and everything between * [Vue.js](https://vuejs.org/) makes it easy to write interactive user interfaces * [Vuetify](https://vuetifyjs.com/en/) gives you a toolbox of graphical user interface components that follow the Material Design-framework * [TypeScript](https://www.typescriptlang.org/) extends JavaScript with typing to catch JavaScript errors before running the code * You'll have a self-contained application to deploy in production with static assets. No separate front-end project. Notes: * You need to have Python ≥ 3.6 and Node.js installed * We'll use Vue.js 2.x since Vue.js 3 is still in alpha * You can use [Flask](https://flask.palletsprojects.com/en/2.0.x/) or other Python web frameworks instead of Django. Actually, you could use any web framework, including [Actix Web](https://actix.rs/) for Rust, since the same principles apply for connecting the front-end with the back-end. * This tutorial uses [whitenoise](http://whitenoise.evans.io/en/stable/) with Django to serve static assets in production builds. You might want to switch to a [CDN](https://en.wikipedia.org/wiki/Content_delivery_network) if you have a high-traffic system. You just need to configure the *DEPLOYMENT_PATH* variable in *vue.config.js* to match your CDN URL addresses. Alright, let's go. First we need the official Vue.js standard tooling and to create a tutorial project folder. ```bash # Install the official Vue.js command line tool npm install -g @vue/cli # Create a project folder mkdir tutorial ``` ## Create Vue.js front-end Create a new Vue.js 2.x project. ```bash # current directory: tutorial # Create a new Vue.js 2.x project in tutorial/client vue create client # Choose "Manually select features" # Select TypeScript with spacebar # Press enter # Choose Vue version 2.x # Use class-style component syntax?: n # Use Babel alongside TypeScript...: Y # Linter: ESLint with error prevention only # Pick additional lint features: Lint on save # Where do you prefer...: In dedicated config files # Save this as a preset for future projects?: N # Install Vuetify cd client vue add vuetify # Choose the default option on prompt # Tracker to connect Django and webpack bundles npm install -D webpack-bundle-tracker ``` Replace the existing content in `tutorial/client/vue.config.js` with the following: ```js const BundleTracker = require("webpack-bundle-tracker"); const DEPLOYMENT_PATH = '/static/' module.exports = { runtimeCompiler: true, pages: { index: { entry: "./src/main.ts", chunks: ["chunk-vendors"] } }, publicPath: process.env.PROJECT_MODE === 'production' ? DEPLOYMENT_PATH : 'http://localhost:8080/', outputDir: "../server/example/static/", chainWebpack: config => { config.optimization.splitChunks(false) config.plugin('BundleTracker').use(BundleTracker, [{filename: '../server/webpack-stats.json'}]) config.resolve.alias.set('__STATIC__', 'static') config.devServer .public('http://0.0.0.0:8080') .host('0.0.0.0') .port(8080) .hotOnly(true) .watchOptions({poll: 1000}) .https(false) .headers({"Access-Control-Allow-Origin": ["*"]}) }, transpileDependencies: ["vuetify"] } ``` We will create the server Django project and the example app very soon. The above `vue.config.js` does the following: * The front-end is served from http://localhost:8080 during development (PROJECT\_MODE=development) * Production version (PROJECT\_MODE=production) of the front-end is written into `tutorial/server/example/static` as a static build (`outputDir`). We'll create a Django app called example, which finds these static files with default configuration. * `webpack-stats.json` informs Django how to access the front-end assets during development and production * The front-end reloads automatically after changes * The resulting bundle is named *index*. Notice that you could output multiple bundles with different TypeScript entry scripts. ## Create Django back-end Install Django for back-end. ```bash # current directory: tutorial # Python virtual environment at tutorial/local/venv python3 -m venv local/venv source local/venv/bin/activate # Upgrade the installation tools pip install --upgrade setuptools pip wheel # Install Django, whitenoise is for serving static assets pip install Django whitenoise # Create a Django project in tutorial/server django-admin startproject server # Create a new Django app for your project cd server python manage.py startapp example ``` Now let's register the new Django app by modifying the existing Django project settings `tutorial/server/server/settings.py`: ```python # Add the following at the beginning of the file: import os # Add the following at the end of the file: STATS_FILE = os.path.join(BASE_DIR, 'webpack-stats.json') # Add the following in the INSTALLED_APPS list: 'example.apps.ExampleConfig' # Add the following in the MIDDLEWARE list # below 'django.middleware.security.SecurityMiddleware' # see http://whitenoise.evans.io/en/stable/ 'whitenoise.middleware.WhiteNoiseMiddleware' ``` Now let's route the views from the example app in the root project. Replace the existing content in `tutorial/server/server/urls.py` with the following: ```python from django.contrib import admin from django.urls import path, include urlpatterns = [ path('', include('example.urls')), path('admin/', admin.site.urls) ] ``` We'll create register an index page in the example app. It will serve the automatically generated Vue.js and Vuetify example page through Django. Write the following in a new file `tutorial/server/example/urls.py` ```python from django.urls import path from . import views urlpatterns = [ path('', views.index, name='index') ] ``` Finally it's time to create the actual view that we already registered previously. Replace the existing content in `tutorial/server/example/views.py` with the following: ```python import json import pathlib from django.shortcuts import render from django.http import HttpResponse from django.conf import settings class WebpackStatsProcessing: def __init__(self): self.data = json.loads(pathlib.Path(settings.STATS_FILE).read_text('utf-8')) def get_tags(self, bundle: str) -> str: include_tags = [] for asset in self.data['chunks'][bundle]: if asset.endswith('.map'): continue location = self.data['assets'][asset]['publicPath'] if asset.endswith(('.js', 'js.gz')): include_tags.append(f'') include_tags.append(f'') if asset.endswith(('.css', '.css.gz')): include_tags.append(f'') return '\n'.join(include_tags) webpack_stats_tracker = WebpackStatsProcessing() def index(request): return HttpResponse(render(request, 'example/vuetify_bundle.html', context={ 'bundle': webpack_stats_tracker.get_tags('index'), 'title': 'Index page from Example Django app' })) ``` The class *WebpackStatsProcessing* is an example on how to parse *webpack-stats.json* to load a webpack bundle (Vue.js, Vuetify, ...) and output the required script and style tags for Django HTML templates. As you see in the *index* view, the *bundle* context parameter contains the required tags. The file *webpack-stats.json* routes the assets through the development front-end server during development and a static asset build in a production. The only thing missing anymore is the actual Django HTML template that is rendered by the *index* view. First, create a new folder `tutorial/server/example/templates/example` and then write the following content in a new file `tutorial/server/example/templates/example/vuetify_bundle.html` ```html