This is a quick guide on how to get get started with building a webapp using the following languages and frameworks:
This isn't meant for learning how to use them, but more how to set them up together. I'll give some simple examples to get the ball rolling, but for more specific things, check out the documentation linked above.
This setup of a Django/Vue.js app isn't the only way to do things, and perhaps may not be the best or most preferred by everyone, but it works. Since I like using all these together, I've made an all-in-one guide for myself to set everything up from scratch. It also covers some general aspects of creating a project like GitHub actions for CI, and using linters to keep your code consistent. You can skip whichever smaller parts you don't need, or any parts for which you'll use a different tool/service (like deploying on something other than Heroku and Firebase, etc.), but for anyone who would want to use all these tools together as well, this should hopefully be enough to get everything set up!
Create project directory
mkdir myproject
cd myproject
Create virtual environment
virtualenv .venv
source .venv/bin/activate
Install Django and DRF
pip install django
pip install djangorestframework
django-admin startproject myproject
cd myproject
Add the Django rest framework to your INSTALLED_APPS
in ./myproject/settings.py
.
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'rest_framework'
]
Ensure pg_config is available on your system.
pg_config
If not (it gives a command not found
error), check here for more info. on how to add it.
Install psycopg2
pip install psycopg2
By default, Django uses SQLite3, change ./myproject/settings.py
to use Postgres instead.
import os
...
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql_psycopg2',
'NAME': '<db_name>',
'USER': '<db_username>',
'PASSWORD': '<password>',
'HOST': '<db_hostname_or_ip>',
'PORT': '<db_port>',
}
}
Start up postgres, and create the database you want to use. In this case, myproject
.
createdb myproject
Change variables to point to your postgres database, and generate and make migrations.
python manage.py makemigrations
python manage.py migrate
Create the superuser
and enter necessary information.
python manage.py createsuperuser
Run the server to test things out!
python manage.py runserver
Go to the /admin
page and log in to verify that superuser is working correctly.
CTRL-C
to stop the server and move on.
pip freeze > requirements.txt
This will create a file or pip packages that can be installed with one command to make it easier to get your project running in a different environment, and should be executed every time you add a new package with pip.
To install packages from a file, run
pip install -r requirements.txt
This part is entirely up to whatever API you're building, but I will make one for films and directors as an example.
python manage.py startapp films
Add the app to your INSTALLED_APPS
in ./myproject/settings.py
.
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'rest_framework',
'films'
]
Add models to ./films/models.py
file.
from django.db import models
class Director(models.Model):
name = models.CharField(max_length=50)
birthday = models.DateField()
def __str__(self):
return f"{self.name}"
class Film(models.Model):
title = models.CharField(max_length=120)
release = models.DateField()
runtime = models.IntegerField()
director = models.ForeignKey("Director", on_delete=models.CASCADE)
def __str__(self):
return f"{self.title} ({self.release})"
If, on VSCode, you open your
./films/models.py
file and notice acannot import 'django.db'
error at the top. Do the following:
CMD-SHIFT-P
> Python: Select Interpreter > Enter interpreter path... > "/home/Projects/django-vue/.venv/bin/python3" (You must find this directory on your own bycd
-ing into the.venv
directory you created, navigating tobin
and adding/python3
to that path. More info here)Next, go to Settings > Workspace Settings > (Search: venv) > Python: Venv path > "/home/Projects/django-vue/" (This is the same path as before, except it's the folder that holds the
.venv
file in it. In other words, just drop the.venv/bin/python3
from the path and you should be OK.)This will create a
.vscode
folder with asettings.json
file at the base of your workspace directory, and should also fix thecannot import
issue.
Next, make and migrate for your new models.
python manage.py makemigrations films
python manage.py migrate films
Check out your Postgres database and run some simple commands to make sure it works!
You should see tables have been created in the database for each of your models. There will be no data in there yet, but you can insert or select as you please from the psql command line.
Register your models in you ./films/admin.py
file.
from django.contrib import admin
from films.models import Director, Film
admin.site.register(Director)
admin.site.register(Film)
Now you can run your server again, and add objects to your database through the admin page.
If you already know how to set up a Django Rest Framework API and just needed to know how to use and configure Postgres, skip this section.
In your ./films
folder, create a file called serializers.py
. In there, create your ModelSerializers
from rest_framework import serializers
from .models import Director, Film
class DirectorSerializer(serializers.ModelSerializer):
class Meta:
model = Director
fields = ['name', 'birthday']
class FilmSerializer(serializers.ModelSerializer):
class Meta:
model = Film
fields = ['title', 'release', 'runtime', 'director']
Now we have Models and Serializers for those models. We'll now have to create views and urls to access that data using our API.
Create views first in your ./films/views.py
file. This is quite a bit more code than some of the previous topics, so I only create them for directors here.
from rest_framework import status
from rest_framework.views import APIView
from rest_framework.response import Response
from django.shortcuts import get_object_or_404
from .models import Director, Film
from .serializers import DirectorSerializer, FilmSerializer
class DirectorAPIView(APIView):
def get(self, request):
directors = Director.objects.all()
item = self.request.query_params.get("item", "")
if item != "":
directors = directors.filter(name=item)
serializer = DirectorSerializer(directors, many=True)
return Response(serializer.data)
def post(self, request):
serializer = DirectorSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(status=status.HTTP_201_CREATED)
return Response(data=serializer.errors, status=status.HTTP_400_BAD_REQUEST)
class DirectorDetailAPIView(APIView):
def get(self, request, director_id):
director = get_object_or_404(Director, id=director_id)
serializer = DirectorSerializer(director)
return Response(data=serializer.data)
Next, make a ./films/urls.py
file, and create your URLs there. Again, These are just URLs for the two views I made.
from django.urls import path
from .views import DirectorAPIView, DirectorDetailAPIView
director_api = DirectorAPIView.as_view()
director_detail_api = DirectorDetailAPIView.as_view()
urlpatterns = [
path("directors", director_api, name="directors"),
path("directors/<int:director_id>", director_detail_api, name="director-detail"),
]
Finally, set up the URL pattern in your ./myproject/urls.py
file to access your API.
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('api/', include('films.urls'))
]
Load up your server, got to /api/<whatever your urls are> and you should see whatever data you have in your database. You've made a REST API with Django and PostgreSQL!
You can write custom commands to run from the command line with manage.py
. For example, if you have data you scraped from a sitte and want to populate your database, or if you need to fill your database with dummy data for production.
First create the file that will contain all the data you want to fill your database with. In your app directory, create a folder called management
and a folder called commands
within there. In your management/commands
folder, create a file with a name like _directors.py
. The _
will make sure you can't call it as a command, and you can put your data in there in dictionary form.
Inside that file, create your data.
data = {
"directors": [
{
"name": "Stanley Kubrick",
"birthday": "1928-07-26"
},
{
"name": "Robert Zemeckis",
"birthday": "1952-05-14"
},
{
"name": "Martin Scorsese",
"birthday": "1942-11-17"
},
{
"name": "Steven Spielberg",
"birthday": "1946-12-18"
}
]
}
This file doesn't have to be in this directory, and it doesn't have to be a
.py
file, but that's easiest for import and using python dictionary objects.
Next, in your management/commands
folder, create a .py
file named after your command. If you name a command after an existing Django, it won't override unless your app is higher up in the INSTALLED_APPS
list in settings.py
. For more info on that, and about writing custom commands as a whole, see the docs.
In my case I made a populate.py
file and added the following.
from django.core.management.base import BaseCommand
from films.models import Director
from ._directors import data
class Command(BaseCommand):
help = "Populate the Directors table"
def handle(self, *args, **options):
directors = []
for director in data["directors"]:
try:
directors.append(Director(**director))
except Exception as e:
print(e)
Director.objects.bulk_create(directors)
Note: I can create an object with
**
because my fields in my data file match my models. If you scraped from the web and this isn't the case, just create the object manually.
Now running the command will populate your table!
python manage.py populate
To add an argument to your command (for exdample, --clear
to completely clear your table) add the add_arguments
method to your Command
class. Then update handle
to check if the argument was passed.
class Command(BaseCommand):
# ...
def add_arguments(self, parser):
parser.add_argument(
'--clear',
action='store_true',
help='Delete clear the table',
)
def handle(self, *args, **options):
directors = []
for director in data["directors"]:
try:
if options["clear"]:
Director.objects.filter(name=director["name"], birthday=director["birthday"]).delete()
else:
directors.append(Director(**director))
except Exception as e:
print(e)
Director.objects.bulk_create(directors)
The
try
now checks if the--clear
flag was passed. If so, it deletes every director that's in the_directors.py
file.
Now running...
python manage.py populate --clear
will clear the database.
To write automated test in Django, create and ./films/tests
directory, and delete the ./films/tests.py
file. You could alternatively write all your tests in the tests.py
file and not add a directory, but it's usually nicer to have the option to separate tests into different categories as your project scales up.
In your newly created test directory, create you empty __init__.py
file. Then add a file for some test functions. Here, I'll make one to test the models: test_models.py
. A simple test will look something like this:
from django.test import TestCase
from ..models import Director
class DirectorTests(TestCase):
def test_NameToTitleCase(self):
# Arrange
name = "alFred hitchCoCK"
director = Director(name=name, birthday="1899-08-13")
# Act
director.save()
# Assert
self.assertEqual(director.name, "Alfred Hitchcock")
I added a function in my Director
model to turn the name into title case:
def save(self, *args, **kwargs):
self.name = self.name.title()
super().save(*args, **kwargs)
To make sure it works, simply run run your tests from your top-level project directory:
python manage.py test films
There are two main ways to set up an app with Django and Vue.
The first way is to have Django and Vue act entirely separately. With this method, you build an API with Django, deploy that, then build a Vue frontend that calls that API from another location.
The second is to have your frontend and backend work as one, and just have Django bundle a built Vue folder and display it on its own. To deploy an app built this way, you will essentially be deploying one Django app, and just use Vue to build the frontend that Django is in charge of showing.
There are pros and cons to both, depending on your situation, but in the end you'll be left with a very similar product.
For the first method, all you have to do is create a Vue app1, the only real configuration comes when you make calls to the backend later o, and when you deploy. I'll show the linking for the second methods, but for deployment It will be assumed you used the first.
1 This can be done in an etirely separate repository, or you can add a frontend right into your current Django project directory. Personally, I usually go with the ladder, but it shouldn't make any difference.
In your top level directory, create a ./templates
folder and add the index.html
file that you point to above.
This file will use Django templating to render our Vue app.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>My Project</title>
</head>
<body>
<div id="app"></div>
</body>
</html>
Now create a path to your IndexTemplateView
and import it in your ./myproject/urls.py
file. I'll only show the lines you need to add so be careful not to overwrite existing functionality you've build in to your API. First import:
from django.urls import re_path
from django.views.generic.base import TemplateView
If you already import anything from
django.urls
then don't delete it, just addre_path
. This allows us to create paths using regular expressions.
Then add the URL to your urlpatterns
array:
re_path(r'^(?!api(/)?|admin(/)?).*$', TemplateView.as_view(template_name="index.html"), name="entry-point")
The regex
r'^(?!api(/)?|admin(/)?).*$'
will match any path that is notapi/
oradmin/
. In other words, it will always show our Vue app unless we want to look at the api or admin pages.
Then add the ./templates
directory to your settings.py file.
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [os.path.join(BASE_DIR, 'templates')],
'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',
],
},
},
]
Create your Vue frontend from your top-level project directory.
vue create frontend
In my case I opt to only use Babel, Router, ESLint (Which we'll configure later) and Vue 2. Use whatever configuration works for you.
To run your development server, just move into the directory and run with npm.
cd frontend
npm install
npm run serve
If using git, you'll also probably want to delete the
.git
file that's created in your frontend directory. From the frontend directory, simply runrm -rf .git
.
CTRL-C
to stop the server and move on.
We want to have the Vue.js server and Django server running at the same time, and have our Vue files mounted in our Django templates. To do this, install webpack-bundle-tracker
from your frontend directory.
npm install [email protected]
I've noticed issues with newer versions failing to create the
webpack-stats.json
file talked about below. You cannpm install
the latest version and see if it works before installing version 0.4.3 instead if you prefer.
Next we'll have to add a ./frontend/vue.config.js
file and configure it.
const BundleTracker = require('webpack-bundle-tracker');
module.exports = {
publicPath: 'http://0.0.0.0:8080/',
outputDir: './dist/',
chainWebpack: config => {
config
.plugin('BundleTracker')
.use(BundleTracker, [{filename: './webpack-stats.json'}])
config.output
.filename('bundle.js')
config.optimization
.splitChunks(false)
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)
.disableHostCheck(true)
.headers({'Access-Control-Allow-Origin': ['\*']})
},
// uncomment before executing 'npm run build'
// css: {
// extract: {
// filename: 'bundle.css',
// chunkFilename: 'bundle.css',
// },
// }
};
Now running npm run serve
will create a webpack-stats.json
file that will be read by Django and injected in the index.html
file.
Go back to your top-level directory and install django-webpack-loader
.
cd ..
pip install django-webpack-loader
Add it to your ./myproject/settings.py
file's INSTALLED_APPS
.
INSTALLED_APPS = [
# ...
'webpack_loader',
# ...
]
Also in settings, add a configuration for the webpack loader at the bottom.
WEBPACK_LOADER = {
'DEFAULT': {
'BUNDLE_DIR_NAME': 'dist/',
'STATS_FILE': os.path.join(BASE_DIR, 'frontend', 'webpack-stats.json')
}
}
Finally, update your ./templates/index.html
file to load from the webpack loader.
{% load render_bundle from webpack_loader %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>My Project</title>
</head>
<body>
<div id="app"></div>
{% render_bundle 'app' %}
</body>
</html>
You can also delete the
./frontend/public
directory, since your Vue components will be mounted with Django now instead.
Now run your Vue server in one terminal...
cd frontend
npm run serve
and run the Django server in another.
python manage.py runserver
Then go to localhost
at port 8000
and you should see your Vue frontend running with your Django backend!
However there's one last problem. When clicking links in our sample page that Vue creates for us we see that our URL changes to http://localhost:8000/http:/0.0.0.0:8080/
which is ugly and weird. To fix this, go to ./frontend/src/router/index.js
and remove the line in the VueRouter
initialization that says:
base: process.env.BASE_URL,
Now your URLs should be pretty and normal!
This part is only necessary if you have separate Vue and Django apps. If you linked them with webpack, you can skip this part.
In your ./frontend
directory, create two files: .env.development.local
and .env.production.local
. Make sure these are ignored by your .gitignore
file. This will hold environment variables for development and production, respectively, which will allow us to keep track of two separate URLs without changing any code.
In .env.development.local
add:
VUE_APP_URL=http://localhost:8000
In .env.production.local
add:
VUE_APP_URL=[your deployed api URL (Heroku, DigitalOcean, etc.)]
In my experience, the Viariable must be
VUE_APP_URL
or it won't work. You can experiment, of course.
Install js-cookie
and axios
from your frontend directory.
cd frontend
npm install js-cookie
npm install axios
In your ./frontend.src
directory make a directory called api
, and add api.service.js
to it. This file will export the functionality used to make HTTP requests to the backend.
import axios from 'axios'
import Cookies from 'js-cookie'
export default axios.create({
baseURL: process.env.VUE_APP_URL + '/api',
timeout: 5000,
headers: {
'Content-Type': 'application/json',
'X-CSRFToken': Cookies.get('csrftoken')
}
})
If you linked your app with webpack, your
baseURL
should just be'/api'
. Theprocess.env.VUE_APP_URL
is there to pull whichever environment variable we need.
In that same api
directory, create a file for some API calls. You can call this anything you like, but in my case I'll do directors.js
. This is where you make backend calls to get data.
import api from '@/api/api.service'
async function getDirectors() {
return api.get(`/directors`)
.then(response => response.data)
}
export default {
getDirectors
}
Finally, in the <script>
tag of whatever component you want to make the call in, import the api function and get the data.
<script>
import directorsAPI from "@/api/directors";
export default {
name: "Directors",
data() {
return {
directors: []
}
},
async mounted() {
this.directors = await directorsAPI.getDirectors();
}
}
</script>
Now just display your data in the Vue template however you like!
Again, if you linked with webpack, skip this step. You should be good to go at this point!
You'll notice when you try to make a call to your backend you'll get an error saying your request has been blocked by CORS Policy.
To fix this, start by installing django-cors-headers
.
pip install django-cors-headers
Then, in your settings.py
file, add corsheaders
to the INSTALLED_APPS
, and add the CORS middleware above the common middleware, but below the rest. Read more about Django CORS Headers here.
INSTALLED_APPS = [
# ...
'corsheaders',
# ...
]
MIDDLEWARE = [
# ...
'corsheaders.middleware.CorsMiddleware',
'django.middleware.common.CommonMiddleware',
# ...
]
Finally, add your frontend dev server to the CORS_ORIGIN_WHITELIST
setting.
CORS_ORIGIN_WHITELIST = [
'http://localhost:8080'
]
Now making requests to the backend should work just fine.
Go to your frontend folder and install Tailwind and its dependencies
cd frontend
npm install -D tailwindcss@npm:@tailwindcss/postcss7-compat @tailwindcss/postcss7-compat postcss@^7 autoprefixer@^9
Generate a tailwind.config.js
file and postcss.config.js
file
npx tailwindcss init -p
Next, configure Tailwind to purge unused styles in production. The purge: []
attribute in tailwind.config.js
should be changed to purge: ['./index.html', './src/**/*.{vue,js,ts,jsx,tsx}']
Now your tailwind.config.js
should look like this:
module.exports = {
purge: ['./index.html', './src/**/*.{vue,js,ts,jsx,tsx}'],
darkMode: false, // or 'media' or 'class'
theme: {
extend: {},
},
variants: {
extend: {},
},
plugins: [],
}
Create a file in the ./frontend/src/assets/css
directory (if this directory does not exist, create it) called tailwind.css
and add the following
/*! @import */
@import "tailwindcss/base";
@import "tailwindcss/components";
@import "tailwindcss/utilities";
Finally, import tailwind.css
in your ./src/main.js
file
import "./assets/css/tailwind.css";
This is completely optional and based on preference, but it will go through some handy configurations to get things started.
In your tailwind.config.js
file, add a screens
section to the theme
section in module.exports
module.exports = {
// ...
theme: {
screens: {
'2xl': {'max': '1535px'},
// => @media (max-width: 1535px) { ... }
'xl': {'max': '1279px'},
// => @media (max-width: 1279px) { ... }
'lg': {'max': '1023px'},
// => @media (max-width: 1023px) { ... }
'md': {'max': '767px'},
// => @media (max-width: 767px) { ... }
'sm': {'max': '639px'},
// => @media (max-width: 639px) { ... }
}
}
// ...
}
Note: Do not delete everything else in
module.exports
as this example shows. Simply add thescreens
section and leave everything else untouched
Now breakpoints will work for desktop development in mind first.
In other words, adding a flex
class will add the class for every screen, and adding md:flex-col
will change the flex direction to columns for medium screens or smaller, rather than the other way around (flex-col
being active for meduim screens or larger).
Tailwind: Customizing the default breakpoints for your project.
Once you've found a font you like, make note of the @import
and font-family:
parts given by Google Fonts. For example, for Quicksand:
@import url('https://fonts.googleapis.com/css2?family=Quicksand:wght@500&display=swap');
font-family: 'Quicksand', sans-serif;
Next, go to your tailwind.config.js
file and add the fontFamily
section to the theme
section in module.exports
In there, add a class name as a key (this can be anything you like), with and array of strings as a value. The array contains each font-family:
attribute given by Google Fonts, in order:
module.exports = {
// ...
theme: {
fontFamily: {
'quicksand': ['Quicksand', 'sans-serif']
},
// ...
}
// ...
}
Note: Again, do not delete everything else in
module.exports
as this example shows. Simply add thefontFamily
section and leave everything else untouched
Finally, in your ./src/assets/css
directory (or whatever directory your tailwind.css
file is in), add a fonts.css
file if it doesn't already exist.
In there, add your @import
:
/* Quicksand (500), sans-serif */
@import url('https://fonts.googleapis.com/css2?family=Quicksand:wght@500&display=swap');
Go to ./src/main.js
and import your fonts.css
file if it is not already imported.
import "./assets/css/fonts.css";
Now just add the font-quicksand
class (or whatever class name you gave your font) to an element in your webpage to change the font!
Tailwind: Utilities for controlling the font family of an element.
For terminal commands that you run often, a Makefile can make life a lot easier.
Create a ./Makefile
file and populate it with any commands you think you need.
run-frontend:
cd frontend && npm run serve
run-backend:
python manage.py runserver
run-test:
python manage.py test films
run-lint:
scripts/lint.sh
The lint command and linting script is new, but will be covered next. Also, adding
run-
is technically unnecessary, but you can't have target names be the same as a folder or file where the makefile is, so that's why they're there.
From there, running make frontend
(or any command you add) will run the commands underneath!
Install Flake8 in your top-level project directory.
pip install flake8
Create a .flake8
file in your project directory and add any configurations.
[flake8]
exclude =
*migrations*,
test_*
max-line-length = 119
To lint your python code, run the linter.
flake8 films --config=.flake8
Requires ESLint to be installed as a VSCode extention.
If you installed ESLint when setting up your Vue project, go to whatever configuration file you chose (either package.json
or .eslintrc.js
) and change the rules to fit your needs. For a list of rules you can apply, check out the Prettier, ESLint, or ESLint-Vue Plugin documentation.
"rules": {
"quotes": ["warn", "single"],
"semi": ["warn", "always"],
"indent": ["warn", 2],
"no-multi-spaces": ["warn"]
}
Next, in your .vscode/settings.json
file, add the settings to set the ./frontend
directory as the directory in which ESLint will work, as well as settings to lint on save.
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true
},
"eslint.validate": [
"javascript"
],
"eslint.workingDirectories": [
"./frontend"
]
ESLint should lint on save now for your Javascript and Vue files. If not, reinstalling it from your ./frontend
directory usually works for me.
npm install -D eslint
In your ./frontend/src/package.json
file change your lint script to run with the --fix
option.
"scripts": {
"serve": "vue-cli-service serve",
"build": "vue-cli-service build",
"lint": "vue-cli-service lint --fix"
}
Now running npm run lint
will auto fix any errors in your code.
Add a ./scripts
folder and add lint.sh
to it.
#!/bin/bash
set -e
set -v
python -m flake8 films --config=.flake8
The
set -e
flag will exit the terminal on an error. Later this will prevent us from pushing code with linting errors.
Give permissions to make your script executable
chmod u+x scripts/lint.sh
In your repository directory add a .github/workflows
folder and create a actions.yml
file in it.
name: myproject
on: [push]
jobs:
build:
container: python:3.8-buster
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Install dependencies
run: make install
- name: Lint Backend
run: make run-lint
The
run: make run-lint
line runs from the makefile created earlier. If you didn't add a makefile, just add the command to run the script. Because of theset -e
flag, our push will abort on lint errors.
Now on every push Github actions will tell you if there are lint errors that need to be corrected.
This assumes you build your app with a separate frontend and backend API as outlines here
This assumes you have some knowledge of deploying to Heroku (at least the very basics of using the site itself). To learn more about Using Django Apps on Heroku look here and here.
This part should work with
os.environ.get
, and can be skipped if that's the way you want to go, but storing environment varables in a file is easy to manage so I went with that route.
You'll want a place to put your environment variables once you deploy. To do this install python-decouple
.
pip install python-decouple
Next, create a .env
file in your root directory (make sure to add it to .gitignore
) and add your variables.
SECRET_KEY='secret'
Note: This file is not for production variables, this is a place to store environment variables for development. Production variables are set on Heroku's end.
Finally, in settings.py
, import and updated any variables you want hidden in production.
from decouple import config
# ...
SECRET_KEY = config('SECRET_KEY')
Heroku's default variable for their database is DATABASE_URL
. Change your database settings to use that in production.
DATABASES = {
'default': {
# ...
'HOST': config('DATABASE_URL', 'localhost'),
# ...
}
}
Once you push, migrate your tables
heroku run python manage.py migrate
First, install gunicorn
if you don't already have it.
pip install gunicorn
Now create a Procfile
in your root directory and add the following.
web: gunicorn myproject.wsgi
myproject
is the name of whatever directory contains yourwsgi.py
file.
Install and configure django-heroku
.
pip install django-heroku
Then in settings.py
.
import django_heroku
# ...
# Bottom of settings.py
django_heroku.settings(locals())
Add your Heroku app URL to your ALLOWED_HOSTS
variable.
ALLOWED_HOSTS = [
'django-vue-postgres.herokuapp.com'
]
Finally, add Django's staticfiles
directory to your gitignore list. Then run collectstatic
python manage.py collectstatic
Now push and make sure it runs on Heroku's end after it installs all the Python dependencies.
If everything works, run any commands you have to populate your database (if you have them), and you're good to go!
heroku run python manage.py populate
This part is much, much easier than backend deployment. Again, it assumes you have some knowledge of the basics deploying to Firebase.
Create a Firebase project.
From your ./frontend
directory, intitialize firebase.
firebase init
These are the settings you'll want to check off:
? Which Firebase CLI features do you want to set up for this folder? Press Space to select features, then Enter to confirm your choices.
◯ Database: Configure Firebase Realtime Database and deploy rules
◯ Firestore: Deploy rules and create indexes for Firestore
◯ Functions: Configure and deploy Cloud Functions
❯◉ Hosting: Configure and deploy Firebase Hosting sites
◯ Storage: Deploy Cloud Storage security rules
◯ Emulators: Set up local emulators for Firebase features
◯ Remote Config: Get, deploy, and rollback configurations for Remote Config
❯ Use an existing project
❯ myproject
? What do you want to use as your public directory? dist
? Configure as a single-page app (rewrite all urls to /index.html)? Yes
? Set up automatic builds and deploys with GitHub? No
Once your project is set up, build and deploy!
npm run build
firebase deploy
Simply add your firebase URL to the CORS_ORIGIN_WHITELIST
variable
CORS_ORIGIN_WHITELIST = [
'http://localhost:8080',
'https://django-vue-postgres.web.app'
]
You've built and deployed a functional webapp with Django, Vue.js, PostgreSQL, and more!