Batteries Not Included
From Hello World to Production
Ready Python Web Apps
Who Am I?
● Tyrel Denison
● Senior Software Engineer @ Lofty Labs (hirelofty.com)
● mic talker @ Friday Afternoon Deploy (friday.hirelofty.com)
● tyrel@hirelofty.com
● @tyreldenison
The Times They Are a Changin’
The Times They Are a Changin’
The Times They Are a Changin’
The Times They Are a Changin’
The Times They Are a Changin’
The Times They Are a Changin’
The Times They Are a Changin’
The Times They Are a Changin’
The Times They Are a Changin’
Why Python
● Approachable
● Portable
● Powerful
● Thriving Community
Why Django
● ”The web framework for perfectionists with deadlines”
● Approachable
● Powerful
● Thriving Community
● Battle-tested
● Feature-rich
● Transparent
One Weird Thing
What’s in the Box
● Admin
● ORM
● Database state management via migrations
● Built-in security
● Authentication
● Site maps
● RSS
● Templates
What’s in the Box
Where do I get what I need?
We need a real DB
Postgres
We need a real DB
Postgres
CREATE DATABASE myproject;
CREATE USER user WITH PASSWORD 'password';
GRANT ALL PRIVILEGES ON DATABASE user TO
myprojectuser;
pip install psycopg2
DATABASES = {
'default': {
'ENGINE':
'django.db.backends.postgresql_psycopg2',
'NAME': 'myproject',
'USER': user,
'PASSWORD': 'password',
'HOST': 'localhost',
'PORT': '',
}
}
We need a real DB
We need a real server
uWSGI + Nginx + Supervisor
We need a real server
[uwsgi]
chdir=/path/to/your/project
module=mysite.wsgi:application
master=True
pidfile=/tmp/project-master.pid
vacuum=True
max-requests=5000
daemonize=/var/log/uwsgi/yourproject.log
We need a real server
# mysite_nginx.conf
upstream django {
server 127.0.0.1:8001;
}
server {
listen 8000;
server_name .example.com;
client_max_body_size 75M;
location /media {
alias /path/to/your/mysite/media;
}
location /static {
alias /path/to/your/mysite/static;
}
location / {
uwsgi_pass django;
include /path/to/your/mysite/uwsgi_params;
}
}
We need a real server
Supervisor
supervisorctl start myproject
supervisorctl stop myproject
supervisorctl restart myproject
[program:nginx]
command=/usr/sbin/nginx -g "daemon off;"
autostart=true
autorestart=true
startretries=5
numprocs=1
startsecs=0
process_name=%(program_name)s_%(process_num)02d
stderr_logfile=/var/log/supervisor/%(program_name)s_stderr.log
stderr_logfile_maxbytes=10MB
stdout_logfile=/var/log/supervisor/%(program_name)s_stdout.log
stdout_logfile_maxbytes=10MB
We need a real server
What about when I want a front end?
What about when I want a front end?
pip install djangorestframework
REST_FRAMEWORK = {
# Use Django's standard
`django.contrib.auth` permissions,
# or allow read-only access for
unauthenticated users.
'DEFAULT_PERMISSION_CLASSES': [
'rest_framework.permissions.DjangoModelPermissio
nsOrAnonReadOnly'
]
}
INSTALLED_APPS = (
‘corsheaders’,
‘rest_framework’,
...
)
What about when I want a front end?
from django.conf.urls import url, include
from django.contrib.auth.models import User
from rest_framework import routers, serializers,
viewsets
# Serializers define the API representation.
class
UserSerializer(serializers.HyperlinkedModelSeria
lizer):
class Meta:
model = User
fields = ('url', 'username', 'email',
'is_staff')
# ViewSets define the view behavior.
class UserViewSet(viewsets.ModelViewSet):
queryset = User.objects.all()
serializer_class = UserSerializer
What about async?
Celery and Rabbit
What about async?
celery.py
import os
from celery import Celery
os.environ.setdefault('DJANGO_SETTINGS_MODULE',
'mysite.settings')
app = Celery('mysite')
app.config_from_object('django.conf:settings',
namespace='CELERY')
app.autodiscover_tasks()
Celery and Rabbit
settings.py
CELERY_BROKER_URL = 'amqp://localhost'
init.py
from .celery import app as celery_app
__all__ = ['celery_app']
What about async?
# Create your tasks here
from celery import app
@app.task()
def add(x, y):
return x + y
@app.task()
def mul(x, y):
return x * y
sum = add.apply_async(args=[1,2])
[program:mysite-celery]
command=/home/mysite/bin/celery worker -A mysite
--loglevel=INFO
directory=/home/mysite/mysite
user=nobody
numprocs=1
stdout_logfile=/home/mysite/logs/celery.log
stderr_logfile=/home/mysite/logs/celery.log
autostart=true
autorestart=true
startsecs=10
What about async?
Do you even test?
● UnitTest
● Coverage
● Django-Nose
Do you even test?
● Elastic APM
● Sentry
● Pingdom (Not Open Source)
Honorable Mentions
Elastic Search and Django Channels and CI Oh My
Further Reading
https://www.djangoproject.com/
https://uwsgi.readthedocs.io
https://www.django-rest-framework.org/
http://docs.celeryproject.org/en/latest/django/
https://www.elastic.co/solutions/apm
http://sentry.io
https://www.elastic.co/products/elasticsearch
https://channels.readthedocs.io
Careers at Lofty Labs

Batteries not included

  • 1.
    Batteries Not Included FromHello World to Production Ready Python Web Apps
  • 2.
    Who Am I? ●Tyrel Denison ● Senior Software Engineer @ Lofty Labs (hirelofty.com) ● mic talker @ Friday Afternoon Deploy (friday.hirelofty.com) ● tyrel@hirelofty.com ● @tyreldenison
  • 3.
    The Times TheyAre a Changin’
  • 4.
    The Times TheyAre a Changin’
  • 5.
    The Times TheyAre a Changin’
  • 6.
    The Times TheyAre a Changin’
  • 7.
    The Times TheyAre a Changin’
  • 8.
    The Times TheyAre a Changin’
  • 9.
    The Times TheyAre a Changin’
  • 10.
    The Times TheyAre a Changin’
  • 11.
    The Times TheyAre a Changin’
  • 12.
    Why Python ● Approachable ●Portable ● Powerful ● Thriving Community
  • 13.
    Why Django ● ”Theweb framework for perfectionists with deadlines” ● Approachable ● Powerful ● Thriving Community ● Battle-tested ● Feature-rich ● Transparent
  • 14.
  • 15.
    What’s in theBox ● Admin ● ORM ● Database state management via migrations ● Built-in security ● Authentication ● Site maps ● RSS ● Templates
  • 16.
  • 17.
    Where do Iget what I need?
  • 18.
    We need areal DB Postgres
  • 19.
    We need areal DB Postgres CREATE DATABASE myproject; CREATE USER user WITH PASSWORD 'password'; GRANT ALL PRIVILEGES ON DATABASE user TO myprojectuser; pip install psycopg2 DATABASES = { 'default': { 'ENGINE': 'django.db.backends.postgresql_psycopg2', 'NAME': 'myproject', 'USER': user, 'PASSWORD': 'password', 'HOST': 'localhost', 'PORT': '', } }
  • 20.
    We need areal DB
  • 21.
    We need areal server uWSGI + Nginx + Supervisor
  • 22.
    We need areal server [uwsgi] chdir=/path/to/your/project module=mysite.wsgi:application master=True pidfile=/tmp/project-master.pid vacuum=True max-requests=5000 daemonize=/var/log/uwsgi/yourproject.log
  • 23.
    We need areal server # mysite_nginx.conf upstream django { server 127.0.0.1:8001; } server { listen 8000; server_name .example.com; client_max_body_size 75M; location /media { alias /path/to/your/mysite/media; } location /static { alias /path/to/your/mysite/static; } location / { uwsgi_pass django; include /path/to/your/mysite/uwsgi_params; } }
  • 24.
    We need areal server Supervisor supervisorctl start myproject supervisorctl stop myproject supervisorctl restart myproject [program:nginx] command=/usr/sbin/nginx -g "daemon off;" autostart=true autorestart=true startretries=5 numprocs=1 startsecs=0 process_name=%(program_name)s_%(process_num)02d stderr_logfile=/var/log/supervisor/%(program_name)s_stderr.log stderr_logfile_maxbytes=10MB stdout_logfile=/var/log/supervisor/%(program_name)s_stdout.log stdout_logfile_maxbytes=10MB
  • 25.
    We need areal server
  • 26.
    What about whenI want a front end?
  • 27.
    What about whenI want a front end? pip install djangorestframework REST_FRAMEWORK = { # Use Django's standard `django.contrib.auth` permissions, # or allow read-only access for unauthenticated users. 'DEFAULT_PERMISSION_CLASSES': [ 'rest_framework.permissions.DjangoModelPermissio nsOrAnonReadOnly' ] } INSTALLED_APPS = ( ‘corsheaders’, ‘rest_framework’, ... )
  • 28.
    What about whenI want a front end? from django.conf.urls import url, include from django.contrib.auth.models import User from rest_framework import routers, serializers, viewsets # Serializers define the API representation. class UserSerializer(serializers.HyperlinkedModelSeria lizer): class Meta: model = User fields = ('url', 'username', 'email', 'is_staff') # ViewSets define the view behavior. class UserViewSet(viewsets.ModelViewSet): queryset = User.objects.all() serializer_class = UserSerializer
  • 29.
  • 30.
    What about async? celery.py importos from celery import Celery os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'mysite.settings') app = Celery('mysite') app.config_from_object('django.conf:settings', namespace='CELERY') app.autodiscover_tasks() Celery and Rabbit settings.py CELERY_BROKER_URL = 'amqp://localhost' init.py from .celery import app as celery_app __all__ = ['celery_app']
  • 31.
    What about async? #Create your tasks here from celery import app @app.task() def add(x, y): return x + y @app.task() def mul(x, y): return x * y sum = add.apply_async(args=[1,2]) [program:mysite-celery] command=/home/mysite/bin/celery worker -A mysite --loglevel=INFO directory=/home/mysite/mysite user=nobody numprocs=1 stdout_logfile=/home/mysite/logs/celery.log stderr_logfile=/home/mysite/logs/celery.log autostart=true autorestart=true startsecs=10
  • 32.
  • 33.
    Do you eventest? ● UnitTest ● Coverage ● Django-Nose
  • 34.
    Do you eventest? ● Elastic APM ● Sentry ● Pingdom (Not Open Source)
  • 35.
    Honorable Mentions Elastic Searchand Django Channels and CI Oh My
  • 36.