More on this book
Community
Kindle Notes & Highlights
by
Eric Matthes
Read between
September 1, 2017 - March 20, 2018
We’ve created a class called Topic, which inherits from Model—a parent class included in Django that defines the basic functionality of a model. Only two attributes are in the Topic class: text and date_added.
The command makemigrations tells Django to figure out how to modify the database so it can store the data associated with any new models we’ve defined. The output here shows that Django has created a migration file called 0001_initial.py.
Whenever we want to modify the data that Learning Log manages, we’ll follow these three steps: modify models.py, call makemigrations on learning_logs, and tell Django to migrate the project.
Django includes some models in the admin site automatically, such as User and Group, but the models we create need to be registered manually.
To record what we’ve been learning about chess and rock climbing, we need to define a model for the kinds of entries users can make in their learning logs. Each entry needs to be associated with a particular topic. This relationship is called a many-to-one relationship, meaning many entries can be associated with one topic.
The date_added attribute allows us to present entries in the order they were created and to place a timestamp next to each entry.
We also need to register the Entry model. Here’s what admin.py should look like now:
When you click Save, you’ll be brought back to the main admin page for entries. Here you’ll see the benefit of using text[:50] as the string representation for each entry; it’s much easier to work with multiple entries in the admin interface if you see only the first part of an entry rather than the entire text of each entry.
Now that we’ve entered some data, we can examine that data programmatically through an interactive terminal session. This interactive environment is called the Django shell, and it’s a great environment for testing and troubleshooting your project. Here’s an example of an interactive shell session:
To get data through a foreign key relationship, you use the lowercase name of the related model followed by an underscore and the word set ➊. For example, say you have the models Pizza and Topping, and Topping is related to Pizza through a foreign key. If your object is called my_pizza, representing a single pizza, you can get all of the pizza’s toppings using the code my_pizza.topping_set.all().
The shell is very useful for making sure your code retrieves the data you want it to. If your code works as you expect it to in the shell, you can expect it to work properly in the files you write within your project.
Each time you modify your models, you’ll need to restart the shell to see the effects of those changes.
Usually, making web pages with Django consists of three stages: defining URLs, writing views, and writing templates.
Each URL then maps to a particular view—the view function retrieves and processes the data needed for that page. The view function often calls a template, which builds a page that a browser can read.
Although it may seem a complicated process for creating one page, this separation between URLs, views, and templates actually works well. It allows you to think about each aspect of a project separately, and in larger projects it allows individuals to focus on the areas in which they’re strongest. For example, a database specialist can focus on the models, a programmer can focus on the view code, and a web designer can focus on the templates.
When building a website, you’ll almost always require some elements to be repeated on each page. Rather than writing these elements directly into each page, you can write a base template containing the repeated elements and then have each page inherit from the template.
The only element we want to repeat on each page right now is the title at the top. Because we’ll include this template on every page, let’s make the title a link to the home page
Having the template tag generate the URL for us makes it much easier to keep our links up to date. To change a URL in our project, we only need to change the URL pattern in urls.py, and Django will automatically insert the updated URL the next time the page is requested.
A child template doesn’t have to define every block from its parent, so you can reserve space in parent templates for as many blocks as you like, and the child template uses only as many as it requires.
You can start to see the benefit of template inheritance: in a child template we only need to include content that’s unique to that page. This not only simplifies each template, but also makes it much easier to modify the site. To modify an element common to many pages, you only need to modify the element in the parent template.
In a large project, it’s common to have one parent template called base.html for the entire site and parent templates for each major section of the site. All the section templates inherit from base.html, and each page in the site inherits from a section template. This way you can easily modify the look and feel of the site as a whole, any section in the site, or any individual page. This configuration provides a very efficient way to work, and it encourages you to steadily update your site over time.
The code used in templates differs from Python in some important ways. Python uses indentation to indicate which lines of a for statement are part of a loop. In a template, every for loop needs an explicit {% endfor %}
The code phrases at ➋ and ➌ are called queries, because they query the database for specific information. When you’re writing queries like these in your own projects, it’s very helpful to try them out in the Django shell first. You’ll get much quicker feedback in the shell than you will by writing a view and template and then checking the results in a browser.
Next, we show the topic that’s currently being displayed ➊, which is stored in the template variable {{ topic }}. The variable topic is available because it’s included in the context dictionary.
In Django templates, a vertical line (|) represents a template filter—a function that modifies the value in a template variable.
Any page that lets a user enter and submit information on a web page is a form, even if it doesn’t look like one. When users enter information, we need to validate that the information provided is the right kind of data and not anything malicious, such as code to interrupt our server. We then need to process and save valid information to the appropriate place in the database. Django automates much of this work.
The new_topic() function needs to handle two different situations: initial requests for the new_topic page (in which case it should show a blank form) and the processing of any data submitted in the form. It then needs to redirect the user back to the topics page:
The two main types of request you’ll use when building web apps are GET requests and POST requests. You use GET requests for pages that only read data from the server. You usually use POST requests when the user needs to submit information through a form. We’ll be specifying the POST method for processing all of our forms. (A few other kinds of requests exist, but we won’t be using them in this project.)
The URL for the page needs to pass the ID of the entry to be edited.
Because we’re not writing our own view function, we pass a dictionary telling Django where to find the template we’re about to write. This template will be part of the users app, not the learning_logs app.
Note that a template in one app can extend a template from another app.
Let’s add the login link to base.html so it appears on every page. We don’t want the link to display when the user is already logged in, so we nest it inside an {% if %} tag:
In Django’s authentication system, every template has a user variable available, which always has an is_authenticated attribute set: the attribute is True if the user is logged in and False if they aren’t. This allows you to display one message to authenticated users and another to unauthenticated users.
Django makes it easy to restrict access to certain pages to logged-in users through the @login_required decorator. A decorator is a directive placed just before a function definition that Python applies to the function before it runs to alter how the function code behaves. Let’s look at an example.
The code in login_required() checks to see if a user is logged in, and Django will run the code in topics() only if they are. If the user is not logged in, they’re redirected to the login page. To make this redirect work, we need to modify settings.py so Django knows where to find the login page. Add the following at the very end of settings.py:
Now we need to connect the data submitted to the user who submitted it. We need to connect only the data highest in the hierarchy to a user, and the lower-level data will follow. For example, in Learning Log, topics are the highest level of data in the app, and all entries are connected to a topic. As long as each topic belongs to a specific user, we’ll be able to trace the ownership of each entry in the database.
When we migrate the database, Django will modify the database so it can store a connection between each topic and a user. To make the migration, Django needs to know which user to associate with each existing topic. The simplest approach is to give all existing topics to one user—for example, the superuser. First, we need to know the ID of that user.
You can simply reset the database instead of migrating, but that will lose all existing data. It’s good practice to learn how to migrate a database while maintaining the integrity of users’ data. If you do want to start with a fresh database, issue the command python manage.py flush to rebuild the database structure. You’ll have to create a new superuser, and all of your data will be gone.
We haven’t actually restricted access to the topic pages yet, so any registered user could try a bunch of URLs, like http://localhost:8000/topics/1/, and retrieve topic pages that happen to match.
We’ll fix this now by performing a check before retrieving the requested entries in the topic() view function:
Currently, our page for adding new topics is broken, because it doesn’t associate new topics with any particular user. If you try adding a new topic, you’ll see the error message IntegrityError along with learning_logs_topic.user_id may not be NULL. Django’s saying you can’t create a new topic without specifying a value for the topic’s owner field.
We’ll deploy Learning Log using Heroku, a site that lets you push your project to one of its servers, making it available to anyone with an Internet connection.
Most apps need to be included in INSTALLED_APPS, but to be sure, read the setup instructions for the particular app you’re using.
Using Bootstrap to Style Learning Log Bootstrap is basically a large collection of styling tools. It also has a number of templates you can apply to your project to create a particular overall style. If you’re just starting out, it’s much easier to use these templates than it is to use individual styling tools.
We need to modify the base.html template to accommodate the Bootstrap template. I’ll introduce the new base.html in parts.
Notice that we’ve removed the {% if form.errors %} block from the template; django-bootstrap3 manages form errors automatically.
We don’t need the {% load bootstrap3 %} tag, because we’re not using any custom bootstrap3 template tags in this file.
Learning Log already depends on six different packages with specific version numbers, so it requires a specific environment to run properly. When we deploy Learning Log, Heroku will install all the packages listed in requirements.txt, creating an environment with the same packages we’re using locally. For this reason, we can be confident the deployed project will behave the same as it does on our local system. This is a huge advantage as you start to build and maintain various projects on your system.
You might want to further develop Learning Log after your initial push to a live server or develop your own projects to deploy. There’s a fairly consistent process for updating projects.

