Django for Beginners

From Hidden Wiki
Jump to navigation Jump to search
Unix Assembly language Mathematics Web development I2P
GhostBSD Assembly Programming Tutorial Statistics Django for Beginners MuWire
GUI Artificial intelligence Artificial neural network Machine learning Messenger
Tkinter Artificial intelligence Artificial neural network Machine Learning Mastery with Python Session

Django is a full-stack web framework for Python.


1 Initial Set Up

2 Hello World app

3 Pages app

4 Message Board app

5 Blog app

6 Forms

7 User Accounts

8 Custom User Model

9 User Authentication

10 Bootstrap

11 Password Change and Reset

12 Email

13 Newspaper app

14 Permissions and Authorization

15 Comments


We start by properly covering how to configure a local development environment in Chapter 1.


In Chapter 2 we build our first project, a minimal Hello, World application that demonstrates how to set up new Django projects.


In Chapter 3 we make, test, and deploy a Pages app that introduces templates and class-based views.


In Chapter 4 we build our first database-backed project, a Message Board app.


Finally in Chapters 5-7 we’re ready for our final project: a robust blog application that demonstrates how to perform CRUD (Create-Read-Update-Delete) functionality in Django. We’ll find that Django’s generic class-based views mean we have to write only a small amount of actual code for this! Then we’ll add forms and integrate Django’s built-in user authentication system (login, logout, signup).


In Chapter 8 we start a Newspaper site and introduce the concept of custom user models, a Django best practice that is rarely covered in tutorials. Simply put all new projects should use a custom user model and in this chapter you’ll learn how.


Chapter 9 covers user authentication, Chapter 10 adds Bootstrap for styling, and Chapters 11-12 implement password reset and change via email.


In Chapters 13-15 we add articles and comments to our project, along with proper permissions and authorizations. We even learn some tricks for customizing the admin to display our growing data.


See TinyIB, phpBB, MediaWiki articles.


See Django for Beginners 2, Django for Beginners 3.

Initial Set Up

Most source codes were tested with Python 3.8.2 on Ubuntu Linux 20.04.


The Command Line

$ cd 

change down a directory


$ cd ..

change up a directory


$ ls 

list files in your current directory


$ pwd 

print working directory


$ mkdir 

make directory


$ touch 

create a new file


You can reload your prior command by using arrow keys.

Install Python 3 on Linux

Firstly, check your Python version.

$ python --version
$ python3 --version


Python 3.8.2 is already installed and Python 2 is not installed on Ubuntu Linux 20.04. You don't have to install Python 2.


If it is not installed on your computer, you can install it by typing the below command on terminal.

$ sudo apt install python3

Virtual Environments

First, check your pip version.

$ pip --version
$ pip3 --version


Ubuntu 19.04 doesn't have pip and pip3.


Open a terminal of Linux.


Input the below command to install python3-pip.

$ sudo apt install python3-pip


Enter the below command to install pipenv.

$ pip3 install --user pipenv

Install Django

$ cd ~
$ mkdir test
$ cd test/

Make the "test" directory. And you don't have to type the whole command such as "cd test". Just type "cd t" or "cd te" and press Tab key then the command will be completed automatically.


$ pipenv install django==3.0.4

To install Django 3.0.4, you should use the above command, but it doesn't work on Ubuntu 19.04. If you use Ubuntu, before doing it, you should edit your "~/.profile" file and "~/.bashrc" file. At first, "~" means your home directory. And "." means hidden files. You can see hidden files and folders if you press "Ctrl + H" keys. You can edit them with gedit or any text editors.


Add below lines into at the bottom of the ".profile" file.

# pipenv
if [ -d "$HOME/.local/bin" ] ; then
    PATH="$PATH:$HOME/.local/bin"
fi

# means comments, so "# pipenv" doesn't have any effect. You can remove it.


Add the below lines to at the bottom of ".bashrc" file.

# pipenv
export PATH="$HOME/.local/bin:$PATH"

You can delete the comment, "# pipenv".


Type the below command on your terminal.

$  source ~/.bashrc

Before doing it, the changes on ".bashrc" file doesn't take effect.


After doing "$ pipenv install django==3.0.4", you can see two files, "Pipfile" and "Pipfile.lock", in the "test" folder.


To activate this project's virtualenv, run "pipenv shell". Alternatively, run a command inside the virtualenv with "pipenv run".


Type the below command on your terminal to launch subshell in virtual environment.

$ pipenv shell


Then you can see the below line.

(test) username@computername:~/test$ 

If you see (username) instead of (test), you should uninstall "pipenv" and reinstall it. First, remove "Pipfile" and "Pipfile.lock" files. Second, uninstall and reinstall "pipenv". Then, it will work properly.


Type the below command.

(test) $ django-admin startproject test_project

It will make a new project, test_project.


(test) $ cd test_project/
(test) $ ls

You can see manage.py file and test_project directory within test_project directory. Actually, there are two "test_project" folders.


(test) $ python3 manage.py runserver

After executing manage.py file, you'll be able to see the test_project web page when you visit http://127.0.0.1:8000/ using your web browser such as Google Chrome. http://localhost:8000/ is also same. 127.0.0.1 is an IP address and 8000 is a port number.

Actually, you don't have to type "python3" in a virtual environment. Just "python" is enough because "python" also executes "Python 3". But outside of a virtual environment, you should distinguish "python" and "python3" because "python" executes "Python 2" on Ubuntu 19.04.


To stop our local server press "Control + c".


(test) $ exit

Then exit our virtual environment using the command "exit".


We can always reactivate the virtual environment again using "pipenv shell" at any time.


We’ll get lots of practice with virtual environments in this book so don’t worry if it’s a little confusing right now. The basic pattern is to install new packages with "pipenv", activate them with "pipenv shell", and then exit when done with "exit". Or just press "Control + d" on Ubuntu Linux when you want to quit a virtual environment.


Reduce redundancy

Make a new folder "test2" and start a virtual environment.

$ cd ~
$ mkdir test2
$ cd test2
$ pipenv install django==3.0.4
$ pipenv shell

Then you can see the below line.

(test2) username@computername:~/test2$ 


If your home directory has "Pipfile" and "Pipfile.lock" files, it will show you the below line.

(username) username@computername:~$ 

Delete "Pipfile" and "Pipfile.lock" files in your home folder. Then it will work properly.


You can start a new project in a diffetent way. It will reduce the redundancy.


Create a new Django project called "test_project_2" with the following command. Don’t forget that period . at the end.

(test2) $ django-admin startproject test_project_2 .

It’s worth pausing here to explain why you should add a period . to the command. If you just run "django-admin startproject test_project_2" then by default Django will create this directory structure:

└── test_project_2
    ├── manage.py
    └── test_project_2
        ├── asgi.py
        ├── __init__.py
        ├── settings.py
        ├── urls.py
        └── wsgi.py


See how it creates a new directory "test_project_2" and then within it a "manage.py" file and a "test_project_2" directory? That feels redundant to me since we already created and navigated into a "test2" folder on our home directory. By running "django-admin startproject test_project_2 ." with the period at the end–which says, install in the current directory–the result is instead this:

├── manage.py
└── test_project_2
    ├── asgi.py
    ├── __init__.py
    ├── settings.py
    ├── urls.py
    └── wsgi.py


The takeaway is that it doesn’t really matter if you include the period or not at the end of the command, but I prefer to include the period and so that’s how we’ll do it in this book.

Install Git

Git is an indispensable part of modern software development. It is a version control system which can be thought of as an extremely powerful version of track changes in Microsoft Word or Google Docs. With git, you can collaborate with other developers, track all your work via commits, and revert to any previous version of your code even if you accidentally delete something important!


Type the below command on the terminal.

$ sudo apt install git


Once installed, we need to do a one-time system setup to configure it by declaring the name and email address you want associated with all your Git commits (more on this shortly).

Within the command line console type the following two lines. Make sure to update them your name and email address.

$ git config --global user.name "Your Name"
$ git config --global user.email "yourname@email.com"


You can always change these configs later if you desire by retyping the same commands with a new name or new email address.

Text Editors

You can use just gedit as a text editor, but you'd better install more powerful text editors such as Visual Studio Code.

https://code.visualstudio.com/

Download code_1.28.2-1539735992_amd64.deb file and just double clicked it to install it.

You can search this app on your Ubuntu after installation. Click "Show Applications" and type "code" in "Type to search...".

After running this app, you can add it to favorite. Click the right button of your mouse on the app's icon and press "Add to Favorites".

Visual Studio Code is developed by Microsoft but it's open-source. However, it has its limitations.

https://github.com/microsoft/vscode

Visual Studio Code collects usage data and sends it to Microsoft, although this telemetry reporting can be disabled.[1] The data is shared among Microsoft-controlled affiliates and subsidiaries and with law enforcement, per the privacy statement.[2] Because of the open-source nature of the app, it is known exactly what is collected. Upstream's binary is shipped under a proprietary licence.[3]


Sublime Text is also a good free source-code editor for code and markup but it's a proprietary software or closed-source software.

https://www.sublimetext.com/

VSCodium

VSCodium is 100% open source version of Microsoft Visual Studio Code.

https://github.com/VSCodium/vscodium

https://vscodium.com/


VSCodium is an alternative binary distribution of the software which uses only the open-source parts and omits Microsoft’s trademarks and the telemetry component, while remaining fully functional and compatible in all other regards.[4]


  • VSCodium: 100% Open Source Version of Microsoft VS Code

April 10, 2019

https://itsfoss.com/vscodium/


  • Installation

Debian / Ubuntu (deb package):


Add the GPG key of the repository:

wget -qO - https://gitlab.com/paulcarroty/vscodium-deb-rpm-repo/raw/master/pub.gpg | sudo apt-key add -


Add the repository:

echo 'deb https://gitlab.com/paulcarroty/vscodium-deb-rpm-repo/raw/repos/debs/ vscodium main' | sudo tee --append /etc/apt/sources.list.d/vscodium.list


Update then install vscodium:

sudo apt update && sudo apt install codium 


Launch VSCodium:

codium


You can save your edited source code with "Ctrl + S".

Hello meathole app

Initial Setup (Hello meathole app)

$ sudo apt install tree

Install 'tree'


$ cd ~
$ mkdir washroom
$ cd washroom/

Make 'washroom' directory


$ pipenv install django==3.0.4

Install Django 3.0.4 in a virtual environment.


$ pipenv shell

Activate the virtual environment


(washroom) $ django-admin startproject toilet

Make 'toilet' project


(washroom) $ tree

If you use the "tree" command, you can see what our Django project structure.


.
├── Pipfile
├── Pipfile.lock
└── toilet
    ├── manage.py
    └── toilet
        ├── asgi.py
        ├── __init__.py
        ├── settings.py
        ├── urls.py
        └── wsgi.py

2 directories, 8 files


The "settings.py" file controls our project’s settings, "urls.py" tells Django which pages to build in response to a browser or url request, and "wsgi.py", which stands for web server gateway interface, helps Django serve our eventual web pages. The last file "manage.py" is used to execute various Django commands such as running the local web server or creating a new app.


Django comes with a built-in web server for local development purposes. We can start it with the "runserver" command.

(washroom) username@computername:~/washroom$ cd toilet/
(washroom) username@computername:~/washroom/toilet$ python3 manage.py runserver

You can use the command, "python", instead of "python3".

If you visit http://127.0.0.1:8000/ or http://localhost:8000 , you can see our familiar Django welcome page.

Create an app

Django uses the concept of projects and apps to keep code clean and readable. A single Django project contains one or more apps within it that all work together to power a web application. This is why the command for a new Django project is "startproject"! For example, a real-world Django e-commerce site might have one app for user authentication, another app for payments, and a third app to power item listing details. Each focuses on an isolated piece of functionality.


We need to create our first app which we’ll call "pages". From the command line, quit the server with "Control+c". Then use the "startapp" command.

(helloworld) $ python3 manage.py startapp pages


If you look again inside the directory with the "tree" command you’ll see Django has created a "pages" directory with the following files:


Let’s review what each new pages app file does:

  • admin.py is a configuration file for the built-in Django Admin app
  • apps.py is a configuration file for the app itself
  • "migrations/" keeps track of any changes to our "models.py" file so our database and "models.py" stay in sync
  • models.py is where we define our database models, which Django automatically translates into database tables
  • tests.py is for our app-specific tests
  • views.py is where we handle the request/response logic for our web app


Even though our new app exists within the Django project, Django doesn’t “know” about it until we explicitly add it. In your text editor open the "settings.py" file and scroll down to "INSTALLED_APPS" where you’ll see six built-in Django apps already there. Add our new "pages" app at the bottom:


# helloworld_project/settings.py

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'pages.apps.PagesConfig', # new
]


# means it's just a comment. So the program ignores it. You don't need to type '#' and 'texts next to #', but it will help you understand what the code means. And "# helloworld_project/settings.py" is not even a comment. It's for your understanding where the file is. It is not a comment, so you don't have to put it in your code.


We should always add our own local apps at the bottom because Django will read our "INSTALLED_APPS" in top down order. Therefore the internal "admin" app is loaded first, then "auth", and so on. We want the core Django apps to be available since it’s quite likely our own apps will rely on their functionality.


Another thing to note is you might be wondering why we can’t just list the app name "pages" here instead of the much longer "pages.apps.PagesConfig"? The answer is that Django creates an "apps.py" file with each new application and it’s possible to add additional information there, especially with the Signals framework which is an advanced technique. For our relatively basic application using "pages" instead would still work, but we’d miss out on additional options and so as a best practice, always use the full app config name like "pages.apps.PagesConfig".


We’ll build and add many more apps to our Django projects in later chapters so this pattern will become familiar with time.

Views and URLConfs

In Django, "Views" determine "what" content is displayed on a given page while "URLConfs" determine "where" that content is going.


URL -> View -> Model (typically) -> Template


Let’s start by updating the "views.py" file in our "pages" app to look as follows:


# pages/views.py

from django.http import HttpResponse

def homePageView(request):
    return HttpResponse('Hello, World!')


Basically we’re saying whenever the view function "homePageView" is called, return the text “Hello, World!” More specifically, we’ve imported the built-in "HttpResponse" method so we can return a response object to the user. Our function "homePageView" accepts the "request" object and returns a response with the string "Hello, World!".


Now we need to configure our urls. Within the "pages" app, create a new "urls.py" file.

(helloworld) $ cd ~
(helloworld) $ cd helloworld/
(helloworld) $ cd helloworld_project/
(helloworld) $ touch pages/urls.py


Then update it with the following code:

# pages/urls.py

from django.urls import path

from .views import homePageView

urlpatterns = [
    path('', homePageView, name='home')
]

(The above code is some what different to the original code from the book as the directory structure is some different to the book's. So the below explanation is not exact since we used from .views import homePageView instead of from . import views. And we replaced path('', views.homePageView, name='home') with path('', homePageView, name='home').)

On the top line we import "path" from Django to power our "urlpattern" and on the next line we import our views. The period used here "from . import views" means reference the current directory, which is our "pages" app containing both "views.py" and "urls.py". Our urlpattern has three parts:

  • a Python regular expression for the empty string ''
  • specify the view which is called "homePageView"
  • add an optional url name of 'home'


In other words, if the user requests the homepage, represented by the empty string '' then use the view called "homePageView".


We’re almost done. The last step is to configure our project-level "urls.py" file too. Remember that it’s common to have multiple apps within a single Django project, so they each need their own route.


Update the existing "helloworld_project/urls.py" file as follows:


# helloworld_project/urls.py

from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', include('pages.urls')),
]


We’ve imported "include" on the second line next to "path" and then created a new urlpattern for our "pages" app. Now whenever a user visits the homepage at / they will first be routed to the "pages" app and then to the "homePageView" view.


It’s often confusing to beginners that we don’t need to import the "pages" app here, yet we refer to it in our urlpattern as "pages.urls". The reason we do it this way is that the method "django.urls.include()" expects us to pass in a module, or app, as the first argument. So without using "include" we would need to import our "pages" app, but since we do use "include" we don’t have to at the project level!

Hello, meathole!

We have all the code we need now! To confirm everything works as expected, restart our Django server:

(helloworld) $ python3 manage.py runserver

If you refresh the browser for http://127.0.0.1:8000/ it now displays the text “Hello, meathole!”

Git

In the previous chapter we also installed "git" which is a version control system. Let’s use it here. The first step is to initialize (or add) "git" to our repository.

(helloworld) $ cd ~
(helloworld) $ cd helloworld/
(helloworld) $ git init

If you then type "git status" you’ll see a list of changes since the last git commit. Since this is our first commit, this list is all of our changes so far.


(helloworld) $ git status
On branch master

No commits yet

Untracked files:
  (use "git add <file>..." to include in what will be committed)

        Pipfile
        Pipfile.lock
        db.sqlite3
        helloworld_project/
        manage.py
        pages/

nothing added to commit but untracked files present (use "git add" to track)


(You can't see "db.sqlite3", "manage.py", "pages/", since we have two "helloworld_project/" folders. They are in the sub folder. This book merges the two folders, but we didn't.)


Or you'll see the below list.

(helloworld) username@usercomputer:~/helloworld$ git status
On branch master

No commits yet

Untracked files:
  (use "git add <file>..." to include in what will be committed)

	Pipfile
	Pipfile.lock
	helloworld_project/

nothing added to commit but untracked files present (use "git add" to track)


We next want to add all changes by using the command "add -A" and then "commit" the changes along with a message describing what has changed.


(helloworld) $ git add -A
(helloworld) $ git commit -m 'initial commit'


Please note Windows users may receive an error "git commit error: pathspec ‘commit’ did not match any file(s) known to git" which appears to be related to using single quotes '' as opposed to double quotes "". If you see this error, using double quotes for all commit messages going forward.

Bitbucket

It’s a good habit to create a remote repository of your code for each project. This way you have a backup in case anything happens to your computer and more importantly, it allows for collaboration with other software developers. The two most popular choices are Bitbucket and Github.

https://bitbucket.org

https://github.com


In this book we will use Bitbucket because it allows private repositories for free. Github charges a fee. Public repositories are available for anyone on the internet to use; private repositories are not. When you’re learning web development, it’s best to stick to private repositories so you don’t inadvertently post critical information such as passwords online.


To get started on Bitbucket, sign up for a free account. After confirming your account via email you’ll be sent a page to create a unique username for your Bitbucket Cloud.


When you see the message "Before we drop you in Bitbucket, help us tailor your experience", then just click the "Skip" button.


Next we can start our first code repository. Click on the button for “Create repository” since we want to add our existing local code to Bitbucket.


Then on the “Create a new repository” page enter in the name of your repository: “hello-world”. Also–and this is important–click on the dropdown menu next to “Include a README” and select “No” rather than the default “Yes, with a tutorial (for beginners)” button. Then click the blue “Create repository” button:


Since we already have local code we want to add to Bitbucket, look at the instructions on the page for “Get your local Git repository on Bitbucket.”


We’re already in the directory for our repo so skip Step 1. In Step 2, we’ll use two commands to add our project to Bitbucket. Note that your command will differ from mine since you have a different username. The general format is the below where "USER" is your Bitbucket username. Mine happens to be "wsvincent".

(helloworld) $ git remote add origin git@bitbucket.org:USER/hello-world.git

After running this command to configure git with this Bitbucket repository, we must “push” our code into it.

(helloworld) $ git push -u origin master

But it doesn't work. You should generate your RSA key pair and add your public key to Bitbucket.


(helloworld) $ ssh-keygen

If there are some messages, just pushing the Enter key is okay.


(helloworld) $ ls ~/.ssh

Then you can see three files, "id_rsa", "id_rsa.pub", "known_hosts".


(helloworld) $ cat ~/.ssh/id_rsa.pub

Then copy your public key. Just drag your mouse on it and then it will be copied if you use Ubuntu.

Visit https://bitbucket.org and click your user icon where the bottom and left of your web browser. Click "Bitbucket settings" and click "SSH keys" of the "SECURITY" section. And then click "Add key" and paste your public key.


And then type the below command. At this time, it will work well.

(helloworld) $ git push -u origin master


Now if you go back to your Bitbucket page and refresh it, you’ll see the code is now online!


Since we’re done, go ahead and exit our virtual environment with the "exit" command.

(helloworld) $ exit

You should now see no parentheses on your command line, indicating the virtual environment is no longer active.

Pages app

Initial Setup (Pages app)

$ cd ~
$ mkdir pp
$ cd pp
$ pipenv install django==2.1
$ pipenv shell
(pp) $ django-admin startproject pages_project
(pp) $ cd pages_project/
(pp) $ python3 manage.py startapp pages


Open your text editor and navigate to the file "settings.py" under "pages_project/" directory. Add the "pages" app at the bottom of our project under INSTALLED_APPS:


# pp/pages_project/pages_project/settings.py

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'pages.apps.PagesConfig', # new
]


Start the local web server with "runserver".

(pp) $ python3 manage.py runserver

And then navigate to http://127.0.0.1:8000/ .

Templates

It’s worth repeating this pattern since you’ll see it over and over again in Django development: Templates, Views, and URLs.

The URLs control the initial route, the entry point into a page, such as "/about", the views contain the logic or the “what”, and the template has the HTML. For web pages that rely on a database model, it is the view that does much of the work to decide what data is available to the template.


The question of where to place the templates directory can be confusing for beginners. By default, Django looks within each app for templates. In our "pages" app it will expect a home.html template to be located in the following location:

└── pp
    ├── pages_project
        ├── templates
            ├── home.html


This means we would need to create a new "templates" directory, a new directory with the name of the app, "pages", and finally our template itself.

A common question is: Why this repetitive structure? The short answer is that the Django template loader wants to be really sure it find the correct template and this is how it’s programmed to look for them.

Fortunately there’s another often-used approach to structuring the templates in a Django project. And that is to instead create a single, project-level "templates" directory that is available to all apps. This is the approach we’ll use. By making a small tweak to our settings.py file we can tell Django to also look in this project-level folder for templates.

First quit our server with Control-c. Then create a project-level folder called "templates" and an HTML file called home.html.

(pp) $ cd ~
(pp) $ cd pp/
(pp) $ cd pages_project/
(pp) $ mkdir templates
(pp) $ touch templates/home.html


Next we need to update settings.py to tell Django to look at the project-level for templates. This is a one-line change to the setting 'DIRS' under TEMPLATES.

# pp/pages_project/pages_project/settings.py

TEMPLATES = [
    {
        ...
        'DIRS': [os.path.join(BASE_DIR, 'templates')], # new
        ...
    },
]


Then we can add a simple headline to our home.html file.

<!-- pp/pages_project/templates/home.html -->

<h1>fucking website's home page</h1>

<!-- --> in HTML web pages is similar to # in source code of programming languages. And <!-- templates/home.html --> is not a comment. It is just for showing you where the file is. So you don't have to put it into the HTML file.

Ok, our template is complete! The next step is to configure our url and view.

Class-Based Views

In our view we’ll use the built-in TemplateView to display our template. Update the pages/views.py file.

# pp/pages_project/pages/views.py

from django.views.generic import TemplateView

class HomePageView(TemplateView):
    template_name = 'home.html'


Note that we’ve capitalized our view since it’s now a Python class. Classes, unlike functions, should always be capitalized. The TemplateView already contains all the logic needed to display our template, we just need to specify the template’s name.

URLs

The last step is to update our URLConfs. Recall from Chapter 2 that we need to make updates in two locations. First we update the project-level urls.py file to point at our "pages" app and then within "pages" we match the views to routes.


Let’s start with the project-level urls.py file.

# pp/pages_project/pages_project/urls.py

from django.contrib import admin
from django.urls import path, include # new

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', include('pages.urls')), # new
]


The code here should be review at this point. We add "include" on the second line to point the existing URL to the "pages" app. Next create an app-level urls.py file.

(pp) $ cd ~
(pp) $ cd pp/
(pp) $ cd pages_project/
(pp) $ touch pages/urls.py


And add the following code.

# pp/pages_project/pages/urls.py

from django.urls import path

from .views import HomePageView

urlpatterns = [
    path('', HomePageView.as_view(), name='home'),
]


This pattern is almost identical to what we did in Chapter 2 with one major difference. When using Class-Based Views, you always add as_view() at the end of the view name.

And we’re done! If you start up the web server with "python3 manage.py runserver" and navigate to http://127.0.0.1:8000/ you can see our new homepage.

(pp) $ python3 manage.py runserver


If you see an error message, type the below command.

(pp) $ python3 manage.py migrate

Add an About Page

The process for adding an about page is very similar to what we just did. We’ll create a new template file, a new view, and a new url route.

Quit the server with Control+c and create a new template called about.html.

(pp) $ cd ~
(pp) $ cd pp/
(pp) $ cd pages_project/
(pp) $ touch templates/about.html

Then populate it with a short HTML headline.

<!-- pp/pages_project/templates/about.html -->

<h1>About page</h1>


Create a new view for the page.

# pp/pages_project/pages/views.py

from django.views.generic import TemplateView

class HomePageView(TemplateView):
    template_name = 'home.html'

class AboutPageView(TemplateView):
    template_name = 'about.html'


And then connect it to a url at "about/".


# pp/pages_project/pages/urls.py

from django.urls import path

from .views import HomePageView, AboutPageView # new

urlpatterns = [
    path('about/', AboutPageView.as_view(), name='about'), # new
    path('', HomePageView.as_view(), name='home'),  
]


Start up the web server with "python3 manage.py runserver", navigate to http://127.0.0.1:8000/about , and you can see our new “About page”.

Extending Templates

The real power of templates is their ability to be extended. If you think about most web sites, there is content that is repeated on every page (header, footer, etc). Wouldn’t it be nice if we, as developers, could have one canonical place for our header code that would be inherited by all other templates?


Well we can! Let’s create a base.html file containing a header with links to our two pages. First Control+c and then type the following.

(pages) $ touch templates/base.html

Django has a minimal templating language for adding links and basic logic in our templates. You can see the full list of built-in template tags here in the official docs. Template tags take the form of {% something %} where the “something” is the template tag itself. You can even create your own custom template tags, though we won’t do that in this book.


To add URL links in our project we can use the built-in "url" template tag which takes the URL pattern name as an argument. Remember how we added optional URL names to our url routers? This is why. The url tag uses these names to automatically create links for us.

The URL route for our homepage is called "home" therefore to configure a link to it we would use the following: {% url 'home' %}.


<!-- templates/base.html -->

<header>
  <a href="//hiddenwep33eg4w225lcdwcez4iefacwpiia6cwg7pfmcz4hvijzbgid.onion.pet/{% url 'home' %}">Home</a> | <a href="//hiddenwep33eg4w225lcdwcez4iefacwpiia6cwg7pfmcz4hvijzbgid.onion.pet/{% url 'about' %}">About</a>
</header>

{% block content %}
{% endblock %}


At the bottom we’ve added a "block" tag called "content". Blocks can be overwritten by child templates via inheritance. While it’s optional to name our closing "endblock"–you can just write {% endblock %} if you prefer–doing so helps with readability, especially in larger template files.

Let’s update our home.html and about.html to extend the base.html template. That means we can reuse the same code from one template in another template. The Django templating language comes with an extends method that we can use for this.


<!-- templates/home.html -->

{% extends 'base.html' %}

{% block content %}
<h1>Homepage</h1>
{% endblock content %}


<!-- templates/about.html -->

{% extends 'base.html' %}

{% block content %}
<h1>About page</h1>
{% endblock %}


Now if you start up the server with "python3 manage.py runserver" and open up our web pages again at http://127.0.0.1:8000/ and http://127.0.0.1:8000/about you’ll see the header is magically included in both locations.


There’s a lot more we can do with templates and in practice you’ll typically create a base.html file and then add additional templates on top of it in a robust Django project. We’ll do this later on in the book.

Tests

Writing tests is important because it automates the process of confirming that the code works as expected. In an app like this one, we can manually look and see that the home page and about page exist and contain the intended content. But as a Django project grows in size there can be hundreds if not thousands of individual web pages and the idea of manually going through each page is not possible. Further, whenever we make changes to the code–adding new features, updating existing ones, deleting unused areas of the site–we want to be sure that we have not inadvertently broken some other piece of the site. Automated tests let us write one time how we expect a specific piece of our project to behave and then let the computer do the checking for us.

Fortunately Django comes with robust, built-in testing tools for writing and running tests.

If you look within our "pages" app, Django already provided a tests.py file we can use. Open it and add the following code:


# pages/tests.py

from django.test import SimpleTestCase

class PagesTests(SimpleTestCase):
    def test_home_page_status_code(self):
        response = self.client.get('/')
        self.assertEqual(response.status_code, 200)

    def test_about_page_status_code(self):
        response = self.client.get('/about/')
        self.assertEqual(response.status_code, 200)


We’re using SimpleTestCase here since we aren’t using a database. If we were using a database, we’d instead use TestCase. Then we perform a check if the status code for each page is 200, which is the standard response for a successful HTTP request. That’s a fancy way of saying it ensures that a given web page actually exists, but says nothing about the content of said page.

To run the tests quit the server Control+c and type "python3 manage.py test" on the command line:


(pages) $ python manage.py test
Creating test database for alias 'default'...
System check identified no issues (0 silenced).
..
----------------------------------------------------------------------
Ran 2 tests in 0.014s

OK
Destroying test database for alias 'default'...


Success! We’ll do much more with testing in the future, especially once we start working with databases. For now, it’s important to see how easy it is to add tests each and every time we add new functionality to our Django project.

Git and Bitbucket

It’s time to track our changes with git and push them up to Bitbucket. We’ll start by initializing our directory.

(pages) $ git init

Use "git status" to see all our code changes then "git add -A" to add them all. Finally we’ll add our first commit message.

(pages) $ git status
(pages) $ git add -A
(pages) $ git commit -m 'initial commit'

Over on Bitbucket create a new repo which we’ll call "pages-app".


On the next page, look for the commands under “Step 2: Connect your existing repository to Bitbucket.” Copy the two commands to your command line to link the repo and then push the repository to Bitbucket.


It should look like this, replacing "wsvincent" with your Bitbucket username:

(pages) $ git remote add origin git@bitbucket.org:wsvincent/pages-app.git
(pages) $ git push -u origin master


Local vs Production

Up to this point we’ve been using Django’s own internal web server to power our Pages application locally on our computer. But you can’t share a localhost address with someone else. To make our site available on the Internet where everyone can see it, we need to deploy our code to an external server that anyone can use to see our site. This is called putting our code into production. Local code lives only on our computer; production code lives on an external server.

There are many server providers available but we will use Heroku because it is free for small projects, widely-used, and has a relatively straightforward deployment process.

https://www.heroku.com

Heroku

You can sign up for a free Heroku account on their website. After you confirm your email Heroku will redirect you to the dashboard section of the site.


Now we need to install Heroku’s Command Line Interface (CLI) so we can deploy from the command line. We want to install Heroku globally so it is available across our entire computer, so open up a new command line tab: Command+t on a Mac, Control+t on Windows. If we installed Heroku within our virtual environment, it would only be available there.


Within this new tab, on a Ubuntu use "snap" to install Heroku:

$ sudo snap install --classic heroku


If you are using Linux except Ubuntu, there are specific install instructions available on the Heroku website.

https://devcenter.heroku.com/articles/heroku-cli


Once installation is complete you can close our new command line tab and return to the initial tab with the "pages" virtual environment active.


Type the command "heroku login" on the terminal to login to Heroku.

(pages) $ heroku login

Additional Files

We need to make the following four changes to our Pages project so it’s ready to deploy online with Heroku:

  • update "Pipfile.lock"
  • make a new "Procfile" file
  • install "gunicorn" as our web server
  • make a one-line change to "settings.py" file

Within your existing "Pipfile" specify the version of Python we’re using, which is "3.6". Add these two lines at the bottom of the file.

# pages/Pipfile

[requires]
python_version = "3.6.6"

Then run "pipenv lock" to generate the appropriate "Pipfile.lock".

(pages) $ pipenv lock

Heroku actually looks in our Pipfile.lock for information on our virtual environment, which is why we add the language setting here.

Next create a "Procfile" which is specific to Heroku.

(pages) $ touch Procfile

Open the "Procfile" with your text editor and add the following:

# pages/Procfile

web: gunicorn --pythonpath pages_project pages_project.wsgi:application --log-file -


  • How can I modify Procfile to run Gunicorn process in a non-standard folder on Heroku?

2013-05-07

https://stackoverflow.com/questions/16416172/how-can-i-modify-procfile-to-run-gunicorn-process-in-a-non-standard-folder-on-he

This says to use gunicorn, which is a web server suitable for production, instead of Django’s own server which is only suitable for local development.


The configuration for the server is contained in a wsgi.py file that Django automatically creates for every new project. It resides at the top-most, project level of our code. Since our project’s name is pages_project here, the file is located at "pages_project/wsgi.py" file.

(pages) $ pipenv install gunicorn==19.9.0

The final step is a one-line change to settings.py. Scroll down to the section called ALLOWED_HOSTS and add a '*' so it looks as follows:

# pages_project/settings.py

ALLOWED_HOSTS = ['*']

The ALLOWED_HOSTS setting represents which host/domain names our Django site can serve. This is a security measure to prevent HTTP Host header attacks, which are possible even under many seemingly-safe web server configurations. However we’ve used the wildcard Asterisk * which means all domains are acceptable to keep things simple. In a production-level Django site you would explicitly list which domains were allowed.


Use "git status" to check our changes, add the new files, and then commit them:

(pages) $ git status
(pages) $ git add -A
(pages) $ git commit -m "New updates for Heroku deployment"


Finally push to Bitbucket so we have an online backup of our code changes.

(pages) $ git push -u origin master

Deploy

The last step is to actually deploy with Heroku. If you’ve ever configured a server yourself in the past, you’ll be amazed at how much simpler the process is with a platform-as-a-service provider like Heroku.

Our process will be as follows:

  • create a new app on Heroku and push our code to it
  • add a git remote “hook” for Heroku
  • configure the app to ignore static files, which we’ll cover in later chapters
  • start the Heroku server so the app is live
  • visit the app on Heroku’s provided URL

We can do the first step, creating a new Heroku app, from the command line with "heroku create". Heroku will create a random name for our app, in my case "fathomless-hamlet-26076". Your name will be different.

(pages) $ heroku create
Creating app... done, ⬢ fathomless-hamlet-26076
https://fathomless-hamlet-26076.herokuapp.com/ | https://git.heroku.com/fathomless-hamlet-26076.git

Now we need to add a “hook” for Heroku within git. This means that git will store both our settings for pushing code to Bitbucket and to Heroku. My Heroku app is called "fathomless-hamlet-26076" so my command is as follows.

(pages) $ heroku git:remote -a fathomless-hamlet-26076

You should replace "fathomless-hamlet-26076" with the app name Heroku provides.


We only need to do one set of Heroku configurations at this point, which is to tell Heroku to ignore static files like CSS and JavaScript which Django by default tries to optimize for us. We’ll cover this in later chapters so for now just run the following command.

(pages) $ heroku config:set DISABLE_COLLECTSTATIC=1


Now we can push our code to Heroku. Because we set our “hook” previously, it will go to Heroku.

(pages) $ git push heroku master


If we just typed "git push origin master" then the code is pushed to Bitbucket, not Heroku. Adding "heroku" to the command sends the code to Heroku. This is a little confusing the first few times.

Finally we need to make our Heroku app live. As websites grow in traffic they need additional Heroku services but for our basic example we can use the lowest level, web=1, which also happens to be free!


Type the following command.

(pages) $ heroku ps:scale web=1


We’re done! The last step is to confirm our app is live and online. If you use the command "heroku open" your web browser will open a new tab with the URL of your app:

(pages) $ heroku open

Mine is at https://fathomless-hamlet-26076.herokuapp.com/. You can see both the homepage and the about page.


Additional tips

(pages) $ heroku help

To see heroku's commands list


(pages) $ heroku logs

To see heroku's logs


(pages) $ heroku apps

To see your heroku apps. You can make up to five apps.


(pages) $ heroku apps:destroy

To destroy your app

To proceed, type fathomless-hamlet-26076. You should replace "fathomless-hamlet-26076" to your own.


Or type the below command to destroy your app

(pages) $ heroku apps:destroy fathomless-hamlet-26076

Also to proceed, type fathomless-hamlet-26076

Message Board app

Initial Setup (Message Board app)


$ cd ~
$ mkdir mb
$ cd mb
$ pipenv install django==2.1
$ pipenv shell
(mb) $ django-admin startproject mb_project .
(mb) $ python manage.py startapp posts


Tell Django about the new app "posts" by adding it to the bottom of the "INSTALLED_APPS" section of our "settings.py" file. Open it with your text editor of choice.

# mb_project/settings.py

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'posts.apps.PostsConfig', # new
]


Then execute the migrate command to create an initial database based on Django’s default settings.

(mb) $ python3 manage.py migrate

If you look inside our directory with the "ls" command, you’ll see there’s now a "db.sqlite3" file representing our SQLite database.

(mb) $ ls
Pipfile      db.sqlite3   mb_project
Pipfile.lock manage.py    posts


Aside: Technically a "db.sqlite3" file is created the first time you run either "migrate" or "runserver". Using "runserver" configures a database using Django’s default settings, however "migrate" will sync the database with the current state of any database models contained in the project and listed in "INSTALLED_APPS". In other words, to make sure the database reflects the current state of your project you’ll need to run "migrate" (and also "makemigrations") each time you update a model. More on this shortly.

To confirm everything works correctly, spin up our local server.

(mb) $ python3 manage.py runserver


And navigate to http://127.0.0.1:8000/ to see the familiar Django installed correctly page.

Create a database model

Our first task is to create a database model where we can store and display posts from our users. Django will turn this model into a database table for us. In real-world Django projects, it’s often the case that there will be many complex, interconnected database models but in our simple message board app we only need one.

I won’t cover database design in this book but I have written a short guide which you can find here if this is all new to you.

https://wsvincent.com/database-design-tutorial-for-beginners/


Open the "posts/models.py" file and look at the default code which Django provides:

# posts/models.py
from django.db import models

# Create your models here


Django imports a module "models" to help us build new database models, which will “model” the characteristics of the data in our database. We want to create a model to store the textual content of a message board post, which we can do so as follows:

# posts/models.py
from django.db import models


class Post(models.Model):
    text = models.TextField()


Note that we’ve created a new database model called "Post" which has the database field "text". We’ve also specified the type of content it will hold, "TextField()". Django provides many model fields supporting common types of content such as characters, dates, integers, emails, and so on.

Activating models

Now that our new model is created we need to activate it. Going forward, whenever we create or modify an existing model we’ll need to update Django in a two-step process.


1. First we create a migration file with the "makemigrations" command which generate the SQL commands for preinstalled apps in our "INSTALLED_APPS" setting. Migration files do not execute those commands on our database file, rather they are a reference of all new changes to our models. This approach means that we have a record of the changes to our models over time.


2. Second we build the actual database with "migrate" which does execute the instructions in our migrations file.


Make sure the local server is stopped Control+c and then run the following two commands:

(mb) $ python3 manage.py makemigrations posts
(mb) $ python3 manage.py migrate


Note that you don’t have to include a name after "makemigrations". If you simply run the command it will apply to all available changes. But it’s a good habit to be specific. If we had two separate apps in our project, and updated the models in both, and then ran "makemigrations" it would generate a migrations file containing data on both changes. This makes debugging harder in the future. You want each migration file to be as small and isolated as possible. That way if you need to look at past migrations, there is only one change per migration rather than one that applies to multiple apps.

Django Admin

Django provides us with a robust admin interface for interacting with our database. This is a truly killer feature that few web frameworks offer. It has its routes in Django’s origin as a project at a newspaper. The developers wanted a CMS (content management system) so that journalists could write and edit their stories without needing to touch “code.” Over time the built-in admin app has evolved into a fantastic, out-of-the-box tool for managing all aspects of a Django project.


To use the Django admin, we first need to create a "superuser" who can login. In your command line console, type "python3 manage.py createsuperuser" and respond to the prompts for a username, email, and password:

(mb) $ python manage.py createsuperuser
Username (leave blank to use 'wsv'): admin
Email:
Password:
Password (again):
Superuser created successfully.


Note: You can use any username for superuser or administrator instead of "admin". You don't have to input your email address. Just press "Enter" key is okay. When you type your password, it will not appear visible in the command line console for security reasons.

Restart the Django server with "python3 manage.py runserver" and in your browser go to http://127.0.0.1:8000/admin/ . You should see the admin’s login screen:


Log in by entering the username and password you just created. You will see the Django admin homepage next:


But where’s our "posts" app? It’s not displayed on the main admin page!

We need to explicitly tell Django what to display in the admin. Fortunately we can change fix this easily by opening the "posts/admin.py" file and editing it to look like this:

# posts/admin.py
from django.contrib import admin

from .models import Post

admin.site.register(Post)


Django now knows that it should display our "posts" app and its database model "Post" on the admin page. If you refresh your browser you’ll see that it now appears:


Now let’s create our first message board post for our database. Click on the "+ Add" button opposite "Posts". Enter your own text in the "Text" form field.

Then click the “Save” button, which will redirect you to the main Post page. However if you look closely, there’s a problem: our new entry is called “Post object”, which isn’t very helpful.


Let’s change that. Within the "posts/models.py" file, add a new function "__str__" as follows:

# posts/models.py
from django.db import models


class Post(models.Model):
    text = models.TextField()

    def __str__(self):
        return self.text[:50]


This will display the first 50 characters of the "text" field. If you refresh your Admin page in the browser, you’ll see it’s changed to a much more descriptive and helpful representation of our database entry.


Much better! It’s a best practice to add "str()" methods to all of your models to improve their readability.


Views/Templates/URLs

In order to display our database content on our homepage, we have to wire up our views, templates, and URLConfs. This pattern should start to feel familiar now.


Let’s begin with the view. Earlier in the book we used the built-in generic TemplateView to display a template file on our homepage. Now we want to list the contents of our database model. Fortunately this is also a common task in web development and Django comes equipped with the generic class-based ListView.


In the "posts/views.py" file enter the Python code below:

# posts/views.py
from django.views.generic import ListView
from .models import Post


class HomePageView(ListView):
    model = Post
    template_name = 'home.html'


On the first line we’re importing "ListView" and in the second line we need to explicitly define which model we’re using. In the view, we subclass "ListView", specify our model name and specify our template reference. Internally "ListView" returns an object called "object_list" that we want to display in our template.


Our view is complete which means we still need to configure our URLs and make our template. Let’s start with the template. Create a project-level directory called "templates" and a "home.html" template file.

(mb) $ mkdir templates
(mb) $ touch templates/home.html


Then update the "DIRS" field in our "settings.py" file so that Django knows to look in this templates folder.

# mb_project/settings.py
TEMPLATES = [
    {
        ...
        'DIRS': [os.path.join(BASE_DIR, 'templates')], # new
        ...
    },
]


In our templates file "home.html" we can use the Django Templating Language’s "for" loop to list all the objects in "object_list". Remember that "object_list" is what "ListView" returns to us.


Why "object_list"? This is the name of the variable that "ListView" returns to us. So it’s hardcoded in which can be tricky to understand at first. Also note that we should specify not just the object we want, "post", but the specific field we want to display which is "text". So we will use "post.text".

<!-- templates/home.html -->
<h1>Message board homepage</h1>
<ul>
  {% for post in object_list %}
    <li>{{ post.text }}</li>
  {% endfor %}
</ul>


However "object_list" isn’t very friendly is it? In fact, it’s one of the common points of confusion for developers new to generic class-based views. So let’s instead provide an explicit name which we can do via context-object-name which is another Django best practice. It makes it easier for developers and especially designers to know what is contained in the template!


Back in our "posts/views.py" file add the following:

# posts/views.py
from django.views.generic import ListView
from .models import Post


class HomePageView(ListView):
    model = Post
    template_name = 'home.html'
    context_object_name = 'all_posts_list' # new


And don’t forget to update our template, too.

<!-- templates/home.html -->
<h1>Message board homepage</h1>
<ul>
  {% for post in all_posts_list %}
    <li>{{ post.text }}</li>
  {% endfor %}
</ul>


The last step is to set up our URLConfs. Let’s start with the project-level "urls.py" file where we simply include our "posts" and add "include" on the second line.

# mb_project/urls.py
from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),
    path(, include('posts.urls')),
]


Then create an app-level "urls.py" file.

(mb) $ touch posts/urls.py

And update it like so:

# posts/urls.py
from django.urls import path

from .views import HomePageView

urlpatterns = [
    path(, HomePageView.as_view(), name='home'),
]


Restart the server with "python3 manage.py runserver" and navigate to our homepage http://127.0.0.1:8000/ which now lists out our message board posts.


We’re basically done at this point, but let’s create a few more message board posts in the Django admin to confirm that they will display correctly on the homepage.

Adding new posts

To add new posts to our message board, go back into the Admin at http://127.0.0.1:8000/admin/ and create two more posts. Here’s what mine look like:


If you return to the homepage you’ll see it automatically displays our formatted posts. Woohoo!


Everything works so it’s a good time to initialize our directory, add the new code, and include our first "git" commit.

(mb) $ git init
(mb) $ git add -A
(mb) $ git commit -m 'initial commit'

Tests (Message Board app)

Previously we were only testing static pages so we used "SimpleTestCase". But now that our homepage works with a database, we need to use "TestCase" which will let us create a “test” database we can check against. In other words, we don’t need to run tests on our actual database but instead can make a separate test database, fill it with sample data, and then test against it.


Let’s start by adding a sample post to the "text" database field and then check that it is stored correctly in the database. It’s important that all our test methods start with "test_" so Django knows to test them! The code will look like this:

# posts/tests.py
from django.test import TestCase
from .models import Post


class PostModelTest(TestCase):

    def setUp(self):
        Post.objects.create(text='just a test')

    def test_text_content(self):
        post=Post.objects.get(id=1)
        expected_object_name = f'{post.text}'
        self.assertEqual(expected_object_name, 'just a test')


At the top we import the "TestCase" module which lets us create a sample database, then import our "Post" model. We create a new class "PostModelTest" and add a method "setUp" to create a new database that has just one entry: a post with a text field containing the string ‘just a test’.


Then we run our first test, "test_text_content", to check that the database field actually contains "just a test". We create a variable called "post" that represents the first "id" on our Post model. Remember that Django automatically sets this id for us. If we created another entry it would have an id of 2, the next one would be 3, and so on.


The following line uses f strings which are a very cool addition to Python. They let us put variables directly in our strings as long as the variables are surrounded by brackets { }. Here we’re setting "expected_object_name" to be the string of the value in "post.text", which should be "just a test".


On the final line we use "assertEqual" to check that our newly created entry does in fact match what we input at the top. Go ahead and run the test on the command line with "python3 manage.py test".


(mb) $ python3 manage.py test
Creating test database for alias 'default'...
System check identified no issues (0 silenced).

----------------------------------------------------------------------
Ran 1 test in 0.001s

OK
Destroying test database for alias 'default'...


It passed!


Don’t worry if the previous explanation felt like information overload. That’s natural the first time you start writing tests, but you’ll soon find that most tests that you write are actually quite repetitive.


Time for our second test. The first test was on the model but now we want test our one and only page: the homepage. Specifically, we want to test that it exists (throws an HTTP 200 response), uses the "home" view, and uses the "home.html" template.


We’ll need to add one more import at the top for "reverse" and a brand new class "HomePageViewTest" for our test.


from django.test import TestCase
from django.urls import reverse

from .models import Post

class PostModelTest(TestCase):

    def setUp(self):
        Post.objects.create(text='just a test')

    def test_text_content(self):
        post=Post.objects.get(id=1)
        expected_object_name = f'{post.text}'
        self.assertEqual(expected_object_name, 'just a test')

class HomePageViewTest(TestCase):

    def setUp(self):
        Post.objects.create(text='this is another test')

    def test_view_url_exists_at_proper_location(self):
        resp = self.client.get('/')
        self.assertEqual(resp.status_code, 200)

    def test_view_url_by_name(self):
        resp = self.client.get(reverse('home'))
        self.assertEqual(resp.status_code, 200)

    def test_view_uses_correct_template(self):
        resp = self.client.get(reverse('home'))
        self.assertEqual(resp.status_code, 200)
        self.assertTemplateUsed(resp, 'home.html')


If you run our tests again you should see that they pass.

(mb) $ python3 manage.py test
Creating test database for alias 'default'...
System check identified no issues (0 silenced).
.
----------------------------------------------------------------------
Ran 4 tests in 0.036s

OK
Destroying test database for alias 'default'...


Why does it say four tests? Remember that our "setUp" methods are not actually tests, they merely let us run subsequent tests. Our four actual tests are "test_text_content", "test_view_url_exists_at_proper_location", "test_view_url_by_name", and "test_view_uses_correct_template".


Any function that has the word "test*" at the beginning and exists in a "tests.py" file will be run when we execute the command "python3 manage.py test".


We’re done adding code for our testing so it’s time to commit the changes to git.

(mb) $ git add -A
(mb) $ git commit -m 'added tests'

Bitbucket (Message Board app)

We also need to store our code on Bitbucket. This is a good habit to get into in case anything happens to your local computer and it also allows you to share and collaborate with other developers.


You should already have a Bitbucket account from Chapter 3 so go ahead and create a new repo which we’ll call "mb-app".

https://bitbucket.org/repo/create


On the next page click on the bottom link for “I have an existing project”. Copy the two commands to connect and then push the repository to Bitbucket.


It should look like this, replacing "wsvincent" (my username) with your Bitbucket username:

(mb) $ git remote add origin git@bitbucket.org:wsvincent/mb-app.git
(mb) $ git push -u origin master


Heroku configuration

You should also already have a Heroku account setup and installed from Chapter 3. We need to make the following changes to our Message Board project to deploy it online:

  • update Pipfile.lock
  • new Procfile
  • install gunicorn
  • update settings.py


Within your "Pipfile" specify the version of Python we’re using, which is "3.6.6". Add these two lines at the bottom of the file.


# Pipfile

[requires]
python_version = "3.6.6"

I use "# Pipfile" to show where the file is to you. So you don't have to put it into the file.


Run "pipenv lock" to generate the appropriate "Pipfile.lock".

(mb) $ pipenv lock

Then create a "Procfile" which tells Heroku how to run the remote server where our code will live.

(mb) $ touch Procfile

For now we’re telling Heroku to use "gunicorn" as our production server and look in our "mb_project.wsgi" file for further instructions.

# Procfile

web: gunicorn mb_project.wsgi --log-file -

You don't have to put "# Procfile" into the file. It's just for your understanding where the file is.


Next install gunicorn which we’ll use in production while still using Django’s internal server for local development use.

(mb) $ pipenv install gunicorn==19.9.0


Finally update "ALLOWED_HOSTS" in our "settings.py" file.

# mb_project/settings.py
ALLOWED_HOSTS = ['*']


We’re all done! Add and commit our new changes to git and then push them up to Bitbucket.

(mb) $ git status
(mb) $ git add -A
(mb) $ git commit -m 'New updates for Heroku deployment'
(mb) $ git push -u origin master

Heroku deployment

Make sure you’re logged into your correct Heroku account.

(mb) $ heroku login

Then run the "create" command and Heroku will randomly generate an app name for you. You can customize this later if desired.

(mb) $ heroku create
Creating app... done, ⬢ sleepy-brook-64719
https://sleepy-brook-64719.herokuapp.com/ | https://git.heroku.com/sleepy-brook-64719.git

Set "git" to use the name of your new app when you push code to Heroku. My Heroku-generated name is "sleepy-brook-64719" so the command looks like this.

(mb) $ heroku git:remote -a sleepy-brook-64719

Tell Heroku to ignore static files which we’ll cover in-depth when deploying our Blog app later in the book.

(mb) $ heroku config:set DISABLE_COLLECTSTATIC=1

Push the code to Heroku and add free scaling so it’s actually running online, otherwise the code is just sitting there.

(mb) $ git push heroku master
(mb) $ heroku ps:scale web=1

If you open the new project with "heroku open" it will automatically launch a new browser window with the URL of your app. Mine is live at https://sleepy-brook-64719.herokuapp.com/ .

Blog app

In this chapter we’ll build a Blog application that allows users to create, edit, and delete posts. The homepage will list all blog posts and there will be a dedicated detail page for each individual post. We’ll also introduce CSS for styling and learn how Django works with static files.

Initial Setup (Blog app)

As covered in previous chapters, our steps for setting up a new Django project are as follows:

  • create a new directory for our code on the home folder called "blog"
  • install Django in a new virtual environment
  • create a new Django project called blog_project
  • create a new app blog
  • perform a migration to set up the database
  • update settings.py


Execute the following commands in a new command line console. Note that the actual name of the virtual environment will be (blog-XXX) where XXX represents random characters. I’m using (blog) here to keep things simpler since my name will differ from yours.


And don’t forget to include the period . at the end of the command for creating our new blog_project.


Command Line

$ cd ~
$ mkdir blog
$ cd blog
$ pipenv install django==2.0.6
$ pipenv shell
(blog) $ django-admin startproject blog_project .
(blog) $ python manage.py startapp blog
(blog) $ python manage.py migrate
(blog) $ python manage.py runserver


To ensure Django knows about our new app, open your text editor and add the new app to INSTALLED_APPS in our settings.py file:


Code

# blog_project/settings.py
INSTALLED_APPS = ['django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'blog', # new
]


If you navigate to http://127.0.0.1:8000/ in your browser you should see the following page.

Ok, initial installation complete! Next we’ll create our database model for blog posts.

Database Models

What are the characteristics of a typical blog application? In our case let’s keep things simple and assume each post has a title, author, and body. We can turn this into a database model by opening the "blog/models.py" file and entering the code below:


Code

# blog/models.py
from django.db import models

class Post(models.Model):
    title = models.CharField(max_length=200)
    author = models.ForeignKey('auth.User', on_delete=models.CASCADE,)
    body = models.TextField()

    def __str__(self):
        return self.title


At the top we’re importing the class models and then creating a subclass of "models.Model" called "Post". Using this subclass functionality we automatically have access to everything within "django.db.models.Models" and can add additional fields and methods as desired.


For title we’re limiting the length to 200 characters and for body we’re using a TextField which will automatically expand as needed to fit the user’s text. There are many field types available in Django; you can see the full list here.

https://docs.djangoproject.com/en/2.0/topics/db/models/#fields


For the author field we’re using a ForeignKey which allows for a many-to-one relationship. This means that a given user can be the author of many different blog posts but not the other way around. The reference is to the built-in User model that Django provides for authentication. For all many-to-one relationships such as a ForeignKey we must also specify an on_delete option.


Now that our new database model is created we need to create a new migration record for it and migrate the change into our database. This two-step process can be completed with the commands below:


Command Line

(blog) $ python manage.py makemigrations blog
(blog) $ python manage.py migrate blog


Our database is configured! What’s next?


Admin

We need a way to access our data. Enter the Django admin! First create a superuser account by typing thecommand below and following the prompts to set up an email and password. Note that when typing your password, it will not appear on the screen for security reasons.


Command Line

(blog) $ python manage.py createsuperuser
Username (leave blank to use 'wsv'): admin
Email:
Password:
Password (again):
Superuser created successfully.


Now start running the Django server again with the command "python manage.py runserver" and open up the Django admin at http://127.0.0.1:8000/admin/ . Login with your new superuser account.


Oops! Where’s our new Post model?


We forgot to update blog/admin.py so let’s do that now.


Code

# blog/admin.py
from django.contrib import admin
from .models import Post

admin.site.register(Post)


If you refresh the page you’ll see the update.


Let’s add two blog posts so we have some sample data to work with. Click on the + Add button next to Posts to create a new entry. Make sure to add an “author” to each post too since by default all model fields are required. If you try to enter a post without an author you will see an error. If we wanted to change this, we could add field options to our model to make a given field optional or fill it with a default value.


Now that our database model is complete we need to create the necessary views, URLs, and templates so we can display the information on our web application.


URLs (Blog app)

We want to display our blog posts on the homepage so, as in previous chapters, we’ll first configure our project-level URLConfs and then our app-level URLConfs to achieve this. Note that “project-level” means in the same parent folder as the blog_project and blog app folders.


On the command line quit the existing server with Control-c and create a new urls.py file within our blog:


Command Line

(blog) $ touch blog/urls.py


Now update it with the code below.


Code

# blog/urls.py
from django.urls import path

from . import views

urlpatterns = [
    path('', views.BlogListView.as_view(), name='home'),
]


We’re importing our soon-to-be-created views at the top. The empty string '' tells Python to match all values and we make it a named URL, home , which we can refer to in our views later on. While it’s optional to add a named URL it’s a best practice you should adopt as it helps keep things organized as your number of URLs grows.

We also should update our project-level urls.py file so that it knows to forward all requests directly to the blog app.


Code

# blog_project/urls.py
from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', include('blog.urls')),
]


We’ve added include on the second line and a urlpattern using an empty string regular expression '' indicating that URL requests should be redirected as is to blog’s URLs for further instructions.

Views

We’re going to use class-based views but if want to see a function-based way to build a blog application, I highly recommend the Django Girls Tutorial. It is excellent.

https://tutorial.djangogirls.org/en/


In our views file, add the code below to display the contents of our Post model using ListView.


Code

# blog/views.py
from django.views.generic import ListView

from . models import Post

class BlogListView(ListView):
    model = Post
    template_name = 'home.html'


On the top two lines we import ListView and our database model Post. Then we subclass ListView and add links to our model and template. This saves us a lot of code versus implementing it all from scratch.

Templates (Blog app)

With our URLConfs and views now complete, we’re only missing the third piece of the puzzle: templates.


As we already saw in Chapter 4, we can inherit from other templates to keep our code clean. Thus we’ll start off with a base.html file and a home.html file that inherits from it. Then later when we add templates for creating and editing blog posts, they too can inherit from base.html.


Start by creating our project-level templates directory with the two template files.


Command Line

(blog) $ mkdir templates
(blog) $ touch templates/base.html
(blog) $ touch templates/home.html


Then update settings.py so Django knows to look there for our templates.

Code

# blog_project/settings.py
TEMPLATES = [
    {
        ...
        'DIRS': ['templates'],
        ...
    },
]


Then update the base.html template as follows.


Code

<!-- templates/base.html -->
<html>
    <head>
        <title>Django blog</title>
    </head>

    <body>
        <header>
            <h1><a href="//hiddenwep33eg4w225lcdwcez4iefacwpiia6cwg7pfmcz4hvijzbgid.onion.pet/">Django blog</a></h1>
        </header>

        <div class="container">
            {% block content %}
            {% endblock content %}
        </div>
    </body>
</html>


Note that code between {% block content %} and {% endblock content %} can be filled by other templates. Speaking of which, here is the code for home.html.


Code

<!-- templates/home.html -->
{% extends 'base.html' %}

{% block content %}
{% for post in object_list %}
<div class="post-entry">
    <h2><a href="">{{ post.title }}</a></h2>
    <p>{{ post.body }}</p>
</div>
{% endfor %}
{% endblock content %}


At the top we note that this template extends base.html and then wraps our desired code with "content" blocks. We use the Django Templating Language to set up a simple for loop for each blog post. Note that object_list comes from ListView and contains all the objects in our view.


If you start the Django server again: "python manage.py runserver".

And refresh http://127.0.0.1:8000/ we can see it’s working.

But it looks terrible. Let’s fix that!

Static files

We need to add some CSS which is referred to as a static file because, unlike our dynamic database content, it doesn’t change. Fortunately it’s straightforward to add static files like CSS, JavaScript, and images to our Django project.


In a production-ready Django project you would typically store this on a Content Delivery Network (CDN) for better performance, but for our purposes storing the files locally is fine.


First quit our local server with Control-c. Then create a project-level folder called "static".


Command Line

(blog) $ mkdir static


Just as we did with our "templates" folder we need to update settings.py to tell Django where to look for these static files. We can update settings.py with a one-line change for STATICFILES_DIRS. Add it at the bottom of the file below the entry for STATIC_URL.


Code

# blog_project/settings.py
STATICFILES_DIRS = [os.path.join(BASE_DIR, 'static')]


Now create a css folder within "static" and add a new base.css file in it.


Command Line

(blog) $ mkdir static/css
(blog) $ touch static/css/base.css


What should we put in our file? How about changing the title to be red?


Code

/* static/css/base.css */
header h1 a {
    color: red;
}


As always, /* */ is just a comment and not a code.


Last step now. We need to add the static files to our templates by adding {% load staticfiles %} to the top of base.html. Because our other templates inherit from base.html we only have to add this once. Include a new line at the bottomof the <head></head> code that explicitly references our new base.css file.


Code

<!-- templates/base.html -->
{% load static %}
<ht</nowiki>ml>
     <head>
         <title>Django blog</title>
         <link rel="stylesheet" href="//hiddenwep33eg4w225lcdwcez4iefacwpiia6cwg7pfmcz4hvijzbgid.onion.pet/{% static 'css/base.css' %}">
     </head>
 ...


Phew! That was a bit of a pain but it’s a one-time pain. Now we can add static files to our "static" folder and they’ll automatically appear in all our templates.


Start up the server again with "python manage.py runserver" and look at our updated homepage at http://127.0.0.1:8000/ .


Blog homepage with red title


We can do a little better though. How about if we add a custom [[font]] and some more [[CSS]]? Since this [[book]] is not a tutorial on CSS simply insert the following between <nowiki><head></head> tags to add Source Sans Pro, a free font from Google.


Code

<!-- templates/base.html -->
{% load static %}
<ht</nowiki>ml>
     <head>
         <title>Django blog</title>
         <link href="//fonts.googleapis.com/css?family=Source+Sans+Pro:400" rel="stylesheet">
         <link rel="stylesheet" href="//hiddenwep33eg4w225lcdwcez4iefacwpiia6cwg7pfmcz4hvijzbgid.onion.pet/{% static 'css/base.css' %}">
     </head>
 ...


Then update our css file by copy and pasting the following code:

Code
 /* static/css/base.css */
 body {
     font-family: 'Source Sans Pro', sans-serif;
     font-size: 18px;
 }
 
 header {
     border-bottom: 1px solid #999;
     margin-bottom: 2rem;
     display: flex;
 }
 
 header h1 a {
     color: red;
     text-decoration: none;
 }
 
 .nav-left {
     margin-right: auto;
 }
 
 .nav-right {
     display: flex;
     padding-top: 2rem;
 }
 
 .post-entry {
     margin-bottom: 2rem;
 }
 
 .post-entry h2 {
     margin: 0.5rem 0;
 }
 
 .post-entry h2 a,
 .post-entry h2 a:visited {
     color: blue;
     text-decoration: none;
 }
 
 .post-entry p {
     margin: 0;
     font-weight: 400;
 }
 
 .post-entry h2 a:hover {
     color: red;
 }


Refresh the homepage at http://127.0.0.1:8000/ and you should see thefollowing.

Blog homepage with CSS

== Individual blog pages ==
Now we can add the functionality for individual blog pages. How do we do that? We need to create a new view, url, and template. I hope you’re noticing a pattern in development with Django now!


Start with the view. We can use the generic class-based DetailView to simplify things. At the top of the file add DetailView to the list of imports and then create our new view called BlogDetailView .


Code
 # blog/views.py
 from django.views.generic import ListView, DetailView
 
 from . models import Post
 
 class BlogListView(ListView):
     model = Post
     template_name = 'home.html'
 
 class BlogDetailView(DetailView):
     model = Post
     template_name = 'post_detail.html'


In this new view we define the model we’re using, "Post", and the template we want it associated with, "post_detail.html". By default "DetailView" will provide a context "object" we can use in our template called either object or the lowercased name of our model, "post". Also, "DetailView" expects either a primary key or a slug passed to it as the identifier. More on this shortly.


Now exit the local server Control-c and create our new template for a postdetail as follows:


Command Line
 (blog) $ touch templates/post_detail.html


Then type in the following code:


Code
 <nowiki><!-- templates/post_detail.html -->
{% extends 'base.html' %}

{% block content %}
<div class="post-entry">
    <h2>{{ post.title }}</h2>
    <p>{{ post.body }}</p>
</div>

{% endblock content %}


At the top we specify that this template inherits from base.html. Then display the title and body from our context object, which DetailView makes accessible as post.


Personally I found the naming of context objects in generic views extremely confusing when first learning Django. Because our context object from DetailView is either our model name post or object we could also update our template as follows and it would work exactly the same.


Code

<!-- templates/post_detail.html -->
{% extends 'base.html' %}

{% block content %}
<div class="post-entry">
    <h2>{{ object.title }}</h2>
    <p>{{ object.body }}</p>
</div>

{% endblock content %}


If you find using post or object confusing, we can also explicitly set the name of the context object in our view. So if we wanted to call it anything_you_want and then use that in the template, the code would look as follows and it would work the same.


Code

# blog/views.py
...
class BlogDetailView(DetailView):
    model = Post
    template_name = 'post_detail.html'
    context_object_name = 'anything_you_want'


Code

<!-- templates/post_detail.html -->
{% extends 'base.html' %}

{% block content %}
<div class="post-entry">
    <h2>{{ anything_you_want.title }}</h2>
    <p>{{ anything_you_want.body }}</p>
</div>

{% endblock content %}


The “magic” naming of the context object is a price you pay for the ease and simplicity of using generic views. They’re great if you know what they’re doing but can be hard to customize if you want different behavior.


Ok, what’s next? How about adding a new URLConf for our view, which we can do as follows.


Code

# blog/urls.py
from django.urls import path

from . import views

urlpatterns = [
    path(, views.BlogListView.as_view(), name='home'),
    path('post/<int:pk>/', views.BlogDetailView.as_view(), name='post_detail'),
]


All blog post entries will start with "post/". Next is the primary key for our post entry which will be represented as an integer <int:pk>. What’s the primary key you’re probably asking? Django automatically adds an "auto-incrementing primary key" to our database models. So while we only declared the fields "title", "author", and "body" on our Post model, under-the-hood Django also added another field called "id", which is our primary key. We can access it as either "id" or "pk".


The "pk" for our first “Hello, World” post is 1. For the second post, it is 2. And so on. Therefore when we go to the individual entry page for our first post, we can expect that its "urlpattern" will be post/1.


Note: Understanding how primary keys work with DetailView is a very common place of confusion for beginners. It’s worth re-reading the previous two paragraphs a few times if it doesn’t click. With practice it will become second nature.


If you now start up the server with "python manage.py runserver" and go directly to http://127.0.0.1:8000/post/1/ you’ll see a dedicated page for our first blog post.


Blog post one detail


Woohoo! You can also go to http://127.0.0.1:8000/post/2/ to see the second entry.


To make our life easier, we should update the link on the homepage so we can directly access individual blog posts from there. Currently in home.html our link is empty: <a href="">. Update it to <a href="//hiddenwep33eg4w225lcdwcez4iefacwpiia6cwg7pfmcz4hvijzbgid.onion.pet/{% url 'post_detail' post.pk %}">.


Code

<!-- templates/home.html -->
{% extends 'base.html' %}

{% block content %}
{% for post in object_list %}
<div class="post-entry">
    <h2><a href="//hiddenwep33eg4w225lcdwcez4iefacwpiia6cwg7pfmcz4hvijzbgid.onion.pet/{% url 'post_detail' post.pk %}">{{ post.title }}</a></h2>
    <p>{{ post.body }}</p>
</div>
{% endfor %}
{% endblock content %}


We start off by telling our Django template we want to reference a URLConf by using the code {% url ... %}. Which URL? The one named post_detail, which is the name we gave BlogDetailView in our URLConf just a moment ago.

If we look at post_detail in our URLConf, we see that it expects to be passed an argument "pk" representing the primary key for the blog post. Fortunately, Django has already created and included this "pk" field on our "post" object. We pass it into the URLConf by adding it in the template as "post.pk".


To confirm everything works, refresh the main page at http://127.0.0.1:8000/ and click on the title of each blog post to confirm the new links work.


Tests (Blog app)

We need to test our model and views now. We want to ensure that the "Post" model works as expected, including its "str" representation. And we want to test both "ListView" and "DetailView".

Here’s what sample tests look like in the blog/tests.py file.


Code

# blog/tests.py
from django.contrib.auth import get_user_model
from django.test import Client, TestCase
from django.urls import reverse

from .models import Post


class BlogTests(TestCase):

    def setUp(self):
        self.user = get_user_model().objects.create_user(
            username='testuser',
            email='test@email.com',
            password='secret'
        )

        self.post = Post.objects.create(
            title='A good title',
            body='Nice body content',
            author=self.user,
        )

    def test_string_representation(self):
        post = Post(title='A sample title')
        self.assertEqual(str(post), post.title)

    def test_post_content(self):
        self.assertEqual(f'{self.post.title}', 'A good title')
        self.assertEqual(f'{self.post.author}', 'testuser')
        self.assertEqual(f'{self.post.body}', 'Nice body content')

    def test_post_list_view(self):
        response = self.client.get(reverse('home'))
        self.assertEqual(response.status_code, 200)
        self.assertContains(response, 'Nice body content')
        self.assertTemplateUsed(response, 'home.html')

    def test_post_detail_view(self):
        response = self.client.get('/post/1/')
        no_response = self.client.get('/post/100000/')
        self.assertEqual(response.status_code, 200)
        self.assertEqual(no_response.status_code, 404)
        self.assertContains(response, 'A good title')
        self.assertTemplateUsed(response, 'post_detail.html')


There’s a lot that’s new in these tests so we’ll walk through them slowly. At the top we import get_user_model to reference our active "User". We import TestCase which we’ve seen before and also Client() which is new and used as a dummy Web browser for simulating GET and POST requests on a URL. In other words, whenever you’re testing views you should use Client() .


In our "setUp" method we add a sample blog post to test and then confirm that both its string representation and content are correct. Then we use test_post_list_view to confirm that our homepage returns a 200 HTTP status code, contains our body text, and uses the correct home.html template.

Finally test_post_detail_view tests that our detail page works as expected and that an incorrect page returns a 404. It’s always good to both test that something does exist and that something incorrect doesn’t exist in your tests.


Go ahead and run these tests now. They should all pass.

Command Line

(testy) $ python manage.py test


Git (Blog app)

Now is also a good time for our first 'git' commit. First initialize our directory.

Command Line

(testy) $ git init


Then review all the content we’ve added by checking the "status". Add all new files. And make our first "commit".

Command Line

(testy) $ git status
(testy) $ git add -A
(testy) $ git commit -m 'initial commit'


Conclusion (Blog app)

We’ve now built a basic blog application from scratch! Using the Django admin we can create, edit, or delete the content. And we used DetailView for the first time to create a detailed individual view of each blog post entry.


In the next section Chapter 6: Blog app with forms, we’ll add forms so we don’t have to use the Django admin at all for these changes.

See also





References

  1. cite web |url=https://code.visualstudio.com/docs/supporting/faq#_how-to-disable-telemetry-reporting |title=Visual Studio Code FAQ |website=code.visualstudio.com |publisher=Microsoft |access-date=4 November 2016 |archive-url=https://web.archive.org/web/20160828111301/https://code.visualstudio.com/docs/supporting/faq |archive-date=28 August 2016 |dead-url=no |quote=VS Code collects usage data and sends it to Microsoft to help improve our products and services. Read our privacy statement to learn more. If you don’t wish to send usage data to Microsoft, you can set the telemetry.enableTelemetry setting to false.
  2. cite web |url=https://privacy.microsoft.com/en-us/privacystatement |title=Microsoft Enterprise and Developer Privacy Statement |website=privacy.microsoft.com |publisher=Microsoft |access-date=4 November 2016 |archive-url=https://web.archive.org/web/20161105141442/https://www.microsoft.com/en-us/privacystatement/EnterpriseDev/default.aspx |archive-date=4 November 2016 |dead-url=no
  3. https://github.com/Microsoft/vscode/tree/master/src/vs/platform/telemetry
  4. Citation|title=binary releases of VS Code without MS branding/telemetry/licensing: VSCodium/vscodium|date=2019-03-17|url=https://github.com/VSCodium/vscodium%7Cpublisher=VSCodium%7Caccess-date=2019-03-18

External links

Build websites with Python & Django

William S. Vincent

https://djangoforbeginners.com


  • William S. Vincent - Django for Beginners (Learn web development with Django 2.0).pdf

http://dropperibhaerr2m.onion/file/arg/2buhm39iqt5w03kz8vfaspvc003lgwqxgcba4001mbbvjh58znk02cnt74kfdaf40


http://dl.free.fr/g9iGrYtAX

https://www.solidfiles.com/v/8pNWGrP3YWG2M


http://bcloud2suoza3ybr.onion/d1/URI:CHK:v254pjdknmgvbfuiuarsmkpu3y:iqkgqe3kiybynv2h5dtelda2pcuuxmil5acd3u3kyjaiklgfmgxa:3:10:18399863/William_S._Vincent_-_Django_for_Beginners__Learn_web_development_with_Django_2.0_.pdf


https://uptobox.com/lkw9sbw2o9x8

https://1fichier.com/?cxj6ggctehs86khyxdoq

https://mx-sh.net/jp41h6rc9hbr/Django_for_Beginners_Learn_web_development_with_Django_2.0.pdf.html


https://www111.zippyshare.com/v/78KCVVXo/file.html

http://upfile.mobi/Vq4YJQ9AShJ