inertia-django
inertia-django copied to clipboard
The Django adapter for Inertia.js
Inertia.js Django Adapter
Warning: This project is in a very early stage and depends on another early stage library and its frontend adapters called Inertia.js.
Requirements
This package is meant to be used in a Django application and it uses django-rest-framework's serializers.
$ pipenv install django djangorestframework django-webpack-loader
Installation
There is still a few too many manual steps involved using inertia-django. I'm looking for ways (and people to help) to make the initial setup more smooth.
Install the inertia-django package from PyPI:
$ pipenv install inertia-django
Django app template
The easiest way to get started is with the inertia-django-boilerpate.
Webpack 📦
INFO: For now it might be the easiest to take a look or clone the example repository.
You have to use webpack for the Dynamic Imports Feature. The following config is borrowed from the Django Inertia.js Example. It uses a few plugins that are not necessary like PurgeCSS, Tailwind, MiniCSSExtract, etc. But the important ones are:
- Webpack Bundle Tracker for Django Webpack Loader.
- Vue Loader is optional, but there is only the inertia-vue adapter yet.
webpack.config.js:
const path = require("path");
const BundleTracker = require('webpack-bundle-tracker');
const VueLoaderPlugin = require('vue-loader/lib/plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const glob = require("glob-all");
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const PurgecssPlugin = require("purgecss-webpack-plugin");
class TailwindExtractor {
static extract(content) {
return content.match(/[A-Za-z0-9-_:\/]+/g) || [];
}
}
module.exports = {
mode: 'development',
devtool: 'inline-source-map',
// TODO: adjust the entry points to your project
entry: ["./core/assets/js/index.js", "./core/assets/css/index.postcss"],
output: {
publicPath: "/static/bundles/",
filename: "[name]-[hash].js",
chunkFilename: '[name]-[hash].js',
path: path.resolve('./bundles/'),
},
plugins: [
new BundleTracker({ filename: './webpack-stats.json' }),
new VueLoaderPlugin(),
new CleanWebpackPlugin(),
new MiniCssExtractPlugin({
filename: "[name]-[hash].css"
}),
new PurgecssPlugin({
paths: glob.sync([
// TODO: adjust the directories to your project
path.join(__dirname, "core/assets/js/**/*.vue"),
path.join(__dirname, "core/templates/index.html")
]),
extractors: [
{
extractor: TailwindExtractor,
extensions: ["html", "js", "vue"]
}
]
})
],
module: {
rules: [
{
test: /\.m?js$/,
exclude: /(node_modules|bower_components)/,
use: {
loader: 'babel-loader',
options: {
presets: [
'@babel/preset-env'
],
plugins: ["@babel/plugin-syntax-dynamic-import"]
}
}
},
{
test: /\.vue$/,
use: 'vue-loader'
},
{
test: /\.postcss$/,
use: [
{
loader: MiniCssExtractPlugin.loader,
},
{ loader: 'css-loader', options: { importLoaders: 1 } },
'postcss-loader',
]
}
]
},
resolve: {
extensions: ['.js', '.vue'],
alias: {
'vue$': 'vue/dist/vue.runtime.js',
// TODO: adjust or remove, it's a convenient way to import js files in your components
'@': path.resolve('core/assets/js'),
}
},
}
Usage
render_inertia
function
The easiest way to render a Vue component with inertia-django is to use the render_inertia
function. Note: You have to have an Index.vue
component in your project.
from inertia import render_inertia
def index(request):
# for function views just use the render_inertia function
return render_inertia(request, 'Index', props={'title': 'My inertia-django page'}, template_name='index.html')
This would be a bit much to write everytime so you can omit the template_name
and set it in settings.py:
INERTIA_TEMPLATE = 'index.html'
After that you just have to call:
from inertia import render_inertia
def index(request):
# for function views just use the render_inertia function
return render_inertia(request, 'Index', props={'title': 'My inertia-django page'})
InertiaListView
inertia-django ships with a crude implementations of the generic ListView:
views.py:
class Index(InertiaListView):
# Inertia supports List and DetailViews right now
model = Contact
serializer_class = ContactSerializer
component_name = "Index"
Index.vue
<template>
<Layout>
<h2 class="mb-4">Contacts</h2>
<p>User: {{shared.user.username}}</p>
<ul>
<li :key="contact.id" v-for="contact in contact_list">
<inertia-link :href="'/contact/' + contact.id">
{{contact.name}}
</inertia-link>
</li>
</ul>
</Layout>
</template>
<script>
import { InertiaLink } from "inertia-vue";
import Layout from "@/Components/Layout";
export default {
props: ["contact_list", "shared"],
components: { Layout, InertiaLink }
};
</script>
InertiaDetailView
inertia-django ships with a crude implementations of the generic DetailView:
views.py:
class ContactView(InertiaDetailView):
model = Contact
serializer_class = ContactSerializer
component_name = "Contact"
props = {"test": True} # you can inject any props you want
Contact.vue:
<template>
<Layout>
<inertia-link href="/">Home</inertia-link>
<h2 class="mb-4">{{contact.name}}, {{contact.first_name}}</h2>
<p>Age: {{contact.age}}</p>
<p>Test: {{test}}</p>
</Layout>
</template>
<script>
import { InertiaLink } from "inertia-vue";
import Layout from "@/Components/Layout";
export default {
props: ['contact', 'test'],
components: { Layout, InertiaLink }
};
</script>
inertia.share
function
If you want to have some basic props that are always injected, you can share
them between Components:
I had to declare the
UserSerializer
in apps.py so that it is available at startup. If somebody has a better idea feel free to create an issue.
e.g. apps.py:
from django.apps import AppConfig
from django.contrib.auth import get_user, get_user_model
from rest_framework import serializers
from inertia import share
def current_user(request):
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = get_user_model()
fields = ["username", "email"]
return UserSerializer(get_user(request)).data
class CoreConfig(AppConfig):
name = 'core'
def ready(self):
share('title', 'Django Inertia.js Example 🤘')
share('user', current_user)
As you might have recognized current_user
is a function. While rendering inertia-django checks if a shared property is callable and if it is, it will call that function with the current request.
Example
~~Take a look at this repository for an example of how to use this package.~~