Modern web apps in Angular 5 and Django
So you want to check how Angular 5 works, you complete their (nice) tutorial, and you are left with an app that works with a fake in-memory database and doesn’t connect to any real REST API backend.
Let’s see a way to connect it to a Django Rest Framework (DRF) app.
First of all, we move all the angular stuff to a frontend
directory:
cd angular-tour-of-heroes
mkdir frontend
mv * frontend/
mv .angular-cli.json frontend/
Then we create a new virtualenv and django project:
virtualenv -p python3 .virtualenvs/heroes
source .virtualenvs/heroes/bin/activate
pip install django
pip install djangorestframework
django-admin.py startproject angular_tour_of_heroes .
In angular_tour_of_heroes/settings.py
add the apps ‘heroes’ and ‘rest_framework’ in the INSTALLED_APPS
vector, and add the following at the end:
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': [
'rest_framework.permissions.AllowAny'
],
'PAGE_SIZE': 10
}
Start a new django app:
python manage.py startapp heroes
Define a model for the Hero class in heroes/models.py
:
class Hero(models.Model):
name = models.TextField(default='')
Define a serializer in a new file heroes/serializers.py
(the id
field is generated by Django as the primary key):
from rest_framework import serializers
from models import Hero
class HeroSerializer(serializers.ModelSerializer):
class Meta:
model = Hero
fields = ('id', 'name')
Let’s define a URL where to view the list of heroes, in angular_tour_of_heroes/urls.py
:
from django.conf.urls import url
from django.contrib import admin
from heroes import views
urlpatterns = [
url(r'^heroes/$', views.hero_list.as_view()),
url(r'^heroes/(?P<pk>[0-9]+)$', views.hero_detail.as_view()),
]
It’s now possible to set up the hero_list
and hero_detail
views in heroes/views.py
as:
from django.shortcuts import render
from rest_framework import mixins, generics
from .models import Hero
from .serializers import HeroSerializer
class hero_list(mixins.ListModelMixin,
mixins.CreateModelMixin,
generics.GenericAPIView):
queryset = Hero.objects.all()
serializer_class = HeroSerializer
def get(self, request, *args, **kwargs):
return self.list(request, *args, **kwargs)
def post(self, request, *args, **kwargs):
return self.create(request, *args, **kwargs)
class hero_detail(mixins.RetrieveModelMixin,
mixins.UpdateModelMixin,
mixins.DestroyModelMixin,
generics.GenericAPIView):
queryset = Hero.objects.all()
serializer_class = HeroSerializer
def get(self, request, *args, **kwargs):
return self.retrieve(request, *args, **kwargs)
def put(self, request, *args, **kwargs):
return self.update(request, *args, **kwargs)
def delete(self, request, *args, **kwargs):
return self.destroy(request, *args, **kwargs)
To check if it works, apply the db migrations:
python manage.py makemigrations
python manage.py migrate
To see the list of heroes, we can add one
python manage.py shell
from heroes.models import Hero
hero = Hero(name='Bug Blazer')
hero.save()
exit()
And then to see it
python manage.py runserver
and browse to http://127.0.0.1:8000/heroes/
Since the local development servers run on different ports, locally there will be CORS issues, therefore we need to setup a proxy. First of all, create a file angular_tour_of_heroes/frontend/proxy.conf.json
:
{
"/heroes": {
"target": "http://localhost:8000",
"secure": false
}
}
Then tell npm
to use it, changing the scripts.start
value in angular_tour_of_heroes/frontend/package.json
to
[...]
"start": "ng serve --proxy-config proxy.conf.json",
[...]
Remove all the references to the angular-in-memory-web-api
and to the in-memory-data
service that we used in the angular app from frontend/src/app/app.module.ts
, and remove the former with:
npm uninstall angular-in-memory-web-api --save
Make sure that heroesUrl
is properly set in the hero service, as in
private heroesUrl = '/heroes/';
Run the Django and Angular servers:
python manage.py runserver # from the root directory
npm start # from the frontend directory
To build the Angular app, we first modify frontend/package.json
to contain, in scripts.build
:
"build": "ng build --prod --base-href /static",
Then we run in the frontend
directory:
npm run build
We want to add the following to angular_tour_of_heroes/settings.py
to specify where to put the Angular frontend static files (in static/frontend
):
STATIC_ROOT = os.path.join(BASE_DIR, 'static')
STATICFILES_DIRS = [
('frontend', os.path.join(BASE_DIR, 'frontend/dist')),
]
Then python manage.py collectstatic
will collect all the Angular app static files and move them in static/frontend
, from where they can be easily served through a web server such as Apache or Nginx. For our purposes, however, it’s easier to add two more url patterns in urls.py
:
from django.conf.urls import url
from django.contrib import admin
from django.views.generic import RedirectView
from django.contrib.staticfiles.views import serve
from heroes import views
urlpatterns = [
url(r'^$', serve,kwargs={'path': 'frontend/index.html'}),
url(r'^(?!/?static/)(?!/?media/)(?P<path>.*\..*)$',
RedirectView.as_view(url='/static/frontend/%(path)s', permanent=False)),
url(r'^heroes/$', views.hero_list.as_view()),
url(r'^heroes/(?P<pk>[0-9]+)$', views.hero_detail.as_view()),
]
Now running python manage.py runserver
will be enough to view our app on http://localhost:8000/.