Django 4 By Example PDF 4th Edition
Document Details
2022
Antonio Melé
Tags
Summary
This book, "Django 4 By Example", is a fourth edition guide dedicated to building Python web applications using the Django framework. It covers the framework's components, architecture, and practical application through real-world projects, including a blog application and a social media website. It includes advanced features like pagination, email recommendations, and comment systems, plus related technologies such as Django REST Framework.
Full Transcript
Django 4 By Example Fourth Edition Build powerful and reliable Python web applications from scratch Antonio Melé BIRMINGHAM—MUMBAI Django 4 By Example Fourth Edition Copyright © 2022 Packt Publishing All rights reserved. No part of this book may be reproduced, stored in a retrieval system, or...
Django 4 By Example Fourth Edition Build powerful and reliable Python web applications from scratch Antonio Melé BIRMINGHAM—MUMBAI Django 4 By Example Fourth Edition Copyright © 2022 Packt Publishing All rights reserved. No part of this book may be reproduced, stored in a retrieval system, or transmitted in any form or by any means, without the prior written permission of the publisher, except in the case of brief quotations embedded in critical articles or reviews. Every effort has been made in the preparation of this book to ensure the accuracy of the information presented. However, the information contained in this book is sold without warranty, either express or implied. Neither the author, nor Packt Publishing or its dealers and distributors, will be held liable for any damages caused or alleged to have been caused directly or indirectly by this book. Packt Publishing has endeavored to provide trademark information about all of the companies and products mentioned in this book by the appropriate use of capitals. However, Packt Publishing cannot guarantee the accuracy of this information. Senior Publishing Product Manager: Manish Nainani Acquisition Editor – Peer Reviews: Suresh Jain Project Editor: Amisha Vathare Content Development Editor: Bhavesh Amin Copy Editor: Safis Editing Technical Editor: Aditya Sawant Proofreader: Safis Editing Indexer: Sejal Dsilva Presentation Designer: Pranit Padwal First published: November 2015 Second edition: May 2018 Third edition: March 2020 Fourth edition: August 2022 Production reference: 2230822 Published by Packt Publishing Ltd. Livery Place 35 Livery Street Birmingham B3 2PB, UK. ISBN 978-1-80181-305-1 www.packt.com To my sister Paloma. Foreword Django: The web framework for perfectionists with deadlines. I like this tagline because it can be easy for developers to fall prey to perfectionism when having to deliver workable code on time. There are many great web frameworks out there, but sometimes they assume too much of the devel- oper, for example, how to properly structure a project, find the right plugins and elegantly use existing abstractions. Django takes most of that decision fatigue away and provides you with so much more. But it’s also a big framework, so learning it from scratch can be overwhelming. I learned Django in 2017, head-on, out of necessity, when we decided it would be our core technology for our Python coding platform (CodeChalleng.es). I forced myself to learn the ins and outs by building a major real-world solution that has served thousands of aspiring and experienced Python developers since its inception. Somewhere in this journey, I picked up an early edition of this book. It turned out to be a treasure trove. Very close to our hearts at Pybites, it teaches you Django by building interesting, real-world applications. Not only that, Antonio brings a lot of real-world experience and knowledge to the table, which shows in how he implements those projects. And Antonio never misses an opportunity to introduce lesser-known features, for example, optimizing database queries with Postgres, useful packages like django-taggit, social auth using various platforms, (model) managers, inclusion template tags, and much more. In this new edition, he even added additional schemas, images, and notes in several chapters and moved from jQuery to vanilla JavaScript (nice!) This book not only covers Django thoroughly, using clean code examples that are well explained, it also explains related technologies which are a must for any Django developer: Django REST Framework, django-debug-toolbar, frontend / JS, and, last but not least, Docker. More importantly, you’ll find many nuances that you’ll encounter and best practices you’ll need to be an effective Django developer in a professional setting. Finding a multifaceted resource like this is hard, and I want to thank Antonio for all the hard work he consistently puts into keeping it up to date. As a Python developer that uses Django a lot, Django by Example has become my GO TO guide, an unmissable resource I want to have close by my desk. Every time I come back to this book, I learn new things, even after having read it multiple times and having used Django for a solid five years now. If you embark on this journey, be prepared to get your hands dirty. It’s a practical guide, so brew yourself a good coffee and expect to sink your teeth into a lot of Django code! But that’s how we best learn, right? :) - Bob Belderbos Co-Founder of Pybites Contributors About the author Antonio Melé is the co-founder and chief technology officer of Nucoro, the fintech platform that allows financial institutions to build, automate, and scale digital wealth management products. Antonio is also CTO of Exo Investing, an AI-driven digital investment platform for the UK market. Antonio has been developing Django projects since 2006 for clients across several industries. In 2009 Antonio founded Zenx IT, a development company specialized in building digital products. He has been working as a CTO and technology consultant for multiple technology-based startups and he has managed development teams building projects for large digital businesses. Antonio holds an MSc. in Computer Science from ICAI - Universidad Pontificia Comillas, where he mentors early-stage startups. His father inspired his passion for computers and programming. About the reviewer Asif Saif Uddin is a software craftsman from Bangladesh. He has a decade-long professional expe- rience working with Python and Django. Besides working for different start-ups and clients, Asif also contributes to some frequently used Python and Django packages. For his open-source contributions, he is now a core maintainer of Celery, oAuthLib, PyJWT, and auditwheel. He is also co-maintainer of several Django and Django REST framework extension packages. He is a voting member of the Django Software Foundation (DSF) and a contributing/managing member of the Python Software Foundation (PSF). He has been mentoring many young people to learn Python and Django, both professionally and personally. A special thanks to Karen Stingel and Ismir Kullolli for reading and providing feedback on the book to enhance the content further. Your help is much appreciated! Table of Contents Preface xxi Chapter 1: Building a Blog Application 1 Installing Python 2 Creating a Python virtual environment 2 Installing Django 3 Installing Django with pip 4 New features in Django 4 4 Django overview 5 Main framework components 5 The Django architecture 6 Creating your first project 7 Applying initial database migrations 8 Running the development server 9 Project settings 10 Projects and applications 11 Creating an application 12 Creating the blog data models 12 Creating the Post model 13 Adding datetime fields 14 Defining a default sort order 15 Adding a database index 16 Activating the application 17 Adding a status field 17 Adding a many-to-one relationship 19 Creating and applying migrations 21 viii Table of Contents Creating an administration site for models 23 Creating a superuser 24 The Django administration site 24 Adding models to the administration site 25 Customizing how models are displayed 27 Working with QuerySets and managers 29 Creating objects 30 Updating objects 31 Retrieving objects 31 Using the filter() method 31 Using exclude() 32 Using order_by() 32 Deleting objects 32 When QuerySets are evaluated 32 Creating model managers 33 Building list and detail views 34 Creating list and detail views 34 Using the get_object_or_404 shortcut 35 Adding URL patterns for your views 36 Creating templates for your views 37 Creating a base template 38 Creating the post list template 39 Accessing our application 40 Creating the post detail template 41 The request/response cycle 42 Additional resources 43 Summary 44 Chapter 2: Enhancing Your Blog with Advanced Features 45 Using canonical URLs for models 45 Creating SEO-friendly URLs for posts 48 Modifying the URL patterns 49 Modifying the views 50 Modifying the canonical URL for posts 50 Adding pagination 51 Adding pagination to the post list view 52 Creating a pagination template 52 Table of Contents ix Handling pagination errors 55 Building class-based views 59 Why use class-based views 59 Using a class-based view to list posts 59 Recommending posts by email 61 Creating forms with Django 62 Handling forms in views 63 Sending emails with Django 64 Sending emails in views 69 Rendering forms in templates 70 Creating a comment system 74 Creating a model for comments 75 Adding comments to the administration site 76 Creating forms from models 78 Handling ModelForms in views 78 Creating templates for the comment form 80 Adding comments to the post detail view 82 Adding comments to the post detail template 83 Additional resources 90 Summary 90 Chapter 3: Extending Your Blog Application 91 Adding the tagging functionality 91 Retrieving posts by similarity 101 Creating custom template tags and filters 106 Implementing custom template tags 106 Creating a simple template tag 107 Creating an inclusion template tag 109 Creating a template tag that returns a QuerySet 110 Implementing custom template filters 113 Creating a template filter to support Markdown syntax 113 Adding a sitemap to the site 118 Creating feeds for blog posts 123 Adding full-text search to the blog 130 Installing PostgreSQL 131 Creating a PostgreSQL database 131 Dumping the existing data 132 x Table of Contents Switching the database in the project 133 Loading the data into the new database 134 Simple search lookups 135 Searching against multiple fields 136 Building a search view 136 Stemming and ranking results 140 Stemming and removing stop words in different languages 142 Weighting queries 142 Searching with trigram similarity 143 Additional resources 144 Summary 145 Chapter 4: Building a Social Website 147 Creating a social website project 148 Starting the social website project 148 Using the Django authentication framework 149 Creating a login view 150 Using Django authentication views 157 Login and logout views 157 Change password views 163 Reset password views 166 User registration and user profiles 174 User registration 174 Extending the user model 182 Installing Pillow and serving media files 183 Creating migrations for the profile model 184 Using a custom user model 190 Using the messages framework 190 Building a custom authentication backend 194 Preventing users from using an existing email 196 Additional resources 197 Summary 198 Chapter 5: Implementing Social Authentication 199 Adding social authentication to your site 200 Running the development server through HTTPS 202 Authentication using Facebook 205 Table of Contents xi Authentication using Twitter 214 Authentication using Google 227 Creating a profile for users that register with social authentication 235 Additional resources 237 Summary 238 Chapter 6: Sharing Content on Your Website 239 Creating an image bookmarking website 239 Building the image model 240 Creating many-to-many relationships 242 Registering the image model in the administration site 243 Posting content from other websites 243 Cleaning form fields 244 Installing the Requests library 245 Overriding the save() method of a ModelForm 245 Building a bookmarklet with JavaScript 250 Creating a detail view for images 263 Creating image thumbnails using easy-thumbnails 265 Adding asynchronous actions with JavaScript 268 Loading JavaScript on the DOM 269 Cross-site request forgery for HTTP requests in JavaScript 270 Performing HTTP requests with JavaScript 272 Adding infinite scroll pagination to the image list 278 Additional resources 285 Summary 286 Chapter 7: Tracking User Actions 287 Building a follow system 287 Creating many-to-many relationships with an intermediary model 288 Creating list and detail views for user profiles 291 Adding user follow/unfollow actions with JavaScript 296 Building a generic activity stream application 298 Using the contenttypes framework 300 Adding generic relations to your models 301 Avoiding duplicate actions in the activity stream 304 Adding user actions to the activity stream 305 Displaying the activity stream 307 xii Table of Contents Optimizing QuerySets that involve related objects 308 Using select_related() 308 Using prefetch_related() 309 Creating templates for actions 310 Using signals for denormalizing counts 312 Working with signals 313 Application configuration classes 315 Using Django Debug Toolbar 317 Installing Django Debug Toolbar 317 Django Debug Toolbar panels 320 Django Debug Toolbar commands 322 Counting image views with Redis 323 Installing Docker 324 Installing Redis 324 Using Redis with Python 326 Storing image views in Redis 326 Storing a ranking in Redis 329 Next steps with Redis 331 Additional resources 332 Summary 332 Chapter 8: Building an Online Shop 333 Creating an online shop project 334 Creating product catalog models 335 Registering catalog models on the administration site 339 Building catalog views 341 Creating catalog templates 344 Building a shopping cart 349 Using Django sessions 349 Session settings 350 Session expiration 351 Storing shopping carts in sessions 351 Creating shopping cart views 355 Adding items to the cart 355 Building a template to display the cart 358 Adding products to the cart 359 Updating product quantities in the cart 361 Table of Contents xiii Creating a context processor for the current cart 362 Context processors 362 Setting the cart into the request context 363 Registering customer orders 365 Creating order models 366 Including order models in the administration site 367 Creating customer orders 369 Asynchronous tasks 374 Working with asynchronous tasks 374 Workers, message queues, and message brokers 374 Using Django with Celery and RabbitMQ 375 Monitoring Celery with Flower 383 Additional resources 385 Summary 385 Chapter 9: Managing Payments and Orders 387 Integrating a payment gateway 387 Creating a Stripe account 388 Installing the Stripe Python library 391 Adding Stripe to your project 392 Building the payment process 393 Integrating Stripe Checkout 395 Testing the checkout process 402 Using test credit cards 404 Checking the payment information in the Stripe dashboard 408 Using webhooks to receive payment notifications 412 Creating a webhook endpoint 412 Testing webhook notifications 417 Referencing Stripe payments in orders 421 Going live 424 Exporting orders to CSV files 424 Adding custom actions to the administration site 425 Extending the administration site with custom views 427 Generating PDF invoices dynamically 432 Installing WeasyPrint 433 Creating a PDF template 433 Rendering PDF files 434 xiv Table of Contents Sending PDF files by email 438 Additional resources 441 Summary 442 Chapter 10: Extending Your Shop 443 Creating a coupon system 443 Building the coupon model 444 Applying a coupon to the shopping cart 448 Applying coupons to orders 456 Creating coupons for Stripe Checkout 461 Adding coupons to orders on the administration site and to PDF invoices 464 Building a recommendation engine 467 Recommending products based on previous purchases 468 Additional resources 476 Summary 476 Chapter 11: Adding Internationalization to Your Shop 477 Internationalization with Django 478 Internationalization and localization settings 478 Internationalization management commands 479 Installing the gettext toolkit 479 How to add translations to a Django project 479 How Django determines the current language 479 Preparing your project for internationalization 480 Translating Python code 481 Standard translations 481 Lazy translations 481 Translations including variables 482 Plural forms in translations 482 Translating your own code 482 Translating templates 486 The {% trans %} template tag 486 The {% blocktrans %} template tag 487 Translating the shop templates 487 Using the Rosetta translation interface 491 Fuzzy translations 494 Table of Contents xv URL patterns for internationalization 494 Adding a language prefix to URL patterns 494 Translating URL patterns 495 Allowing users to switch language 499 Translating models with django-parler 501 Installing django-parler 501 Translating model fields 501 Integrating translations into the administration site 504 Creating migrations for model translations 505 Using translations with the ORM 508 Adapting views for translations 508 Format localization 511 Using django-localflavor to validate form fields 512 Additional resources 514 Summary 514 Chapter 12: Building an E-Learning Platform 515 Setting up the e-learning project 515 Serving media files 516 Building the course models 517 Registering the models in the administration site 520 Using fixtures to provide initial data for models 520 Creating models for polymorphic content 524 Using model inheritance 525 Abstract models 525 Multi-table model inheritance 526 Proxy models 526 Creating the Content models 527 Creating custom model fields 529 Adding ordering to module and content objects 531 Adding authentication views 535 Adding an authentication system 535 Creating the authentication templates 536 Additional resources 539 Summary 539 xvi Table of Contents Chapter 13: Creating a Content Management System 541 Creating a CMS 541 Creating class-based views 542 Using mixins for class-based views 542 Working with groups and permissions 544 Restricting access to class-based views 546 Managing course modules and their contents 553 Using formsets for course modules 553 Adding content to course modules 557 Managing modules and their contents 562 Reordering modules and their contents 568 Using mixins from django-braces 569 Additional resources 577 Summary 578 Chapter 14: Rendering and Caching Content 579 Displaying courses 580 Adding student registration 585 Creating a student registration view 585 Enrolling on courses 589 Accessing the course contents 592 Rendering different types of content 596 Using the cache framework 598 Available cache backends 599 Installing Memcached 599 Installing the Memcached Docker image 600 Installing the Memcached Python binding 600 Django cache settings 600 Adding Memcached to your project 601 Cache levels 601 Using the low-level cache API 601 Checking cache requests with Django Debug Toolbar 603 Caching based on dynamic data 606 Caching template fragments 607 Caching views 608 Using the per-site cache 609 Table of Contents xvii Using the Redis cache backend 610 Monitoring Redis with Django Redisboard 611 Additional resources 613 Summary 613 Chapter 15: Building an API 615 Building a RESTful API 616 Installing Django REST framework 616 Defining serializers 617 Understanding parsers and renderers 618 Building list and detail views 619 Consuming the API 620 Creating nested serializers 622 Building custom API views 624 Handling authentication 625 Adding permissions to views 626 Creating ViewSets and routers 627 Adding additional actions to ViewSets 629 Creating custom permissions 630 Serializing course contents 631 Consuming the RESTful API 633 Additional resources 636 Summary 637 Chapter 16: Building a Chat Server 639 Creating a chat application 639 Implementing the chat room view 640 Real-time Django with Channels 643 Asynchronous applications using ASGI 643 The request/response cycle using Channels 644 Installing Channels 646 Writing a consumer 648 Routing 649 Implementing the WebSocket client 650 Enabling a channel layer 656 Channels and groups 657 Setting up a channel layer with Redis 657 xviii Table of Contents Updating the consumer to broadcast messages 658 Adding context to the messages 662 Modifying the consumer to be fully asynchronous 666 Integrating the chat application with existing views 668 Additional resources 670 Summary 670 Chapter 17: Going Live 671 Creating a production environment 672 Managing settings for multiple environments 672 Local environment settings 673 Running the local environment 673 Production environment settings 674 Using Docker Compose 675 Installing Docker Compose 675 Creating a Dockerfile 676 Adding the Python requirements 677 Creating a Docker Compose file 678 Configuring the PostgreSQL service 681 Applying database migrations and creating a superuser 684 Configuring the Redis service 684 Serving Django through WSGI and NGINX 686 Using uWSGI 686 Configuring uWSGI 687 Using NGINX 688 Configuring NGINX 690 Using a hostname 692 Serving static and media assets 692 Collecting static files 693 Serving static files with NGINX 693 Securing your site with SSL/TLS 695 Checking your project for production 695 Configuring your Django project for SSL/TLS 696 Creating an SSL/TLS certificate 697 Configuring NGINX to use SSL/TLS 698 Redirecting HTTP traffic over to HTTPS 701 Table of Contents xix Using Daphne for Django Channels 702 Using secure connections for WebSockets 703 Including Daphne in the NGINX configuration 704 Creating a custom middleware 707 Creating a subdomain middleware 708 Serving multiple subdomains with NGINX 709 Implementing custom management commands 710 Additional resources 712 Summary 713 Other Books You May Enjoy 717 Index 721 Preface Django is an open-source Python web framework that encourages rapid development and clean, prag- matic design. It takes care of much of the hassle of web development and presents a relatively shallow learning curve for beginner programmers. Django follows Python’s “batteries included” philosophy, shipping with a rich and versatile set of modules that solve common web-development problems. The simplicity of Django, together with its powerful features, makes it attractive to both novice and expert programmers. Django has been designed for simplicity, flexibility, reliability, and scalability. Nowadays, Django is used by countless start-ups and large organizations such as Instagram, Spotify, Pinterest, Udemy, Robinhood, and Coursera. It is not by coincidence that, over the last few years, Django has consistently been chosen by developers worldwide as one of the most loved web frameworks in Stack Overflow’s annual developer survey. This book will guide you through the entire process of developing professional web applications with Django. The book focuses on explaining how the Django web framework works by building multiple projects from the ground up. This book not only covers the most relevant aspects of the framework but also explains how to apply Django to very diverse real-world situations. This book not only teaches Django but also presents other popular technologies like PostgreSQL, Redis, Celery, RabbitMQ, and Memcached. You will learn how to integrate these technologies into your Django projects throughout the book to create advanced functionalities and build complex web applications. Django 4 By Example will walk you through the creation of real-world applications, solving common problems, and implementing best practices, using a step-by-step approach that is easy to follow. After reading this book, you will have a good understanding of how Django works and how to build full-fledged Python web applications. Who this book is for This book should serve as a primer for programmers newly initiated to Django. The book is intended for developers with Python knowledge who wish to learn Django in a pragmatic manner. Perhaps you are completely new to Django, or you already know a little but you want to get the most out of it. This book will help you to master the most relevant areas of the framework by building practical projects from scratch. You need to have familiarity with programming concepts in order to read this book. In addition to basic Python knowledge, some previous knowledge of HTML and JavaScript is assumed. xxii Preface What this book covers This book encompasses a range of topics of web application development with Django. The book will guide you through building four different fully-featured web applications, built over the course of 17 chapters: A blog application (chapters 1 to 3) An image bookmarking website (chapters 4 to 7) An online shop (chapters 8 to 11) An e-learning platform (chapters 12 to 17) Each chapter covers several Django features: Chapter 1, Building a Blog Application, will introduce you to the framework through a blog application. You will create the basic blog models, views, templates, and URLs to display blog posts. You will learn how to build QuerySets with the Django object-relational mapper (ORM), and you will configure the Django administration site. Chapter 2, Enhancing Your Blog with Advanced Features, will teach you how to add pagination to your blog, and how to implement Django class-based views. You will learn to send emails with Django, and handle forms and model forms. You will also implement a comment system for blog posts. Chapter 3, Extending Your Blog Application, explores how to integrate third-party applications. This chapter will guide you through the process of creating a tagging system, and you will learn how to build complex QuerySets to recommend similar posts. The chapter will teach you how to create cus- tom template tags and filters. You will also learn how to use the sitemap framework and create an RSS feed for your posts. You will complete your blog application by building a search engine using PostgreSQL’s full-text search capabilities. Chapter 4, Building a Social Website, explains how to build a social website. You will learn how to use the Django authentication framework, and you will extend the user model with a custom profile model. The chapter will teach you how to use the messages framework and you will build a custom authentication backend. Chapter 5, Implementing Social Authentication, covers implementing social authentication with Goo- gle, Facebook, and Twitter using OAuth 2 with Python Social Auth. You will learn how to use Django Extensions to run the development server through HTTPS and customize the social authentication pipeline to automate the user profile creation. Chapter 6, Sharing Content on Your Website, will teach you how to transform your social application into an image bookmarking website. You will define many-to-many relationships for models, and you will create a JavaScript bookmarklet that integrates into your project. The chapter will show you how to generate image thumbnails. You will also learn how to implement asynchronous HTTP requests using JavaScript and Django and you will implement infinite scroll pagination. Preface xxiii Chapter 7, Tracking User Actions, will show you how to build a follower system for users. You will com- plete your image bookmarking website by creating a user activity stream application. You will learn how to create generic relations between models and optimize QuerySets. You will work with signals and implement denormalization. You will use Django Debug Toolbar to obtain relevant debug infor- mation. Finally, you will integrate Redis into your project to count image views and you will create a ranking of the most viewed images with Redis. Chapter 8, Building an Online Shop, explores how to create an online shop. You will build models for a product catalog, and you will create a shopping cart using Django sessions. You will build a context processor for the shopping cart and will learn how to manage customer orders. The chapter will teach you how to send asynchronous notifications using Celery and RabbitMQ. You will also learn to monitor Celery using Flower. Chapter 9, Managing Payments and Orders, explains how to integrate a payment gateway into your shop. You will integrate Stripe Checkout and receive asynchronous payment notifications in your application. You will implement custom views in the administration site and you will also customize the adminis- tration site to export orders to CSV files. You will also learn how to generate PDF invoices dynamically. Chapter 10, Extending Your Shop, will teach you how to create a coupon system to apply discounts to the shopping cart. You will update the Stripe Checkout integration to implement coupon discounts and you will apply coupons to orders. You will use Redis to store products that are usually bought together, and use this information to build a product recommendation engine. Chapter 11, Adding Internationalization to Your Shop, will show you how to add internationalization to your project. You will learn how to generate and manage translation files and translate strings in Python code and Django templates. You will use Rosetta to manage translations and implement per-language URLs. You will learn how to translate model fields using django-parler and how to use translations with the ORM. Finally, you will create a localized form field using django-localflavor. Chapter 12, Building an E-Learning Platform, will guide you through creating an e-learning platform. You will add fixtures to your project, and create initial models for the content management system. You will use model inheritance to create data models for polymorphic content. You will learn how to create custom model fields by building a field to order objects. You will also implement authentication views for the CMS. Chapter 13, Creating a Content Management System, will teach you how to create a CMS using class- based views and mixins. You will use the Django groups and permissions system to restrict access to views and implement formsets to edit the content of courses. You will also create a drag-and-drop functionality to reorder course modules and their content using JavaScript and Django. Chapter 14, Rendering and Caching Content, will show you how to implement the public views for the course catalog. You will create a student registration system and manage student enrollment on courses. You will create the functionality to render different types of content for the course modules. You will learn how to cache content using the Django cache framework and configure the Memcached and Redis cache backends for your project. Finally, you will learn how to monitor Redis using the administration site. xxiv Preface Chapter 15, Building an API, explores building a RESTful API for your project using Django REST framework. You will learn how to create serializers for your models and create custom API views. You will handle API authentication and implement permissions for API views. You will learn how to build API viewsets and routers. The chapter will also teach you how to consume your API using the Requests library. Chapter 16, Building a Chat Server, explains how to use Django Channels to create a real-time chat server for students. You will learn how to implement functionalities that rely on asynchronous com- munication through WebSockets. You will create a WebSocket consumer with Python and implement a WebSocket client with JavaScript. You will use Redis to set up a channel layer and you will learn how to make your WebSocket consumer fully asynchronous. Chapter 17, Going Live, will show you how to create settings for multiple environments and how to set up a production environment using PostgreSQL, Redis, uWSGI, NGINX, and Daphne with Docker Compose. You will learn how to serve your project securely through HTTPS and use the Django sys- tem check framework. The chapter will also teach you how to build a custom middleware and create custom management commands. To get the most out of this book The reader must possess a good working knowledge of Python. The reader should be comfortable with HTML and JavaScript. It is recommended that the reader goes through parts 1 to 3 of the tutorial in the official Django documentation at https://docs.djangoproject.com/en/4.1/intro/tutorial01/. Download the example code files The code bundle for the book is hosted on GitHub at https://github.com/PacktPublishing/Django- 4-by-example. We also have other code bundles from our rich catalog of books and videos available at https://github.com/PacktPublishing/. Check them out! Download the color images We also provide a PDF file that has color images of the screenshots/diagrams used in this book. You can download it here: https://static.packt-cdn.com/downloads/9781801813051_ColorImages.pdf. Conventions used There are a number of text conventions used throughout this book. CodeInText: Indicates code words in text, database table names, folder names, filenames, file exten- sions, pathnames, dummy URLs, user input, and Twitter handles. For example: “Edit the models.py file of the shop application.” Preface xxv A block of code is set as follows: from django.contrib import admin from.models import Post admin.site.register(Post) When we wish to draw your attention to a particular part of a code block, the relevant lines or items are set in bold: INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'blog.apps.BlogConfig', ] Any command-line input or output is written as follows: python manage.py runserver Bold: Indicates a new term, an important word, or words that you see on the screen. For instance, words in menus or dialog boxes appear in the text like this. For example: “Fill in the form and click the Save button.” Warnings or important notes appear like this. Tips and tricks appear like this. xxvi Preface Get in touch Feedback from our readers is always welcome. General feedback: Email [email protected] and mention the book’s title in the subject of your message. If you have questions about any aspect of this book, please email us at questions@packtpub. com. Errata: Although we have taken every care to ensure the accuracy of our content, mistakes do happen. If you have found a mistake in this book, we would be grateful if you reported this to us. Please visit http://www.packtpub.com/submit-errata, click Submit Errata, and fill in the form. Piracy: If you come across any illegal copies of our works in any form on the internet, we would be grateful if you would provide us with the location address or website name. Please contact us at [email protected] with a link to the material. If you are interested in becoming an author: If there is a topic that you have expertise in and you are interested in either writing or contributing to a book, please visit http://authors.packtpub.com. Share your thoughts Once you’ve read Django 4 By Example, Fourth Edition, we’d love to hear your thoughts! Please click here to go straight to the Amazon review page for this book and share your feedback. Your review is important to us and the tech community and will help us make sure we’re delivering excellent quality content. 1 Building a Blog Application In this book, you will learn how to build professional Django projects. This chapter will teach you how to build a Django application using the main components of the framework. If you haven’t installed Django yet, you will discover how to do so in the first part of this chapter. Before starting our first Django project, let’s take a moment to see what you will learn. This chapter will give you a general overview of the framework. The chapter will guide you through the different major components to create a fully functional web application: models, templates, views, and URLs. After reading it, you will have a good understanding of how Django works and how the different framework components interact. In this chapter, you will learn the difference between Django projects and applications, and you will learn the most important Django settings. You will build a simple blog application that allows users to navigate through all published posts and read single posts. You will also create a simple administration interface to manage and publish posts. In the next two chapters, you will extend the blog application with more advanced functionalities. This chapter should serve as a guide to build a complete Django application and shall provide an insight into how the framework works. Don’t be concerned if you don’t understand all the aspects of the framework. The different framework components will be explored in detail throughout this book. This chapter will cover the following topics: Installing Python Creating a Python virtual environment Installing Django Creating and configuring a Django project Building a Django application Designing data models Creating and applying model migrations Creating an administration site for your models 2 Building a Blog Application Working with QuerySets and model managers Building views, templates, and URLs Understanding the Django request/response cycle Installing Python Django 4.1 supports Python 3.8, 3.9, and 3.10. In the examples in this book, we will use Python 3.10.6. If you’re using Linux or macOS, you probably have Python installed. If you’re using Windows, you can download a Python installer from https://www.python.org/downloads/windows/. Open the command-line shell prompt of your machine. If you are using macOS, open the /Applications/ Utilities directory in the Finder, then double-click Terminal. If you are using Windows, open the Start menu and type cmd into the search box. Then click on the Command Prompt application to open it. Verify that Python is installed on your machine by typing the following command in the shell prompt: python If you see something like the following, then Python is installed on your computer: Python 3.10.6 (v3.10.6:9c7b4bd164, Aug 1 2022, 17:13:48) [Clang 13.0.0 (clang-1300.0.29.30)] on darwin Type "help", "copyright", "credits" or "license" for more information. If your installed Python version is lower than 3.10, or if Python is not installed on your computer, download Python 3.10.6 from https://www.python.org/downloads/ and follow the instructions to install it. On the download site, you can find Python installers for Windows, macOS, and Linux. Throughout this book, when Python is referenced in the shell prompt, we will be using python, though some systems may require using python3. If you are using Linux or macOS and your system’s Python is Python 2 you will need to use python3 to use the Python 3 version you installed. In Windows, python is the Python executable of your default Python installation, whereas py is the Python launcher. The Python launcher for Windows was introduced in Python 3.3. It detects what Python versions are installed on your machine and it automatically delegates to the latest version. If you use Windows, it’s recommended that you replace python with the py command. You can read more about the Windows Python launcher at https://docs.python.org/3/using/windows.html#launcher. Creating a Python virtual environment When you write Python applications, you will usually use packages and modules that are not includ- ed in the standard Python library. You may have Python applications that require a different version of the same module. However, only a specific version of a module can be installed system-wide. If you upgrade a module version for an application, you might end up breaking other applications that require an older version of that module. Chapter 1 3 To address this issue, you can use Python virtual environments. With virtual environments, you can install Python modules in an isolated location rather than installing them globally. Each virtual envi- ronment has its own Python binary and can have its own independent set of installed Python packages in its site directories. Since version 3.3, Python comes with the venv library, which provides support for creating lightweight virtual environments. By using the Python venv module to create isolated Python environments, you can use different package versions for different projects. Another advantage of using venv is that you won’t need any administration privileges to install Python packages. If you are using Linux or macOS, create an isolated environment with the following command: python -m venv my_env Remember to use python3 instead of python if your system comes with Python 2 and you installed Python 3. If you are using Windows, use the following command instead: py -m venv my_env This will use the Python launcher in Windows. The previous command will create a Python environment in a new directory named my_env/. Any Python libraries you install while your virtual environment is active will go into the my_env/lib/ python3.10/site-packages directory. If you are using Linux or macOS, run the following command to activate your virtual environment: source my_env/bin/activate If you are using Windows, use the following command instead:.\my_env\Scripts\activate The shell prompt will include the name of the active virtual environment enclosed in parentheses like this: (my_env) zenx@pc:~ zenx$ You can deactivate your environment at any time with the deactivate command. You can find more information about venv at https://docs.python.org/3/library/venv.html. Installing Django If you have already installed Django 4.1, you can skip this section and jump directly to the Creating your first project section. Django comes as a Python module and thus can be installed in any Python environment. If you haven’t installed Django yet, the following is a quick guide to installing it on your machine. 4 Building a Blog Application Installing Django with pip The pip package management system is the preferred method of installing Django. Python 3.10 comes with pip preinstalled, but you can find pip installation instructions at https://pip.pypa.io/en/ stable/installing/. Run the following command at the shell prompt to install Django with pip: pip install Django~=4.1.0 This will install Django’s latest 4.1 version in the Python site-packages/ directory of your virtual environment. Now we will check whether Django has been successfully installed. Run the following command in a shell prompt: python -m django --version If you get the output 4.1.X, Django has been successfully installed on your machine. If you get the message No module named Django, Django is not installed on your machine. If you have issues installing Django, you can review the different installation options described in https://docs.djangoproject. com/en/4.1/intro/install/. Django can be installed in different ways. You can find the different installation options at https://docs.djangoproject.com/en/4.1/topics/install/. All Python packages used in this chapter are included in the requirements.txt file in the source code for the chapter. You can follow the instructions to install each Python package in the following sections, or you can install all requirements at once with the command pip install -r requirements.txt. New features in Django 4 Django 4 introduces a collection of new features, including some backward-incompatible changes, while deprecating other features and eliminating old functionalities. Being a time-based release, there is no drastic change in Django 4, and it is easy to migrate Django 3 applications to the 4.1 release. While Django 3 included for the first time Asynchronous Server Gateway Interface (ASGI) support, Django 4.0 adds several features such as functional unique constraints for Django models, built-in support for caching data with Redis, a new default timezone implementation using the standard Python package zoneinfo, a new scrypt password hasher, template-based rendering for forms, as well as other new minor features. Django 4.0 drops support for Python 3.6 and 3.7. It also drops support for PostgreSQL 9.6, Oracle 12.2, and Oracle 18c. Django 4.1 introduces asynchronous handlers for class-based views, an asynchronous ORM interface, new validation of model constraints and new templates for rendering forms. The 4.1 version drops support for PostgreSQL 10 and MariaDB 10.2. Chapter 1 5 You can read the complete list of changes in the Django 4.0 release notes at https://docs.djangoproject. com/en/dev/releases/4.0/ and the Django 4.1 release notes at https://docs.djangoproject.com/ en/4.1/releases/4.1/. Django overview Django is a framework consisting of a set of components that solve common web development problems. Django components are loosely coupled, which means they can be managed independently. This helps separate the responsibilities of the different layers of the framework; the database layer knows nothing about how the data is displayed, the template system knows nothing about web requests, and so on. Django offers maximum code reusability by following the DRY (don’t repeat yourself) principle. Djan- go also fosters rapid development and allows you to use less code by taking advantage of Python’s dynamic capabilities, such as introspection. You can read more about Django’s design philosophies at https://docs.djangoproject.com/en/4.1/ misc/design-philosophies/. Main framework components Django follows the MTV (Model-Template-View) pattern. It is a slightly similar pattern to the well- known MVC (Model-View-Controller) pattern, where the Template acts as View and the framework itself acts as the Controller. The responsibilities in the Django MTV pattern are divided as follows: Model – Defines the logical data structure and is the data handler between the database and the View. Template – Is the presentation layer. Django uses a plain-text template system that keeps everything that the browser renders. View – Communicates with the database via the Model and transfers the data to the Template for viewing. The framework itself acts as the Controller. It sends a request to the appropriate view, according to the Django URL configuration. When developing any Django project, you will always work with models, views, templates, and URLs. In this chapter, you will learn how they fit together. 6 Building a Blog Application The Django architecture Figure 1.1 shows how Django processes requests and how the request/response cycle is managed with the different main Django components: URLs, views, models, and templates: Figure 1.1: The Django architecture This is how Django handles HTTP requests and generates responses: 1. A web browser requests a page by its URL and the web server passes the HTTP request to Django. 2. Django runs through its configured URL patterns and stops at the first one that matches the requested URL. 3. Django executes the view that corresponds to the matched URL pattern. 4. The view potentially uses data models to retrieve information from the database. 5. Data models provide the data definition and behaviors. They are used to query the database. 6. The view renders a template (usually HTML) to display the data and returns it with an HTTP response. We will get back to the Django request/response cycle at the end of this chapter in the The request/ response cycle section. Django also includes hooks in the request/response process, which are called middleware. Middleware has been intentionally left out of this diagram for the sake of simplicity. You will use middleware in different examples of this book, and you will learn how to create custom middleware in Chapter 17, Going Live. Chapter 1 7 Creating your first project Your first Django project will consist of a blog application. We will start by creating the Django project and a Django application for the blog. We will then create our data models and synchronize them to the database. Django provides a command that allows you to create an initial project file structure. Run the following command in your shell prompt: django-admin startproject mysite This will create a Django project with the name mysite. Avoid naming projects after built-in Python or Django modules in order to avoid conflicts. Let’s take a look at the generated project structure: mysite/ manage.py mysite/ __init__.py asgi.py settings.py urls.py wsgi.py The outer mysite/ directory is the container for our project. It contains the following files: manage.py: This is a command-line utility used to interact with your project. You don’t need to edit this file. mysite/: This is the Python package for your project, which consists of the following files: __init__.py: An empty file that tells Python to treat the mysite directory as a Python module. asgi.py: This is the configuration to run your project as an Asynchronous Server Gateway Interface (ASGI) application with ASGI-compatible web servers. ASGI is the emerging Python standard for asynchronous web servers and applications. settings.py: This indicates settings and configuration for your project and contains initial default settings. urls.py: This is the place where your URL patterns live. Each URL defined here is mapped to a view. wsgi.py: This is the configuration to run your project as a Web Server Gateway Inter- face (WSGI) application with WSGI-compatible web servers. 8 Building a Blog Application Applying initial database migrations Django applications require a database to store data. The settings.py file contains the database con- figuration for your project in the DATABASES setting. The default configuration is an SQLite3 database. SQLite comes bundled with Python 3 and can be used in any of your Python applications. SQLite is a lightweight database that you can use with Django for development. If you plan to deploy your ap- plication in a production environment, you should use a full-featured database, such as PostgreSQL, MySQL, or Oracle. You can find more information about how to get your database running with Django at https://docs.djangoproject.com/en/4.1/topics/install/#database-installation. Your settings.py file also includes a list named INSTALLED_APPS that contains common Django applications that are added to your project by default. We will go through these applications later in the Project settings section. Django applications contain data models that are mapped to database tables. You will create your own models in the Creating the blog data models section. To complete the project setup, you need to create the tables associated with the models of the default Django applications included in the INSTALLED_APPS setting. Django comes with a system that helps you manage database migrations. Open the shell prompt and run the following commands: cd mysite python manage.py migrate You will see an output that ends with the following lines: Applying contenttypes.0001_initial... OK Applying auth.0001_initial... OK Applying admin.0001_initial... OK Applying admin.0002_logentry_remove_auto_add... OK Applying admin.0003_logentry_add_action_flag_choices... OK Applying contenttypes.0002_remove_content_type_name... OK Applying auth.0002_alter_permission_name_max_length... OK Applying auth.0003_alter_user_email_max_length... OK Applying auth.0004_alter_user_username_opts... OK Applying auth.0005_alter_user_last_login_null... OK Applying auth.0006_require_contenttypes_0002... OK Applying auth.0007_alter_validators_add_error_messages... OK Applying auth.0008_alter_user_username_max_length... OK Applying auth.0009_alter_user_last_name_max_length... OK Applying auth.0010_alter_group_name_max_length... OK Applying auth.0011_update_proxy_permissions... OK Applying auth.0012_alter_user_first_name_max_length... OK Applying sessions.0001_initial... OK Chapter 1 9 The preceding lines are the database migrations that are applied by Django. By applying the initial mi- grations, the tables for the applications listed in the INSTALLED_APPS setting are created in the database. You will learn more about the migrate management command in the Creating and applying migrations section of this chapter. Running the development server Django comes with a lightweight web server to run your code quickly, without needing to spend time configuring a production server. When you run the Django development server, it keeps checking for changes in your code. It reloads automatically, freeing you from manually reloading it after code changes. However, it might not notice some actions, such as adding new files to your project, so you will have to restart the server manually in these cases. Start the development server by typing the following command in the shell prompt: python manage.py runserver You should see something like this: Watching for file changes with StatReloader Performing system checks... System check identified no issues (0 silenced). January 01, 2022 - 10:00:00 Django version 4.0, using settings 'mysite.settings' Starting development server at http://127.0.0.1:8000/ Quit the server with CONTROL-C. Now open http://127.0.0.1:8000/ in your browser. You should see a page stating that the project is successfully running, as shown in the Figure 1.2: Figure 1.2: The default page of the Django development server 10 Building a Blog Application The preceding screenshot indicates that Django is running. If you take a look at your console, you will see the GET request performed by your browser: [01/Jan/2022 17:20:30] "GET / HTTP/1.1" 200 16351 Each HTTP request is logged in the console by the development server. Any error that occurs while running the development server will also appear in the console. You can run the Django development server on a custom host and port or tell Django to load a specific settings file, as follows: python manage.py runserver 127.0.0.1:8001 --settings=mysite.settings When you have to deal with multiple environments that require different configurations, you can create a different settings file for each environment. This server is only intended for development and is not suitable for production use. To deploy Django in a production environment, you should run it as a WSGI application using a web server, such as Apache, Gunicorn, or uWSGI, or as an ASGI application using a server such as Daphne or Uvicorn. You can find more information on how to deploy Django with different web servers at https://docs. djangoproject.com/en/4.1/howto/deployment/wsgi/. Chapter 17, Going Live, explains how to set up a production environment for your Django projects. Project settings Let’s open the settings.py file and take a look at the configuration of the project. There are several set- tings that Django includes in this file, but these are only part of all the available Django settings. You can see all the settings and their default values at https://docs.djangoproject.com/en/4.1/ref/settings/. Let’s review some of the project settings: DEBUG is a Boolean that turns the debug mode of the project on and off. If it is set to True, Django will display detailed error pages when an uncaught exception is thrown by your application. When you move to a production environment, remember that you have to set it to False. Never deploy a site into production with DEBUG turned on because you will expose sensitive project-related data. ALLOWED_HOSTS is not applied while debug mode is on or when the tests are run. Once you move your site to production and set DEBUG to False, you will have to add your domain/host to this setting to allow it to serve your Django site. INSTALLED_APPS is a setting you will have to edit for all projects. This setting tells Django which applications are active for this site. By default, Django includes the following applications: django.contrib.admin: An administration site django.contrib.auth: An authentication framework Chapter 1 11 django.contrib.contenttypes: A framework for handling content types django.contrib.sessions: A session framework django.contrib.messages: A messaging framework django.contrib.staticfiles: A framework for managing static files MIDDLEWARE is a list that contains middleware to be executed. ROOT_URLCONF indicates the Python module where the root URL patterns of your application are defined. DATABASES is a dictionary that contains the settings for all the databases to be used in the project. There must always be a default database. The default configuration uses an SQLite3 database. LANGUAGE_CODE defines the default language code for this Django site. USE_TZ tells Django to activate/deactivate timezone support. Django comes with support for timezone-aware datetimes. This setting is set to True when you create a new project using the startproject management command. Don’t worry if you don’t understand much about what you’re seeing here. You will learn more about the different Django settings in the following chapters. Projects and applications Throughout this book, you will encounter the terms project and application over and over. In Django, a project is considered a Django installation with some settings. An application is a group of models, views, templates, and URLs. Applications interact with the framework to provide specific functional- ities and may be reused in various projects. You can think of a project as your website, which contains several applications, such as a blog, wiki, or forum, that can also be used by other Django projects. Figure 1.3 shows the structure of a Django project: Figure 1.3: The Django project/application structure 12 Building a Blog Application Creating an application Let’s create our first Django application. We will build a blog application from scratch. Run the following command in the shell prompt from the project’s root directory: python manage.py startapp blog This will create the basic structure of the application, which will look like this: blog/ __init__.py admin.py apps.py migrations/ __init__.py models.py tests.py views.py These files are as follows: __init__.py: An empty file that tells Python to treat the blog directory as a Python module. admin.py: This is where you register models to include them in the Django administration site—using this site is optional. apps.py: This includes the main configuration of the blog application. migrations: This directory will contain database migrations of the application. Migrations allow Django to track your model changes and synchronize the database accordingly. This directory contains an empty __init__.py file. models.py: This includes the data models of your application; all Django applications need to have a models.py file but it can be left empty. tests.py: This is where you can add tests for your application. views.py: The logic of your application goes here; each view receives an HTTP request, pro- cesses it, and returns a response. With the application structure ready, we can start building the data models for the blog. Creating the blog data models Remember that a Python object is a collection of data and methods. Classes are the blueprint for bundling data and functionality together. Creating a new class creates a new type of object, allowing you to create instances of that type. A Django model is a source of information and behaviors of your data. It consists of a Python class that subclasses django.db.models.Model. Each model maps to a single database table, where each attribute of the class represents a database field. When you create a model, Django will provide you with a practical API to query objects in the database easily. Chapter 1 13 We will define the database models for our blog application. Then, we will generate the database mi- grations for the models to create the corresponding database tables. When applying the migrations, Django will create a table for each model defined in the models.py file of the application. Creating the Post model First, we will define a Post model that will allow us to store blog posts in the database. Add the following lines to the models.py file of the blog application. The new lines are highlighted in bold: from django.db import models class Post(models.Model): title = models.CharField(max_length=250) slug = models.SlugField(max_length=250) body = models.TextField() def __str__(self): return self.title This is the data model for blog posts. Posts will have a title, a short label called slug, and a body. Let’s take a look at the fields of this model: title: This is the field for the post title. This is a CharField field that translates into a VARCHAR column in the SQL database. slug: This is a SlugField field that translates into a VARCHAR column in the SQL database. A slug is a short label that contains only letters, numbers, underscores, or hyphens. A post with the title Django Reinhardt: A legend of Jazz could have a slug like django-reinhardt-legend-jazz. We will use the slug field to build beautiful, SEO-friendly URLs for blog posts in Chapter 2, Enhancing Your Blog with Advanced Features. body: This is the field for storing the body of the post. This is a TextField field that translates into a TEXT column in the SQL database. We have also added a __str__() method to the model class. This is the default Python method to return a string with the human-readable representation of the object. Django will use this method to display the name of the object in many places, such as the Django administration site. If you have been using Python 2.x, note that in Python 3, all strings are natively considered Unicode; therefore, we only use the __str__() method. The __unicode__() method from Python 2.x is obsolete. 14 Building a Blog Application Let’s take a look at how the model and its fields will be translated into a database table and columns. The following diagram shows the Post model and the corresponding database table that Django will create when we synchronize the model to the database: Figure 1.4: Initial Post model and database table correspondence Django will create a database column for each of the model fields: title, slug, and body. You can see how each field type corresponds to a database data type. By default, Django adds an auto-incrementing primary key field to each model. The field type for this field is specified in each application configuration or globally in the DEFAULT_AUTO_FIELD setting. When creating an application with the startapp command, the default value for DEFAULT_AUTO_FIELD is BigAutoField. This is a 64-bit integer that automatically increments according to available IDs. If you don’t specify a primary key for your model, Django adds this field automatically. You can also define one of the model fields to be the primary key by setting primary_key=True on it. We will expand the Post model with additional fields and behaviors. Once complete, we will synchro- nize it to the database by creating a database migration and applying it. Adding datetime fields We will continue by adding different datetime fields to the Post model. Each post will be published at a specific date and time. Therefore, we need a field to store the publication date and time. We also want to store the date and time when the Post object was created, and when it was last modified. Edit the models.py file of the blog application to make it look like this. The new lines are highlighted in bold: from django.db import models from django.utils import timezone class Post(models.Model): title = models.CharField(max_length=250) slug = models.SlugField(max_length=250) body = models.TextField() publish = models.DateTimeField(default=timezone.now) created = models.DateTimeField(auto_now_add=True) Chapter 1 15 updated = models.DateTimeField(auto_now=True) def __str__(self): return self.title We have added the following fields to the Post model: publish: This is a DateTimeField field that translates into a DATETIME column in the SQL da- tabase. We will use it to store the date and time when the post was published. We use Django’s timezone.now method as the default value for the field. Note that we imported the timezone module to use this method. timezone.now returns the current datetime in a timezone-aware format. You can think of it as a timezone-aware version of the standard Python datetime.now method. created: This is a DateTimeField field. We will use it to store the date and time when the post was created. By using auto_now_add, the date will be saved automatically when creating an object. updated: This is a DateTimeField field. We will use it to store the last date and time when the post was updated. By using auto_now, the date will be updated automatically when saving an object. Defining a default sort order Blog posts are usually displayed in reverse chronological order (from newest to oldest). We will de- fine a default ordering for our model. The default order will apply when obtaining objects from the database when no order is specified in the query. Edit the models.py file of the blog application to make it look like this. The new lines are highlighted in bold: from django.db import models from django.utils import timezone class Post(models.Model): title = models.CharField(max_length=250) slug = models.SlugField(max_length=250) body = models.TextField() publish = models.DateTimeField(default=timezone.now) created = models.DateTimeField(auto_now_add=True) updated = models.DateTimeField(auto_now=True) class Meta: ordering = ['-publish'] def __str__(self): return self.title 16 Building a Blog Application We have added a Meta class inside the model. This class defines metadata for the model. We use the ordering attribute to tell Django that it should sort results by the publish field. This ordering will apply by default for database queries when no specific order is provided in the query. We indicate descending order by using a hyphen before the field name, -publish. Posts will be returned in reverse chronological order by default. Adding a database index Let’s define a database index for the publish field. This will improve performance for queries filtering or ordering results by this field. We expect many queries to take advantage of this index since we are using the publish field to order results by default. Edit the models.py file of the blog application and make it look like this. The new lines are highlighted in bold: from django.db import models from django.utils import timezone class Post(models.Model): title = models.CharField(max_length=250) slug = models.SlugField(max_length=250) body = models.TextField() publish = models.DateTimeField(default=timezone.now) created = models.DateTimeField(auto_now_add=True) updated = models.DateTimeField(auto_now=True) class Meta: ordering = ['-publish'] indexes = [ models.Index(fields=['-publish']), ] def __str__(self): return self.title We have added the indexes option to the model’s Meta class. This option allows you to define database indexes for your model, which could comprise one or multiple fields, in ascending or descending order, or functional expressions and database functions. We have added an index for the publish field. We use a hyphen before the field name to define the index in descending order. The creation of this index will be included in the database migrations that we will generate later for our blog models. Index ordering is not supported on MySQL. If you use MySQL for the database, a descend- ing index will be created as a normal index. Chapter 1 17 You can read more information about how to define indexes for models at https://docs.djangoproject. com/en/4.1/ref/models/indexes/. Activating the application We need to activate the blog application in the project, for Django to keep track of the application and be able to create database tables for its models. Edit the settings.py file and add blog.apps.BlogConfig to the INSTALLED_APPS setting. It should look like this. The new lines are highlighted in bold: INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'blog.apps.BlogConfig', ] The BlogConfig class is the application configuration. Now Django knows that the application is active for this project and will be able to load the application models. Adding a status field A common functionality for blogs is to save posts as a draft until ready for publication. We will add a status field to our model that will allow us to manage the status of blog posts. We will be using Draft and Published statuses for posts. Edit the models.py file of the blog application to make it look as follows. The new lines are highlighted in bold: from django.db import models from django.utils import timezone class Post(models.Model): class Status(models.TextChoices): DRAFT = 'DF', 'Draft' PUBLISHED = 'PB', 'Published' title = models.CharField(max_length=250) slug = models.SlugField(max_length=250) body = models.TextField() 18 Building a Blog Application publish = models.DateTimeField(default=timezone.now) created = models.DateTimeField(auto_now_add=True) updated = models.DateTimeField(auto_now=True) status = models.CharField(max_length=2, choices=Status.choices, default=Status.DRAFT) class Meta: ordering = ['-publish'] indexes = [ models.Index(fields=['-publish']), ] def __str__(self): return self.title We have defined the enumeration class Status by subclassing models.TextChoices. The available choices for the post status are DRAFT and PUBLISHED. Their respective values are DF and PB, and their labels or readable names are Draft and Published. Django provides enumeration types that you can subclass to define choices simply. These are based on the enum object of Python’s standard library. You can read more about enum at https://docs.python. org/3/library/enum.html. Django enumeration types present some modifications over enum. You can learn about those differences at https://docs.djangoproject.com/en/4.1/ref/models/fields/#enumeration-types. We can access Post.Status.choices to obtain the available choices, Post.Status.labels to obtain the human-readable names, and Post.Status.values to obtain the actual values of the choices. We have also added a new status field to the model that is an instance of CharField. It includes a choices parameter to limit the value of the field to the choices in Status.choices. We have also set a default value for the field using the default parameter. We use DRAFT as the default choice for this field. It’s a good practice to define choices inside the model class and use the enumeration types. This will allow you to easily reference choice labels, values, or names from anywhere in your code. You can import the Post model and use Post.Status.DRAFT as a reference for the Draft status anywhere in your code. Let’s take a look at how to interact with the status choices. Run the following command in the shell prompt to open the Python shell: python manage.py shell Chapter 1 19 Then, type the following lines: >>> from blog.models import Post >>> Post.Status.choices You will obtain the enum choices with value-label pairs like this: [('DF', 'Draft'), ('PB', 'Published')] Type the following line: >>> Post.Status.labels You will get the human-readable names of the enum members as follows: ['Draft', 'Published'] Type the following line: >>> Post.Status.values You will get the values of the enum members as follows. These are the values that can be stored in the database for the status field: ['DF', 'PB'] Type the following line: >>> Post.Status.names You will get the names of the choices like this: ['DRAFT', 'PUBLISHED'] You can access a specific lookup enumeration member with Post.Status.PUBLISHED and you can access its.name and.value properties as well. Adding a many-to-one relationship Posts are always written by an author. We will create a relationship between users and posts that will indicate which user wrote which posts. Django comes with an authentication framework that handles user accounts. The Django authentication framework comes in the django.contrib.auth package and contains a User model. We will use the User model from the Django authentication framework to create a relationship between users and posts. Edit the models.py file of the blog application to make it look like this. The new lines are highlighted in bold: from django.db import models from django.utils import timezone from django.contrib.auth.models import User 20 Building a Blog Application class Post(models.Model): class Status(models.TextChoices): DRAFT = 'DF', 'Draft' PUBLISHED = 'PB', 'Published' title = models.CharField(max_length=250) slug = models.SlugField(max_length=250) author = models.ForeignKey(User, on_delete=models.CASCADE, related_name='blog_posts') body = models.TextField() publish = models.DateTimeField(default=timezone.now) created = models.DateTimeField(auto_now_add=True) updated = models.DateTimeField(auto_now=True) status = models.CharField(max_length=2, choices=Status.choices, default=Status.DRAFT) class Meta: ordering = ['-publish'] indexes = [ models.Index(fields=['-publish']), ] def __str__(self): return self.title We have imported the User model from the django.contrib.auth.models module and we have added an author field to the Post model. This field defines a many-to-one relationship, meaning that each post is written by a user, and a user can write any number of posts. For this field, Django will create a foreign key in the database using the primary key of the related model. The on_delete parameter specifies the behavior to adopt when the referenced object is deleted. This is not specific to Django; it is an SQL standard. Using CASCADE, you specify that when the referenced user is deleted, the database will also delete all related blog posts. You can take a look at all the possi- ble options at https://docs.djangoproject.com/en/4.1/ref/models/fields/#django.db.models. ForeignKey.on_delete. We use related_name to specify the name of the reverse relationship, from User to Post. This will allow us to access related objects easily from a user object by using the user.blog_posts notation. We will learn more about this later. Chapter 1 21 Django comes with different types of fields that you can use to define your models. You can find all field types at https://docs.djangoproject.com/en/4.1/ref/models/fields/. The Post model is now complete, and we can now synchronize it to the database. But before that, we need to activate the blog application in our Django project. Creating and applying migrations Now that we have a data model for blog posts, we need to create the corresponding database table. Django comes with a migration system that tracks the changes made to models and enables them to propagate into the database. The migrate command applies migrations for all applications listed in INSTALLED_APPS. It synchro- nizes the database with the current models and existing migrations. First, we will need to create an initial migration for our Post model. Run the following command in the shell prompt from the root directory of your project: python manage.py makemigrations blog You should get an output similar to the following one: Migrations for 'blog': blog/migrations/0001_initial.py - Create model Post - Create index blog_post_publish_bb7600_idx on field(s) -publish of model post Django just created the 0001_initial.py file inside the migrations directory of the blog application. This migration contains the SQL statements to create the database table for the Post model and the definition of the database index for the publish field. You can take a look at the file contents to see how the migration is defined. A migration specifies dependencies on other migrations and operations to perform in the database to synchronize it with model changes. Let’s take a look at the SQL code that Django will execute in the database to create the table for your model. The sqlmigrate command takes the migration names and returns their SQL without executing it. Run the following command from the shell prompt to inspect the SQL output of your first migration: python manage.py sqlmigrate blog 0001 The output should look as follows: BEGIN; -- -- Create model Post -- 22 Building a Blog Application CREATE TABLE "blog_post" ( "id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, "title" varchar(250) NOT NULL, "slug" varchar(250) NOT NULL, "body" text NOT NULL, "publish" datetime NOT NULL, "created" datetime NOT NULL, "updated" datetime NOT NULL, "status" varchar(10) NOT NULL, "author_id" integer NOT NULL REFERENCES "auth_user" ("id") DEFERRABLE INITIALLY DEFERRED); -- -- Create blog_post_publish_bb7600_idx on field(s) -publish of model post -- CREATE INDEX "blog_post_publish_bb7600_idx" ON "blog_post" ("publish" DESC); CREATE INDEX "blog_post_slug_b95473f2" ON "blog_post" ("slug"); CREATE INDEX "blog_post_author_id_dd7a8485" ON "blog_post" ("author_id"); COMMIT; The exact output depends on the database you are using. The preceding output is generated for SQLite. As you can see in the output, Django generates the table names by combining the application name and the lowercase name of the model (blog_post), but you can also specify a custom database name for your model in the Meta class of the model using the db_table attribute. Django creates an auto-incremental id column used as the primary key for each model, but you can also override this by specifying primary_key=True on one of your model fields. The default id column consists of an integer that is incremented automatically. This column corresponds to the id field that is automatically added to your model. The following three database indexes are created: An index with descending order on the publish column. This is the index we explicitly defined with the indexes option of the model’s Meta class. An index on the slug column because SlugField fields imply an index by default. An index on the author_id column because ForeignKey fields imply an index by default. Chapter 1 23 Let’s compare the Post model with its corresponding database blog_post table: Figure 1.5: Complete Post model and database table correspondence Figure 1.5 shows how the model fields correspond to database table columns. Let’s sync the database with the new model. Execute the following command in the shell prompt to apply existing migrations: python manage.py migrate You will get an output that ends with the following line: Applying blog.0001_initial... OK We just applied migrations for the applications listed in INSTALLED_APPS, including the blog application. After applying the migrations, the database reflects the current status of the models. If you edit the models.py file in order to add, remove, or change the fields of existing models, or if you add new models, you will have to create a new migration using the makemigrations command. Each migration allows Django to keep track of model changes. Then, you will have to apply the migration using the migrate command to keep the database in sync with your models. Creating an administration site for models Now that the Post model is in sync with the database, we can create a simple administration site to manage blog posts. 24 Building a Blog Application Django comes with a built-in administration interface that is very useful for editing content. The Django site is built dynamically by reading the model metadata and providing a production-ready interface for editing content. You can use it out of the box, configuring how you want your models to be displayed in it. The django.contrib.admin application is already included in the INSTALLED_APPS setting, so you don’t need to add it. Creating a superuser First, you will need to create a user to manage the administration site. Run the following command: python manage.py createsuperuser You will see the following output. Enter your desired username, email, and password, as follows: Username (leave blank to use 'admin'): admin Email address: [email protected] Password: ******** Password (again): ******** Then you will see the following success message: Superuser created successfully. We just created an administrator user with the highest permissions. The Django administration site Start the development server with the following command: python manage.py runserver Open http://127.0.0.1:8000/admin/ in your browser. You should see the administration login page, as shown in Figure 1.6: Figure 1.6: The Django administration site login screen Chapter 1 25 Log in using the credentials of the user you created in the preceding step. You will see the adminis- tration site index page, as shown in Figure 1.7: Figure 1.7: The Django administration site index page The Group and User models that you can see in the preceding screenshot are part of the Django au- thentication framework located in django.contrib.auth. If you click on Users, you will see the user you created previously. Adding models to the administration site Let’s add your blog models to the administration site. Edit the admin.py file of the blog application and make it look like this. The new lines are highlighted in bold: from django.contrib import admin from.models import Post admin.site.register(Post) Now reload the administration site in your browser. You should see your Post model on the site, as follows: Figure 1.8: The Post model of the blog application included in the Django administration site index page 26 Building a Blog Application That was easy, right? When you register a model in the Django administration site, you get a us- er-friendly interface generated by introspecting your models that allows you to list, edit, create, and delete objects in a simple way. Click on the Add link beside Posts to add a new post. You will note the form that Django has generated dynamically for your model, as shown in Figure 1.9: Figure 1.9: The Django administration site edit form for the Post model Django uses different form widgets for each type of field. Even complex fields, such as the DateTimeField, are displayed with an easy interface, such as a JavaScript date picker. Chapter 1 27 Fill in the form and click on the SAVE button. You should be redirected to the post list page with a success message and the post you just created, as shown in Figure 1.10: Figure 1.10: The Django administration site list view for the Post model with an added successfully message Customizing how models are displayed Now, we will take a look at how to customize the administration site. Edit the admin.py file of your blog application and change it, as follows. The new lines are highlighted in bold: from django.contrib import admin from.models import Post @admin.register(Post) class PostAdmin(admin.ModelAdmin): list_display = ['title', 'slug', 'author', 'publish', 'status'] We are telling the Django administration site that the model is registered in the site using a custom class that inherits from ModelAdmin. In this class, we can include information about how to display the model on the site and how to interact with it. The list_display attribute allows you to set the fields of your model that you want to display on the administration object list page. The @admin.register() decorator performs the same function as the admin.site.register() function that you replaced, registering the ModelAdmin class that it decorates. Let’s customize the admin model with some more options. 28 Building a Blog Application Edit the admin.py file of your blog application and change it, as follows. The new lines are highlighted in bold: from django.contrib import admin from.models import Post @admin.register(Post) class PostAdmin(admin.ModelAdmin): list_display = ['title', 'slug', 'author', 'publish', 'status'] list_filter = ['status', 'created', 'publish', 'author'] search_fields = ['title', 'body'] prepopulated_fields = {'slug': ('title',)} raw_id_fields = ['author'] date_hierarchy = 'publish' ordering = ['status', 'publish'] Return to your browser and reload the post list page. Now, it will look like this: Figure 1.11: The Django administration site custom list view for the Post model You can see that the fields displayed on the post list page are the ones we specified in the list_display attribute. The list page now includes a right sidebar that allows you to filter the results by the fields included in the list_filter attribute. A search bar has appeared on the page. This is because we have defined a list of searchable fields using the search_fields attribute. Just below the search bar, there are navigation links to navigate through a date hierarchy; this has been defined by the date_hierarchy attribute. You can also see that the posts are ordered by STATUS and PUBLISH columns by default. We have specified the default sorting criteria using the ordering attribute. Chapter 1 29 Next, click on the ADD POST link. You will also note some changes here. As you type the title of a new post, the slug field is filled in automatically. You have told Django to prepopulate the slug field with the input of the title field using the prepopulated_fields attribute: Figure 1.12: The slug model is now automatically prepopulated as you type in the title Also, the author field is now displayed with a lookup widget, which can be much better than a drop- down select input when you have thousands of users. This is achieved with the raw_id_fields attribute and it looks like this: Figure 1.13: The widget to select related objects for the author field of the Post model With a few lines of code, we have customized the way the model is displayed on the administration site. There are plenty of ways to customize and extend the Django administration site; you will learn more about this later in this book. You can find more information about the Django administration site at https://docs.djangoproject. com/en/4.1/ref/contrib/admin/. Working with QuerySets and managers Now that we have a fully functional administration site to manage blog posts, it is a good time to learn how to read and write content to the database programmatically. The Django object-relational mapper (ORM) is a powerful database abstraction API that lets you create, retrieve, update, and delete objects easily. An ORM allows you to generate SQL queries using the object-oriented paradigm of Python. You can think of it as a way to interact with your database in pythonic fashion instead of writing raw SQL queries. The ORM maps your models to database tables and provides you with a simple pythonic interface to interact with your database. The ORM generates SQL queries and maps the results to model objects. The Django ORM is compatible with MySQL, PostgreSQL, SQLite, Oracle, and MariaDB. Remember that you can define the database of your project in the DATABASES setting of your project’s settings.py file. Django can work with multiple databases at a time, and you can program database routers to create custom data routing schemes. 30 Building a Blog Application Once you have created your data models, Django gives you a free API to interact with them. You can find the data model reference of the official documentation at https://docs.djangoproject.com/ en/4.1/ref/models/. The Django ORM is based on QuerySets. A QuerySet is a collection of database queries to retrieve ob- jects from your database. You can apply filters to QuerySets to narrow down the query results based on given parameters. Creating objects Run the following command in the shell prompt to open the Python shell: python manage.py shell Then, type the following lines: >>> from django.contrib.auth.models import User >>> from blog.models import Post >>> user = User.objects.get(username='admin') >>> post = Post(title='Another post',... slug='another-post',... body='Post body.',... author=user) >>> post.save() Let’s analyze what this code does. First, we are retrieving the user object with the username admin: user = User.objects.get(username='admin') The get() method allows you to retrieve a single object from the database. Note that this method expects a result that matches the query. If no results are returne