Showing posts with label Django. Show all posts

Development and Deployment of Cookiecutter-Django on Fedora

21:46:00

Last time we set up a Django Project using Cookiecutter, managed the application environment via Docker, and then deployed the app to Digital Ocean. In this tutorial, we’ll shift away from Docker and detail a development to deployment workflow of a Cookiecutter-Django Project on Fedora 23.
cookiecutter django fedora

Development

Install cookiecutter globally and then generate a bootstrapped Django project:
$ pip install cookiecutter==1.3.0
$ cookiecutter https://github.com/pydanny/cookiecutter-django.git
This command runs cookiecutter with the cookiecutter-django repo, allowing us to enter project-specific details. (We named the project and the repo django_cookiecutter_fedora.)
NOTE: Check out the Local Setup section from the previous post for more information on this command as well as the generated project structure.
Before we can start our Django Project, we are still left with few steps…

Database Setup

First, we need to set up Postgres since cookiecutter-django uses it as its default database (see django_cookiecutter_fedora/config/settings/common.py for more info). Follow the steps to set up a Postgres database server, from any good resource on the Internet or just scroll down to the Deployment Section for setting it up on Fedora.
NOTE: If you’re on a Mac, check out Postgres.app.
Once the Postgres server is running, create a new database from psql that shares the same name as your project name:
$ create database django_cookiecutter_fedora;
CREATE DATABASE
NOTE: There may be some variation in the above command for creating a database based upon your version of Postgres. You can check for the correct command in Postgres’ latest documentation found here.

Dependency Setup

Next, in order to get your Django Project in a state ready for development, navigate to the root directory, create/activate a virtual environment, and then install the dependencies:
$ cd django_cookiecutter_fedora
$ pyvenv-3.4 .env
$ source .env/bin/activate
$ ./install_python_dependencies.sh

Sanity Check

Apply the migrations and then run the local development server:
$ python manage.py makemigrations
$ python manage.py migrate
$ python manage.py runserver
Ensure all is well by navigating to http://localhost:8000/ in your browser to view the Project quick start page. Once done, kill the development server, initialize a new Git repo, commit, and PUSH to Github.

Deployment

With the Project setup and running locally, we can now move on to deployment where we will utilize the following tools:

Fedora 23 Setup

Set up a quick Digital Ocean droplet, making sure to use a Fedora 23 image. For help, follow this tutorial. Make sure you set up an SSH key for secure login.
Now let’s update our server. SSH into the server as root, and then fire the update process:
$ ssh root@SERVER_IP_ADDRESS
# dnf upgrade

Non-Root User

Next, let’s set up a non-root user so that applications are not run under administrative privileges, which makes the system more secure.
As a root user, follow these commands to set up a non-root user:
# adduser <name-of-user>
# passwd <name-of-user>
Changing password for user <name-of-user>.
New password:
Retype new password:
passwd: all authentication tokens updated successfully.
In the above snippet we created the new, non-root user user then specified a password for said user. We now need to add this user to the administrative group so that they can run commands that require admin privileges with sudo:
# usermod <name-of-user> -a -G wheel
Exit from the server and log back in again as the non-root user. Did you notice that the shell prompt changed from a # (pound-sign) to $ (dollar-sign)? This indicates that we are logged in as a non-root user.

Required Packages

While logged in as the non-root user, download and install the following packages:
Note: Below we are giving our non-root user root rights (recommended!). If by any chance you wish to not give the non-root user root rights explicitly, you will have to prepend sudo keyword with every command you execute in the terminal.
$ sudo su
# dnf install postgresql-server postgresql-contrib postgresql-devel python3-devel python-devel gcc nginx git

Postgres Setup

With the dependencies downloaded and installed, we just need to set up our Postgres server and create a database.
Initialize Postgres and then manually start the server:
# sudo postgresql-setup initdb
# sudo systemctl start postgresql
Then log in to the Postgres server by switching (su) to the postgres user:
# sudo su - postgres
# psql
postgres=#
Now create a Postgres user and database required for our project, making sure that the username matches the name of the non-root user:
postgres=# CREATE USER <user-name> WITH PASSWORD '<password-for-user>';
CREATE ROLE
postgres=# CREATE DATABASE django_cookiecutter_fedora;
CREATE DATABASE
postgres=# GRANT ALL ON DATABASE django_cookiecutter_fedora TO <user-name>;
GRANT
NOTE: If you need help, please follow the official guide for setting up Postgres on Fedora.
Exit psql and return to your non-root user’s shell session:
postgres=# \q
$ exit
When you exit from the postgres session, you will return back to your non-root user prompt. [username@django-cookiecutter-deploy ~]#.
Note: Did you notice the # sign at the prompt? This is now appearing because we gave our non-root user the root rights before starting off with setting up our server.
Configure Postgres so that it starts when the server boots/reboots:
# systemctl enable postgresql
# systemctl restart postgresql

Project Setup

Clone the project structure from your GitHub repo to the /opt directory:
# git clone <github-repo-url> /opt/<name of the repo>
NOTE: Want to use the repo associated with this tutorial? Simply run: sudo git clone https://github.com/realpython/django_cookiecutter_fedora /opt/django_cookiecutter_fedora

Dependency Setup

Next, in order to get your Django Project in a state ready for deployment, create and active a virtualenv inside your project’s root directory:
# cd /opt/django_cookiecutter_fedora/
# pip3 install virtualenv
# pyvenv-3.4 venv
Before activating the virtualenv, give the current, non-root user admin rights (if not given):
$ sudo su
# source venv/bin/activate
Unlike with the set up of the development environment from above, before installing the dependencies we need to install all of Pillow’s external libraries. Check out this resource for more info.
# dnf install libtiff-devel libjpeg-devel libzip-devel freetype-devel \
    lcms2-devel libwebp-devel tcl-devel tk-devel
Next, we need to install one more package in order to ensure that we do not get conflicts while installing dependencies in our virtualenv.
# dnf install redhat-rpm-config
Then run:
# ./install_python_dependencies.sh
Again, this will install all the base, local, and production requirements. This is just for a quick sanity check to ensure all is working. Since this is technically the production environment, we will change the environment shortly.
NOTE: Deactivate the virtualenv. Now if you issue an exit command – e.g., exit – the non-root user will no longer have root rights. Notice the change in prompt. That said, the user can still activate the virtualenv. Try it!

Sanity Check (take 2)

Apply all the migrations:
$ python manage.py makemigrations
$ python manage.py migrate
Now run the server:
$ python manage.py runserver 0.0.0.0:8000
To ensure things are working fine, just visit the server’s IP address in your browser – e.g., <ip-address or hostname>:8000.

Gunicorn Setup

Before setting up Gunicorn, we need to make some changes to the production settings, in the /config/settings/production.py module.
Take a look at the production settings here, which are the minimal settings needed for deploying our Django Project to a production server.
To update these, open the file in VI:
$ sudo vi config/settings/production.py
First, select all and delete:
:%d
And then copy the new settings and paste them into the now empty file by entering INSERT mode and then pasting. Make sure to update the ALLOWED_HOSTS variable as well (very, very important!) with your server’s IP address or hostname. Exit INSERT mode and then save and exit:
:wq
Once done, we need to add some environment variables to the .bashrc file, since the majority of the config settings come from environment variables within the production.py file.
Again, use VI to edit this file:
$ vi ~/.bashrc
Enter INSERT mode and add the following:
# Environment Variables
export DJANGO_SETTINGS_MODULE='config.settings.production'

export DJANGO_SECRET_KEY='CHANGEME!!!m_-0-_)3&-(+hn$!1i3$*$ujru4yw4@!u7048_(#1a*y_g2v3r'

export DATABASE_URL='postgres:///django_cookiecutter_fedora'
Two things to note:
  1. Notice how we updated the DJANGO_SETTINGS_MODULE variable to use the production settings.
  2. It’s a good practice to change your DJANGO_SECRET_KEY to a more complex string. Do this now if you want.
Again, exit INSERT mode, and then save and exit VI.
Now just reload the .bashrc file:
$ source ~/.bashrc
Ready to test?! Inside the root directory, with the virtualenv activated, execute the gunicorn server:
$ gunicorn --bind <ip-address or hostname>:8000 config.wsgi:application
This will make our web application, again, serve on <ip-address or hostname>:8000.
Keep in mind that as soon as we log out of our server this command will stop and hence we would no longer be able to serve our web app. So, we have to make our gunicorn server execute as a service so that it can be started, stopped, and monitored.

Nginx Config

Follow these steps for adding a configuration file for making our Django Project serve via Nginx:
$ cd /etc/nginx/conf.d
$ sudo vi django_cookiecutter_fedora.conf
Add the following, making sure to update the server, server_name, and location for project root:
upstream app_server {
    server 192.241.166.11:8000 fail_timeout=0;
}

server {
    listen 80;
    server_name 192.241.166.11;
    access_log /var/log/nginx/django_project-access.log;
    error_log /var/log/nginx/django_project-error.log info;

    keepalive_timeout 5;

    # path for staticfiles
    location /static {
            autoindex on;
            alias /opt/django_cookiecutter_fedora/staticfiles/;
    }

    location / {
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header Host $http_host;
        proxy_redirect off;

        if (!-f $request_filename) {
            proxy_pass http://app_server;
            break;
        }
    }
}
Save and exit VI, and then restart the Nginx server:
$ sudo systemctl restart nginx.service
That’s it!

Gunicorn Start Script

Now let’s create a Gunicorn start script which will run as an executable, making our bootstrapped Django web application run via the Gunicorn server, routed through Nginx.
Within the project root, run:
$ sudo mkdir deploy log
$ cd deploy
$ sudo vi gunicorn_start
The contents of the gunicorn_start script can be found here. It is divided into 3 significant parts, which are, for the most part, self-explanatory. For any questions, please comment below.
NOTE: Make sure the USER and GROUP variables match the same user and group for the non-root user.
Paste the contents into VI, and then save and exit.
Finally, let’s make it executable:
$ sudo chmod +x gunicorn_start
Start the server:
$ ./gunicorn_start
Once again visit the your server’s ip address in the browser and you will see your Django web app running!
Did you get a 502 Bad gateway error? Just follow these steps and it will probably be enough to make your application work…

Modifying SELinux Policy Rules

$ sudo dnf install policycoreutils-devel
$ sudo cat /var/log/audit/audit.log | grep nginx | grep denied | audit2allow -M mynginx
$ sudo semodule -i mynginx.pp
Once done, make sure to restart your server via Digital Ocean’s dashboard utility.

Systemd

To make our gunicorn_start script run as a system service so that, even if we are no longer logged into the server, it still serves our Django web application, we need to create a systemd service.
Just change the working directory to /etc/systemd/system, and then create a service file:
$ cd /etc/systemd/system
$ sudo vi django-bootstrap.service
Add the following:
#!/bin/sh

[Unit]
Description=Django Web App
After=network.target

[Service]
PIDFile=/var/run/cric.pid
ExecStart=/opt/django_cookiecutter_fedora/deploy/gunicorn_start
Restart=on-abort

[Install]
WantedBy=multi-user.target
Save and exit, and then start the service and enable it:
$ sudo systemctl start django-bootstrap.service
NOTE: If you encounter an error, run journalctl -xe to see more details.
Finally, enable the service so that it runs forever and restarts on arbitrary shut downs:
$ sudo systemctl enable django-bootstrap.service

Sanity Check (final!)

Check for the status of the service:
$ sudo systemctl status django-bootstrap.service
Now just visit your server’s IP address (or hostname) and you will see a Django error page. To fix this, run the following command in your project’s root directory (with your virtualenv activated):
$ python manage.py collectstatic
Now you are good to go, and you will see the Django web app running on the web browser with all the static files (HTML/CSS/JS) files working.

Read More

Deploying Django + Python 3 + PostgreSQL to AWS Elastic Beanstalk

21:44:00

The following is a soup to nuts walkthrough of how to set up and deploy a Django application, powered by Python 3, and PostgreSQL to Amazon Web Services (AWS) all while remaining sane.
Django Elastic Beanstalk

Tools/technologies used:
  1. Python v3.4.3
  2. Django v1.9
  3. Amazon Elastic Beanstalk, EC2, S3, and RDS
  4. EB CLI 3.x
  5. PostgreSQL
Check out the Python 2 version of this article here.

Elastic Beanstalk vs EC2

Elastic Beanstalk is a Platform As A Service (PaaS) that streamlines the setup, deployment, and maintenance of your app on Amazon AWS. It’s a managed service, coupling the server (EC2), database (RDS), and your static files (S3). You can quickly deploy and manage your application, which automatically scales as your site grows. Check out the official documentation for more information.
elastic beanstalk architecture

Getting Started

We’ll be using a simple “Image of the Day” app, which you can grab from this repository:
$ git clone https://github.com/realpython/image-of-the-day.git
$ cd image-of-the-day/
$ git checkout tags/start_here_py3
After you download the code, create a virtualenv and install the requirements via pip:
$ pip install -r requirements.txt
Next, with PostgreSQL running locally, set up a new database named iotd. Also, depending upon your local Postgres configuration, you may need to update the DATABASES configuration in settings.py. For example, I updated the config to:
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql_psycopg2',
        'NAME': 'iotd',
        'USER': '',
        'PASSWORD': '',
        'HOST': 'localhost',
        'PORT': '5432',
    }
}
Now you can set up the database schema, create a superuser, and run the app:
$ python manage.py migrate
$ python manage.py createsuperuser
$ python manage.py runserver
Navigate to the admin page in your browser at http://localhost:8000/admin and add a new image, which will then be displayed on the main page.
The application isn’t meant to be very exciting; we’re just using it for demo purposes. All it does is let you upload an image via the admin interface and display that image full screen on the main page. That said, although this is a relatively basic app, it will still allow us to explore a number of “gotchas” that exist when deploying to Amazon Beanstalk and RDS.
Now that we have the site up and running on our local machine, let’s start the Amazon deployment process.

CLI for AWS Elastic Beanstalk

To work with a Amazon Elastic Beanstalk, we can use a package called awsebcli. As of this writing the latest version of is 3.7.4 and the recommended way to install it is with pip:
$ pip install awsebcli
Now test the installation to make sure it’s working:
$ eb --version
This should give you a nice 3.x version number:
EB CLI 3.7.4 (Python 3.4.3)
To actually start using Elastic Beanstalk you will need an account with AWS (surprise!). Sign up (or log in).

login screen for aws

Configure EB – initialize your app

With the AWS Elastic Beanstalk CLI working, the first thing we want to do is create a Beanstalk environment to host the application on. Run this from the project directory (“image-of-the-day”):
$ eb init
This will prompt you with a number of questions to help you configure your environment.
Default region
Choosing the region closest to your end users will generally provide the best performance. Check out this map if you’re unsure which to choose.
Credentials
Next, it’s going to ask for your AWS credentials.
Here, you will most likely want to set up an IAM User. See this guide for how to set one up. If you do set up a new user you will need to ensure the user has the appropriate permissions. The simplest way to do this is to just add “Administrator Access” to the User. (This is probably not a great choice for security reasons, though.) For the specific policies/roles that a user needs in order to create/manage an Elastic Beanstalk application, see the link here.
Application name
This will default to the directory name. Just go with that.
Python version
Next, the CLI should automagically detect that you are using Python and just ask for confirmation. Say yes. Then you need to select a platform version. You have 2 different options here for Python 3:
  • Python 3.4
  • Python 3.4 (Preconfigured – Docker)
If you’re a hipster, choose the ‘Preconfigured – Docker’ choice, otherwise go with the normal ‘Python 3.4’. No, only teasing; the basic difference is this:

Python 3.4

This gives you an EC2 image running 64bit Amazon Linux with Python 3.4 pre-installed. The front end web server is apache, with mod_wsgi installed. This is the “standard” or “traditional” way that Beanstalk works. In other words, with this option Beanstalk will create EC2 images for you, and you can use the ebextension files we will talk about later to customize the EC2 image.

Python 3.4 (Preconfigured – Docker)

This gives you an EC2 image running Docker, with a Docker image already setup for you. The Docker image runs 64bit Debian Jessie with Python 3.4, nginx 1.8 and uWSGI 2.0.8. Because you’re basically interacting with the Docker image directly, if you choose this route you would use standard Docker configuration techniques (i.e., a ‘Dockerfile’), and then you don’t have to do much that is AWS Beanstalk specific, as Beanstalk knows how to manage the Docker image for you.
For this article we will focus on the “standard” or “traditional” way of using an EC2 image, so choose the ‘Python 3.4’ option and let’s move on.
SSH
Say yes to setting up SSH for your instances.
RSA Keypair
Next, you need to generate an RSA keypair, which will be added to your ~/.ssh folder. This keypair will also be uploaded to the EC2 public key for the region you specified in step one. This will allow you to SSH into your EC2 instance later in this tutorial.

What did we accomplish?

Once eb init is finished, you will see a new hidden folder called .elasticbeanstalk in your project directory:
├── .elasticbeanstalk
│   └── config.yml
├── .gitignore
├── README.md
├── iotd
│   ├── images
│   │   ├── __init__.py
│   │   ├── admin.py
│   │   ├── migrations
│   │   │   ├── 0001_initial.py
│   │   │   └── __init__.py
│   │   ├── models.py
│   │   ├── tests.py
│   │   └── views.py
│   ├── iotd
│   │   ├── __init__.py
│   │   ├── settings.py
│   │   ├── urls.py
│   │   └── wsgi.py
│   ├── manage.py
│   ├── static
│   │   ├── css
│   │   │   └── bootstrap.min.css
│   │   └── js
│   │       ├── bootstrap.min.js
│   │       └── jquery-1.11.0.min.js
│   └── templates
│       ├── base.html
│       └── images
│           └── home.html
├── requirements.txt
└── www
    └── media
        └── sitelogo.png
Inside that directory is a config.yml file, which is a configuration file that is used to define certain parameters for your newly minted Beanstalk application.
At this point, if you type eb console it will open up your default browser and navigate to the Elastic Beanstalk console. On the page, you should see one application (called image-of-the-day if you’re following along exactly), but no environments.

elastic beanstalk console
An application represents your code application and is what eb init created for us. With Elastic Beanstalk, an application can have multiple environments (i.e., development, testing, staging, production). It is completely up to you how you want to configure/manage these environments. For simple Django applications I like to have the development environment on my laptop, then create a test and a production environment on Beanstalk.
Let’s get a test environment set up…

Configure EB – create an environment

Coming back to the terminal, in your project directory type:
$ eb create
Just like eb init, this command will prompt you with a series of questions.
Environment Name
You should use a similar naming convention to what Amazon suggests – e.g., application_name-env_name – especially when/if you start hosting multiple applications with AWS. I used – iod-test.
DNS CNAME prefix
When you deploy an app to Elastic Beanstalk you will automatically get a domain name like xxx.elasticbeanstalk.com. DNS CNAME prefix is what you want to be used in place of xxx. The default probably won’t work if you’re following along because somebody else has already used it (the names are global to AWS), so pick something unique and keep on going.

What happens now?

At this point eb will actually create your environment for you. Be patient as this can take some time.
If you do get an error creating the environment, like – aws.auth.client.error.ARCInstanceIdentityProfileNotFoundException– check that the credentials you are using have appropriate permissions to create the Beanstalk environment, as discussed earlier in this post.
Also, it may prompt you with a message about Platform requires a service role. If it does, just say yes and let it create the role for you.
Immediately after the environment is created, eb will attempt to deploy your application, by copying all the code in your project directory to the new EC2 instance, running pip install -r requirements.txt in the process.
You should see a bunch of information about the environment being set up displayed to your screen, as well as information about eb trying to deploy. You will also see some errors. In particular you should see these lines buried somewhere in the output:
ERROR: Your requirements.txt is invalid. Snapshot your logs for details.
Don’t worry – It’s not really invalid. Check the logs for details:
$ eb logs
This will grab all the recent log files from the EC2 instance and output them to your terminal. It’s a lot of information, so you may want to redirect the output to a file (eb logs -z). Looking through the logs, you’ll see one log file named eb-activity.log:
Error: pg_config executable not found.
The problem is that we tried to install psycopy2 (the Postgres Python bindings), but we need the Postgres client drivers to be installed as well. Since they are not installed by default, we need to install them first. Let’s fix that…

Customizing the Deployment Process

eb will read custom .config files from a folder called “.ebextensions” at the root level of your project (“image-of-the-day” directory). These .config files allow you to install packages, run arbitrary commands and/or set environment variables. Files in the “.ebextensions” directory should conform to either JSON or YAML syntax and are executed in alphabetical order.

Installing packages

The first thing we need to do is install some packages so that our pip install command will complete successfully. To do this, let’s first create a file called .ebextensions/01_packages.config:
packages:
  yum:
    git: []
    postgresql93-devel: []
    libjpeg-turbo-devel: []
EC2 instances run Amazon Linux, which is a Redhat flavor, so we can use yum to install the packages that we need. For now, we are just going to install three packages – git, the Postgres client, and libjpeg for Pillow.
After creating that file to redeploy the application, we need to do the following:
$ git add .ebextensions/
$ git commit -m "added eb package configuration"
We have to commit the changes because the deployment command eb deploy works off the latest commit, and will thus only be aware of our file changes after we commit them to git. (Do note though that we don’t have to push; we are working from our local copy…)
As you probably guessed, the next command is:
$ eb deploy
You should now see only one error:
INFO: Environment update is starting.
INFO: Deploying new version to instance(s).
ERROR: Your WSGIPath refers to a file that does not exist.
INFO: New application version was deployed to running EC2 instances.
INFO: Environment update completed successfully.
Let’s find out what’s happening…

Configuring our Python environment

EC2 instances in Beanstalk run Apache, and Apache will find our Python app according to the WSGIPATH that we have set. By default eb assumes our wsgi file is called application.py. There are two ways of correcting this-
Option 1: Using environment-specific configuration settings
$ eb config
This command will open your default editor, editing a configuration file called .elasticbeanstalk/iod-test.env.yml. This file doesn’t actually exist locally; eb pulled it down from the AWS servers and presented it to you so that you can change settings in it. If you make any changes to this pseudo-file and then save and exit, eb will update the corresponding settings in your Beanstalk environment.
If you search for the terms ‘WSGI’ in the file, and you should find a configuration section that looks like this:
aws:elasticbeanstalk:container:python:
  NumProcesses: '1'
  NumThreads: '15'
  StaticFiles: /static/=static/
  WSGIPath: application.py
Update the WSGIPath:
 aws:elasticbeanstalk:container:python:
   NumProcesses: '1'
   NumThreads: '15'
   StaticFiles: /static/=static/
   WSGIPath: iotd/iotd/wsgi.py
And then you will have you WSGIPath set up correctly. If you then save the file and exit, eb will update the environment configuration automatically:
Printing Status:
INFO: Environment update is starting.
INFO: Updating environment iod-test's configuration settings.
INFO: Successfully deployed new configuration to environment.
INFO: Environment update completed successfully.
The advantage to using the eb config method to change settings is that you can specify different settings per environment. But you can also update settings using the same .config files that we were using before. This will use the same settings for each environment, as the .config files will be applied on deployment (after the settings from eb config have been applied).
Option 2: Using global configuration settings
To use the .config file option, let’s create a new file called /.ebextensions/02_python.config:
option_settings:
  "aws:elasticbeanstalk:application:environment":
    DJANGO_SETTINGS_MODULE: "iotd.settings"
    "PYTHONPATH": "/opt/python/current/app/iotd:$PYTHONPATH"
  "aws:elasticbeanstalk:container:python":
    WSGIPath: iotd/iotd/wsgi.py
    NumProcesses: 3
    NumThreads: 20
  "aws:elasticbeanstalk:container:python:staticfiles":
    "/static/": "www/static/"
What’s happening?
  • DJANGO_SETTINGS_MODULE: "iotd.settings" – adds the path to the settings module.
  • "PYTHONPATH": "/opt/python/current/app/iotd:$PYTHONPATH" – updates our PYTHONPATH so Python can find the modules in our application. (Note that the use of the full path is necessary.)
  • WSGIPath: iotd/iotd/wsgi.py sets our WSGI Path.
  • NumProcesses: 3 and NumThreads: 20 – updates the number of processes and threads used to run our WSGI application.
  • "/static/": "www/static/" sets our static files path.
Again, we can do a git commit then an eb deploy to update these settings.
Next let’s add a database.

Configuring a Database

Try to view the deployed website:
$ eb open
This command will show the deployed application in your default browser. You should see a connection refused error:
OperationalError at /
could not connect to server: Connection refused
    Is the server running on host "localhost" (127.0.0.1) and accepting
    TCP/IP connections on port 5432?
This is because we haven’t set up a database yet. At this point eb will set up your Beanstalk environment, but it doesn’t set up RDS (the database tier). We have to set that up manually.

Database setup

Again, use eb console to open up the Beanstalk configuration page.

elastic beanstalk config page
From there, do the following:
  1. Click the “Configuration” link.
  2. Scroll all the way to the bottom of the page, and then under the “Data Tier” section, click the link “create a new RDS database”.
  3. On the RDS setup page change the “DB Engine” to “postgres”.
  4. Add a “Master Username” and “Master Password”.
  5. Save the changes.

create new rds database
Beanstalk will create the RDS for you. Now we need to get our Django app to connect to the RDS. Beanstalk will help us out here by exposing a number of environment variables on the EC2 instances for us that detail how to connect to the Postgres server. So all we need to do is update our settings.py file to take advantage of those environment variables. Confirm that the DATABASES configuration parameter reflects the following in settings.py:
if 'RDS_DB_NAME' in os.environ:
    DATABASES = {
        'default': {
            'ENGINE': 'django.db.backends.postgresql_psycopg2',
            'NAME': os.environ['RDS_DB_NAME'],
            'USER': os.environ['RDS_USERNAME'],
            'PASSWORD': os.environ['RDS_PASSWORD'],
            'HOST': os.environ['RDS_HOSTNAME'],
            'PORT': os.environ['RDS_PORT'],
        }
    }
else:
    DATABASES = {
        'default': {
            'ENGINE': 'django.db.backends.postgresql_psycopg2',
            'NAME': 'iotd',
            'USER': 'iotd',
            'PASSWORD': 'iotd',
            'HOST': 'localhost',
            'PORT': '5432',
        }
    }
This simply says, “use the environment variable settings if present, otherwise use our default development settings.” Simple.

Handling database migrations

With our database setup, we still need to make sure that migrations run so that the database table structure is correct. We can do that by modifying .ebextensions/02_python.config and adding the following lines at the top of the file:
container_commands:
  01_migrate:
    command: "source /opt/python/run/venv/bin/activate && python iotd/manage.py migrate --noinput"
    leader_only: true
container_commands allow you to run arbitrary commands after the application has been deployed on the EC2 instance. Because the EC2 instance is set up using a virtual environment, we must first activate that virtual environment before running our migrate command. Also the leader_only: true setting means, “only run this command on the first instance when deploying to multiple instances.”
Don’t forget that our application makes use of Django’s admin, so we are going to need a superuser…

Create the Admin User

Unfortunately createsuperuser doesn’t allow you to specify a password when using the --noinput option, so we will have to write our own command. Fortunately, Django makes it very easy to create custom commands.
Create the file iotd/images/management/commands/createsu.py:
from django.core.management.base import BaseCommand
from django.contrib.auth.models import User


class Command(BaseCommand):

    def handle(self, *args, **options):
        if not User.objects.filter(username="admin").exists():
            User.objects.create_superuser("admin", "admin@admin.com", "admin")
Make sure you add the appropriate __init__.py files as well:
└─ management
    ├── __init__.py
    └── commands
        ├── __init__.py
        └── createsu.py
This file will allow you to run python manage.py createsu, and it will create a superuser without prompting for a password. Feel free to expand the command to use environment variables or another means to allow you to change the password.
Once you have the command created, we can just add another command to our container_commands section in .ebextensions/02_python.config:
02_createsu:
  command: "source /opt/python/run/venv/bin/activate && python iotd/manage.py createsu"
  leader_only: true
Before you test this out, let’s make sure our static files are all put in the correct place…

Static Files

Add one more command under container_commands:
03_collectstatic:
  command: "source /opt/python/run/venv/bin/activate && python iotd/manage.py collectstatic --noinput"
So the entire file looks like this:
container_commands:
  01_migrate:
    command: "source /opt/python/run/venv/bin/activate && python iotd/manage.py migrate --noinput"
    leader_only: true
  02_createsu:
    command: "source /opt/python/run/venv/bin/activate && python iotd/manage.py createsu"
    leader_only: true
  03_collectstatic:
    command: "source /opt/python/run/venv/bin/activate && python iotd/manage.py collectstatic --noinput"

option_settings:
  "aws:elasticbeanstalk:application:environment":
    DJANGO_SETTINGS_MODULE: "iotd.settings"
    "PYTHONPATH": "/opt/python/current/app/iotd:$PYTHONPATH"
    "ALLOWED_HOSTS": ".elasticbeanstalk.com"
  "aws:elasticbeanstalk:container:python":
    WSGIPath: iotd/iotd/wsgi.py
    NumProcesses: 3
    NumThreads: 20
  "aws:elasticbeanstalk:container:python:staticfiles":
    "/static/": "www/static/"
We now need to ensure that the STATIC_ROOT is set correctly in the settings.py file:
STATIC_ROOT = os.path.join(BASE_DIR, "..", "www", "static")
STATIC_URL = '/static/'
Make sure you commit the www directory to git so the static dir can be created. Then run eb deploy again, and you should now be in business:
INFO: Environment update is starting.
INFO: Deploying new version to instance(s).
INFO: New application version was deployed to running EC2 instances.
INFO: Environment update completed successfully.
At this point you should be able to go to http://your_app_url/admin, log in, add an image, and then see that image displayed on the main page of your application.
Success!

Using S3 for Media Storage

With this setup, each time we deploy again, we will lose all of our uploaded images. Why? Well, when you run eb deploy, a fresh instance is spun up for you. This is not what we want since we will then have entries in the database for the images, but no associated images. The solution is to store the media files in Amazon Simple Storage Service (Amazon S3) instead of on the EC2 instance itself.
You will need to:
  1. Create a bucket
  2. Grab your user’s ARN (Amazon Resource Name)
  3. Add bucket permissions
  4. Configure your Django app to use S3 to serve your static files
Since there are good write ups on this already, I’ll just point you to my favorite: Using Amazon S3 to store you Django Static and Media Files

Apache Config

Since we are using Apache with Beanstalk, we probably want to set up Apache to (among other things) enable gzip compression so files are downloaded faster by the clients. That can be done with container_commands. Create a new file .ebextensions/03_apache.config and add the following:
container_commands:
  01_setup_apache:
    command: "cp .ebextensions/enable_mod_deflate.conf /etc/httpd/conf.d/enable_mod_deflate.conf"
Then you need to create the file .ebextensions/enable_mod_deflate.conf:
# mod_deflate configuration
<IfModule mod_deflate.c>
  # Restrict compression to these MIME types
  AddOutputFilterByType DEFLATE text/plain
  AddOutputFilterByType DEFLATE text/html
  AddOutputFilterByType DEFLATE application/xhtml+xml
  AddOutputFilterByType DEFLATE text/xml
  AddOutputFilterByType DEFLATE application/xml
  AddOutputFilterByType DEFLATE application/xml+rss
  AddOutputFilterByType DEFLATE application/x-javascript
  AddOutputFilterByType DEFLATE text/javascript
  AddOutputFilterByType DEFLATE text/css
  # Level of compression (Highest 9 - Lowest 1)
  DeflateCompressionLevel 9
  # Netscape 4.x has some problems.
  BrowserMatch ^Mozilla/4 gzip-only-text/html
  # Netscape 4.06-4.08 have some more problems
  BrowserMatch ^Mozilla/4\.0[678] no-gzip
  # MSIE masquerades as Netscape, but it is fine
  BrowserMatch \bMSI[E] !no-gzip !gzip-only-text/html
<IfModule mod_headers.c>
  # Make sure proxies don't deliver the wrong content
  Header append Vary User-Agent env=!dont-vary
</IfModule>
</IfModule>
Doing this will enable gzip compression, which should help with the size of the files you’re downloading. You could also use the same strategy to automatically minify and combine your CSS/JS and do any other preprocessing you need to do.

Troubleshooting

Don’t forget the very helpful eb ssh command, which will get you into the EC2 instance so you can poke around and see what’s going on. When troubleshooting, there are a few directories you should be aware of:
  • /opt/python – Root of where you application will end up.
  • /opt/python/current/app – The current application that is hosted in the environment.
  • /opt/python/on-deck/app – The app is initially put in on-deck and then, after all the deployment is complete, it will be moved to current. If you are getting failures in your container_commands, check out out the on-deck folder and not the current folder.
  • /opt/python/current/env – All the env variables that eb will set up for you. If you are trying to reproduce an error, you may first need to source /opt/python/current/env to get things set up as they would be when eb deploy is running.
  • opt/python/run/venv – The virtual env used by your application; you will also need to run source /opt/python/run/venv/bin/activate if you are trying to reproduce an error.

Conclusion

Deploying to Elastic Beanstalk can be a bit daunting at first, but once you understand where all the parts are and how things work, it’s actually pretty easy and extremely flexible. It also gives you an environment that will scale automatically as your usage grows. Hopefully by now you have enough to be dangerous! Good luck on your next Beanstalk deployment.

Read More

Build Your First Python and Django Application

22:09:00

 

Python and Django can help make quick and functional applications.

You may have heard of Python before, especially if you have been coding for a while.
If not, Python is a high level, general purpose programming language. What this means is that you can use it to code up anything from a simple game to a website supporting millions of users per month.
In fact, several high profile sites with millions of visitors per month rely on Python for some of their services. Examples include YouTube and Dropbox



That being said, why should you use Python in the first place? Why not one of the many other popular languages out in the wild like Ruby or PHP? Well, with Python you get the following awesome benefits:
  1. Easily readable syntax.
  2. Awesome community around the language.
  3. Easy to learn.
  4. Python is useful for a myriad of tasks from basic shell sripting to advanced web development.

When Not to Use Python

While you can easily write a Desktop app with Python using tools like wxPython, you generally would do better to use the specialized tools offered by the platform you are targeting for example .NET on Windows.
You should also not use Python when your particular use case has very specialized requirements which are better met by other languages. An example is when you are building an embedded system, a domain in which languages like C, C++ and Java dominate.

Python 2 vs Python 3

Python 2.7.x and 3.x are both being used extensively in the wild. Python 3 introduced changes into the language which required applications written in Python 2 to be rewritten in order to work with the Python 3.x branch. However, most libraries you will require to use have now been ported to Python 3.
This tutorial will use Python 3 which is at version 3.5.1 at the time of writing. The principles remain the same though and only minor syntax modifications will be required to get the code running under Python 2.7.x.

Some Python Code Samples

Hello World

As I said before, one of Python's main benefits is its' very easily readable syntax. How easy? Check out Python's version of the ubiquitous Hello World.
# This line of code will print "Hello, World!" to your terminal
print("Hello, World!")
This code prints out Hello, World! to the console. You can easily try out this code by visiting this site, pasting the code samples in the editor on the right side of the page, and clicking the run button above the page to see the output.

Conditional Logic

Conditional logic is just as easy. Here is some code to check if a user's age is above 18, and if it is, to print Access allowed or Access not allowed otherwise.
# read in age
age = int(input("What's your age?"))

if age >= 18:
    print("Access allowed")
elif age < 18 and age > 0:
    print("Access not allowed")
else:
    print("Invalid age")
The input() function is used to read in keyboard input. You will therefore need to type something in the terminal prompt after running the script for rest of the script to execute. Note that the input() function is wrapped in the int() function.
This is because input() reads in values as strings and yet we need age to be an integer. We therefore have to cast the keyboard input into a string or else we will get an error for example when checking if the string is greater than 18.
Finally, note the else statement which executes for any other input which doesn't fit the criteria being checked for in the if statements.

Abstract Data Types

Python also has some excellent built in abstract data types for holding collections of items. An example is a list which can be used to hold variables of any type. The following code shows how to create a list and iterate through it to print each item to the terminal.
# create a list called my_list
my_list = [1, 2, 3, "python", 67,  [4, 5]]

# go through my_list and print every item
for item in my_list:
    print item
The code above creates a list with numbers, a string and a list (yes, lists can contain other lists!). To iterate through lists, a for-in loop comes in handy. Remember, lists are zero-indexed so we can also access list items using indexes. For example, to output the string python, you can write:
# create a list called my_list
my_list = [1, 2, 3, "python", 67,  [4, 5]]

print(my_list[3])

Dictionaries

Another excellent data type Python offers out of the box is dictionaries. Dictionaries store key-value pairs, kind of like JSON objects. Creating a dictionary is quite simple as well.
# create a dictionary
person = {
            "name": "Amos",
            "age": 23,
            "hobbies": ["Travelling", "Swimming", "Coding", "Music"]
        }

# iterate through the dict and print the keys
for key in person:
    print(key)

# iterate through the dict's keys and print their values
for key in person:
    print(person[key])
Now that you know a little bit of Python, let's talk about Django.

Django



Django is a Python web framework. It is free and open source and has been around since 2005. It is very mature and comes with excellent documentation and awesome features included by default. Some excellent tools it provides are:
  1. Excellent lightweight server for development and testing.
  2. Good templating language.
  3. Security features like CSRF included right out of the box.
There are a myriad of other useful things included in Django but you shall probably discover them as you go along. We are going to be using Django to build our website in this tutorial.

Setting Up

In this tutorial, I will show you how to get a Django website up and running. Before we get there though, first grab a copy of the latest Python from the Python website.

Note that if you are on OSX and you have Homebrew installed you can do
brew install python3
After that go straight to the Getting started with Django section
After installing the correct version for your OS, you will need to make sure it is set up correctly. Open a terminal and type:
python3
You should see something resembling the following:
Python 3.5.1 (v3.5.1:37a07cee5969, Dec  5 2015, 21:12:44) 
[GCC 4.2.1 (Apple Inc. build 5666) (dot 3)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> 

This is the interactive Python shell. Hit `CTRL + D` to exit it for now

Next, we need to install pip. Pip is a python package manager that will enable us to get libraries we require in our app easily such as Django. 

```bash
sudo easy_install pip

Setting Up the Environment

To avoid polluting our global scope with unecessary packages, we are going to use a virtual environment to store our packages. One excellent virtual environment manager available for free is virtualenv. You can get it by doing a pip install.
pip install virtualenv
Once that is done, create a folder called projects anywhere you like then cd into it.
mkdir projects
cd projects
Once inside the projects folder, create another folder called hello. This folder will hold our app.
mkdir hello
At this point, we need to create the environment to hold our requirements. We will do this inside the hello folder.
virtualenv -p /usr/local/bin/python3 env
The -p switch tells virtualenv the path to the python version you want to use. Feel free to switch out the path after it with your own Python installation path. The name env is the environment name. You can also change it to something else which fits the name of your project.
Once that is done, you should have a folder called env inside your hello folder. Your structure should now look something like this.
projects
├─hello
│   ├── env
You are now ready to activate the environment and start coding!
 source env/bin/activate
You will see a prompt with the environment name. That means the environment is active.
 (env)

Installing Django

This is a simple pip install. The latest Django version at the time of writing is Django 1.9.6
 pip install django

Creating an App

Now that Django is installed, we can use its start script to create a skeleton project. This is as simple as using its admin script in the following way.
django-admin startproject helloapp
Running this command creates a skeleton django app with the following structure:
helloapp
├─helloapp
│   ├── __init__.py
│   ├── settings.py
│   ├── urls.py
│   └── wsgi.py
└── manage.py
When you look into the helloapp folder that was created, you will find a file called manage.py and another folder called helloapp. This is your main project folder and contains the project's settings in a file called settings.py and the routes in your project in the file called urls.py. Feel free to open up the settings.py file to familiarize yourself with its contents.
Ready to move on? Excellent.

Changing App Settings

Let's change a few settings. Open up the settings.py file in your favorite editor. Find a section called Installed Apps which looks something like this.
# helloapp/settings.py
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
]
Django operates on the concept of apps. An app is a self contained unit of code which can be executed on its own. An app can do many things such as serve a webpage on the browser or handle user authentication or anything else you can think of. Django comes with some default apps preinstalled such as the authentication and session manager apps. Any apps we will create or third-party apps we will need will be added at the bottom of the Installed Apps list after the default apps installed.
Before we create a custom app, let's change the application timezone. Django uses the tz database timezones, a list of which can be found here.
The timezone setting looks like this.
# helloapp/settings.py
TIME_ZONE = 'UTC'
Change it to something resembling this as appropriate for your timezone.
# helloapp/settings.py
TIME_ZONE = 'America/Los_Angeles'

Creating your own app

 It is important to note that Django apps follow the Model, View, Template paradigm. In a nutshell, the app gets data from a model, the view does something to the data and then renders a template containing the processed information. As such, Django templates correspond to views in traditional MVC and Django views can be likened to the controllers found in traditional MVC.

That being said, let's create an app. cd into the first helloapp folder and type;
python manage.py startapp howdy
Running this command will create an app called howdy. Your file structure should now look something like this.
helloapp
├── helloapp
│        ├── __init__.py
│        ├── settings.py
│        ├── urls.py
│        └── wsgi.py
├── howdy
│        ├── __init__.py
│        ├── admin.py
│        ├── apps.py
│        ├── migrations
│        ├── models.py
│        ├── tests.py
│        └── views.py
└── manage.py
To get Django to recognize our brand new app, we need to add the app name to the Installed Apps list in our settings.py file.
# helloapp/settings.py
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'howdy'
]
Once that is done, let's run our server and see what will be output. We mentioned that Django comes with a built in lightweight web server which, while useful during development, should never be used in production. Run the server as follows:
python manage.py runserver
Your output should resemble the following:
Performing system checks...

System check identified no issues (0 silenced).

You have unapplied migrations; your app may not work properly until they are applied.
Run 'python manage.py migrate' to apply them.

June 04, 2016 - 07:42:08
Django version 1.9.6, using settings 'helloapp.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.
If you look carefully, you will see a warning that you have unapplied migrations. Ignore that for now. Go to your browser and access http://127.0.0.1:8000/. If all is running smoothly, you should see the Django welcome page.



We are going to replace this page with our own template. But first, let's talk migrations.

Migrations

Migrations make it easy for you to change your database schema (model) without having to lose any data. Any time you create a new database model, running migrations will update your database tables to use the new schema without you having to lose any data or go through the tedious process of dropping and recreating the database yourself.
Django comes with some migrations already created for its default apps. If your server is still running, stop it by hitting CTRL + C. Apply the migrations by typing:
python manage.py migrate
If successful, you will see an output resembling this one.
Operations to perform:
  Apply all migrations: sessions, auth, contenttypes, admin
Running migrations:
  Rendering model states... DONE
  Applying contenttypes.0001_initial... OK
  Applying auth.0001_initial... OK
  Applying admin.0001_initial... OK
  Applying admin.0002_logentry_remove_auto_add... OK
  Applying contenttypes.0002_remove_content_type_name... OK
  Applying auth.0002_alter_permission_name_max_length... OK
  Applying auth.0003_alter_user_email_max_length... OK
  Applying auth.0004_alter_user_username_opts... OK
  Applying auth.0005_alter_user_last_login_null... OK
  Applying auth.0006_require_contenttypes_0002... OK
  Applying auth.0007_alter_validators_add_error_messages... OK
  Applying sessions.0001_initial... OK
Running the server now will not show any warnings.

Urls & Templates

 When we ran the server, the default Django page was shown. We need Django to access our howdy app when someone goes to the home page URL which is /. For that, we need to define a URL which will tell Django where to look for the homepage template.

Open up the urls.py file inside the inner helloapp folder. It should look like this.
 # helloapp/urls.py
 """helloapp URL Configuration

The `urlpatterns` list routes URLs to views. For more information please see:

https://docs.djangoproject.com/en/1.9/topics/http/urls/

Examples:
Function views
    1. Add an import:  from my_app import views
    2. Add a URL to urlpatterns:  url(r'^$', views.home, name='home')
Class-based views
    1. Add an import:  from other_app.views import Home
    2. Add a URL to urlpatterns:  url(r'^$', Home.as_view(), name='home')
Including another URLconf
    1. Import the include() function: from django.conf.urls import url, include
    2. Add a URL to urlpatterns:  url(r'^blog/', include('blog.urls'))
"""
from django.conf.urls import url
from django.contrib import admin

urlpatterns = [
    url(r'^admin/', admin.site.urls),
]
As you can see, there is an existing URL pattern for the Django admin site which comes by default with Django. Let's add our own url to point to our howdy app. Edit the file to look like this.
# helloapp/urls.py
from django.conf.urls import url, include
from django.contrib import admin

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^', include('howdy.urls')),
]
Note that we have added an import for include from django.conf.urls and added a url pattern for an empty route. When someone accesses the homepage, (in our case http://localhost:8000), Django will look for more url definitions in the howdy app. Since there are none, running the app will produce a huge stack trace due to an ImportError.
.
.
ImportError: No module named 'howdy.urls'
Let's fix that. Go to the howdy app folder and create a file called urls.py. The howdy app folder should now look like this.
├── howdy
│   ├── __init__.py
│   ├── admin.py
│   ├── apps.py
│   ├── migrations
│   │   ├── __init__.py
│   ├── models.py
│   ├── tests.py
│   ├── urls.py
│   └── views.py
Inside the new urls.py file, write this.
# howdy/urls.py
from django.conf.urls import url
from howdy import views

urlpatterns = [
    url(r'^$', views.HomePageView.as_view()),
]
This code imports the views from our howdy app and expects a view called HomePageView to be defined. Since we don't have one, open the views.py file in the howdy app and write this code.
 # howdy/views.py
from django.shortcuts import render
from django.views.generic import TemplateView

# Create your views here.
class HomePageView(TemplateView):
    def get(self, request, **kwargs):
        return render(request, 'index.html', context=None)
This file defines a view called HomePageView. Django views take in a request and return a response. In our case, the method get expects a HTTP GET request to the url defined in our urls.py file. On a side note, we could rename our method to post to handle HTTP POST requests.
Once a HTTP GET request has been received, the method renders a template called index.html which is just a normal HTML file which could have special Django template tags written alongside normal HTML tags. If you run the server now, you will see the following error page:




This is because we do not have any templates at all! Django looks for templates in a templates folder inside your app so go ahead and create one in your howdy app folder.
mkdir templates
Go into the templates folder you just created and create a file called index.html
(env) hello/helloapp/howdy/templates
 > touch index.html
Inside the index.html file, paste this code.
<!-- howdy/templates/index.html -->
<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title>Howdy!</title>
    </head>
    <body>
        <h1>Howdy! I am Learning Django!</h1>
    </body>
</html>
Now run your server.
python manage.py runserver
You should see your template rendered.

Linking pages

Let's add another page. In your howdy/templates folder, add a file called about.html. Inside it, write this HTML code:
<!-- howdy/templates/about.html -->
<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title>Howdy!</title>
    </head>
    <body>
        <h1>Welcome to the about page</h1>
    <p>
    Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc quis neque ex. Donec feugiat egestas dictum. In eget erat sit amet elit pellentesque convallis nec vitae turpis. Vivamus et molestie nisl. Aenean non suscipit velit. Nunc eleifend convallis consectetur. Phasellus ornare dolor eu mi vestibulum, ornare tempus lacus imperdiet. Interdum et malesuada fames ac ante ipsum primis in faucibus. Pellentesque ut sem ligula. Mauris volutpat vestibulum dui in cursus. Donec aliquam orci pellentesque, interdum neque sit amet, vulputate purus. Quisque volutpat cursus nisl, in volutpat velit lacinia id. Maecenas id felis diam. 
    </p>
    <a href="/">Go back home</a>
    </body>
</html>
Once done, edit the original index.html page to look like this.
<!-- howdy/templates/index.html -->
<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title>Howdy!</title>
    </head>
    <body>
        <h1>Howdy! I am Learning Django!</h1>
     <a href="/about/">About Me</a>
    </body>
</html>
Clicking on the About me link won't work quite yet because our app doesn't have a /about/ url defined. Let's edit the urls.py file in our howdy app to add it.

# howdy/urls.py
from django.conf.urls import url
from howdy import views

urlpatterns = [
    url(r'^$', views.HomePageView.as_view()),
    url(r'^about/$', views.AboutPageView.as_view()), # Add this /about/ route
]
 
Once we have added the route, we need to add a view to render the about.html template when we access the /about/ url. Let's edit the views.py file in the howdy app.

# howdy/views.py
from django.shortcuts import render
from django.views.generic import TemplateView

class HomePageView(TemplateView):
    def get(self, request, **kwargs):
        return render(request, 'index.html', context=None)

# Add this view
class AboutPageView(TemplateView):
    template_name = "about.html"
 
Notice that in the second view, I did not define a get method. This is just another way of using the TemplateView class. If you set the template_name attribute, a get request to that view will automatically use the defined template. Try changing the HomePageView to use the format used in AboutPageView.
Running the server now and accessing the home page should display our original template with the newly added link to the about page.
Clicking on the About me link should direct you to the About page.
On the About me page, clicking on the Go back home link should redirect you back to our original index page. Try editing both these templates to add more information about you.

Conclusion

That was just a brief look at how to get a Django website up and running. You can read more about Django on the official Django docs. The full code for this tutorial can also be found on Github.

Read More