Как в питоне сделать to do list
Перейти к содержимому

Как в питоне сделать to do list

  • автор:

Список дел

1) Напишите простое приложение Todo, целью которого является
ведение списка
дел. Приложение должен состоять из двух классов: Todo и TodoItem:
a) Инициализатор Todo ничего не принимает. В классе должен
быть метод
add_todo, который принимает экземпляр класса TodoItem, и добавляет в
список todo_items. Метод list_items должен показывать все элементы в
списке todo_items. Метод find должен принимать слово в качестве
аргумента и выводиpть список экземпляров TodoItem, которые содержат
это слово в виде кортежа, который будет иметь формат (индекс,
экземпляр)
b) Инициализатор TodoItem принимает строковое значение. В нем
должна
быть переменная, сохраняющая состояние is_done. Также в классе
должен быть методы check и uncheck, которые меняют состояние
is_done.
c) Создайте экземпляр Todo, создайте несколько TodoItem,
вызовите их
методы.
d) Сделайте так, чтобы приложение работало с командой строки.
Подсказка: в качестве меню может выступать словарь, в котором ключем
будет номер, значением — метод. Для этого можете добавить метод run,
в котором будет цикл while принимающий input. Примените проверку
__name__ == “__main__”, создайте в ее блоке экземпляр класса Todo.

Лучшие ответы ( 1 )
94731 / 64177 / 26122
Регистрация: 12.04.2006
Сообщений: 116,782
Ответы с готовыми решениями:

Задача на распределение домашних дел
Господа жители, требуется помощь не просто в решении, а в объяснении как это возможно.

Вывести списки домашних дел для Саши и Жени
Женя и Саша распределяют между собой домашние дела из общего списка, при этом стараясь поделить.

Напишите программу для сортировки предстоящих дел по важности
Ученик чародея Контрольная работа макс. 15 балл. Дедлайн: 21 апр. 17:36 Ограничение времени 5.

Имитация реального положения дел с модулем numpy.random
Написать код Общее количество примеров 400 Есть вводные данные: у нас отрицательных примеров.

Меню пользователя @ Dax

301 / 211 / 112
Регистрация: 03.12.2016
Сообщений: 409

Лучший ответ

Сообщение было отмечено Catstail как решение

Решение

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79
class Todo: menu = {'1': 'Добавить дело', '2': 'Список дел', '3': 'Найти дело', '4': 'Выполнить дело', '5': 'Повторить дело', '6': 'Выйти'} def __init__(self): self.todo_items = [] # Список дел def add_todo(self, items): self.todo_items.append(items) def list(self): print('Список дел:') for item in self.todo_items: print(str(item.num) + '. ' + item.todo + ' (Выполнено)' * int(item.is_done)) print() def find(self, find_str): return ((item.num, item.todo) for item in self.todo_items if item.todo.find(find_str) != -1) def run(self): while True: print('Меню:') for num, val in Todo.menu.items(): print(num + '. ' + val, end='\n') command = input() if command == '1': self.add_todo(TodoItem(input('Какре дело? '))) print('Новое дело добавлено') elif command == '2': self.list() elif command == '3': find = self.find(input('Введите ключевое слово? ')) for num, val in find: print(str(num) + '. ' + val) elif command == '4': num = int(input('Номер дела для выполнения: ')) for item in self.todo_items: if item.num == num: item.check() print(f'Дело выполнено') break else: print(f'Дело не найдено') elif command == '5': num = int(input('Номер дела для повторения: ')) for item in self.todo_items: if item.num == num: item.uncheck() print(f'Дело повторено') break else: print(f'Дело не найдено') elif command == '6': print('Программа завершена') break class TodoItem: couner_do = 0 def __init__(self, new_do): self.is_done = False TodoItem.couner_do += 1 self.num = TodoItem.couner_do self.todo = new_do def check(self): self.is_done = True def uncheck(self): self.is_done = False if __name__ == '__main__': todo_1 = Todo() todo_1.run()

TODO comments

Sometimes, you need to mark parts of your code for future reference: areas of optimization and improvement, possible changes, questions to be discussed, and so on. PyCharm lets you add special types of comments that are highlighted in the editor, indexed, and listed in the TODO tool window. This way you and your teammates can keep track of issues that require attention.

TODO comments example

By default, there are two patterns recognized by PyCharm: TODO and FIXME in both lower and upper case. These patterns can be used inside line and block comments of any supported file type. You can modify the default patterns or add your own patterns if necessary.

Create a multiline TODO item

  • Indent the text in comment lines that follow the initial comment line. You can use spaces and tabs, or a mix of both for indenting multiline TODO items.

Disable multiline TODO items

TODO comments example

  1. Press Control+Alt+S to open the IDE settings and then select Editor | TODO .
  2. Clear the Treat indented text on the following lines as part of the same TODO checkbox.

View TODO items

  • Open the TODO tool window: View | Tool Windows | TODO .

Use tabs to change the source of TODO items you want to view: from all files in your current project, only those in the current file, based on a certain scope of files, or from files in the active changelist (if you have version control integration configured).

To jump to a TODO comment in the source code, click the corresponding TODO item in the TODO tool window. To disable this behavior, use the Navigate with Single Click button on the toolbar (in this case, you will need to double-click the TODO item to jump to the relevant comment).

The TODO tool window

Add custom patterns and filter TODO items

You can add your own patterns and filter the list to show only TODO items that match certain patterns. For example, you can choose to mark places of possible optimization in your code with the OPTIMIZE pattern and ignore all other types of TODO items when viewing them in the TODO tool window.

  1. In the Settings dialog ( Control+Alt+S ), select Editor | TODO .
  2. Use a regular expression to specify a custom pattern. For example, to add a pattern for the word OPTIMIZE in a comment, click in the Patterns section of the TODO dialog, and type the following regular expression:

\boptimize\b.*

The icon that you select for a pattern is displayed in the TODO tool window to better distinguish various TODO items. By enabling the Case Sensitive checkbox for a pattern, you can force the pattern to match only with the specified case.

Manage Your To-Do Lists Using Python and Django

Manage Your To-Do Lists Using Python and Django

Have you ever struggled to keep track of the things that you need to do? Perhaps you’re in the habit of using a handwritten to-do list to remind you of what needs doing, and by when. But handwritten notes have a way of getting lost or forgotten. Because you’re a Python coder, it makes sense to build a Django to-do list manager!

In this step-by-step tutorial, you’re going to create a web app using Django. You’ll learn how Django can integrate with a database that stores all your to-do items in lists that you can define. Each item will have a title, a description, and a deadline. With this app, you can manage your own deadlines and help your entire team stay on track!

In this tutorial, you’ll learn how to:

  • Create a web app using Django
  • Build a data model with one-to-many relationships
  • Use the Django admin interface to explore your data model and add test data
  • Design templates for displaying your lists
  • Leverage class-based views to handle the standard database operations
  • Control the Django URL dispatcher by creating URL configurations

Along the way, you’ll learn how Django’s class-based views leverage the power of object-oriented programming. That’ll save you a ton of development effort!

Get Source Code: Click here to get the source code you’ll use to build your to-do list app.

Demo

In this tutorial, you’ll build a Django to-do list manager. Your main page will display all of your to-do lists. By clicking the Add a new list button, you’ll display a page where you can name and create a new list:

Django Development Server's Default Success Page

Django’s default page provides links to lots of helpful documentation. You can explore those links later.

Note: When you’ve finished testing, you can type Ctrl + C or Cmd + C in the console window to stop the server.

You’ve completed all the standard setup tasks for a new Django app. It’s time to start coding the unique features of your application.

Step 3: Design Your To-Do Data

The heart of any application is its data structures. In this step, you’ll design and code the application data model and the relations between application objects. Then you’ll use Django’s object-relational modeling tools to map that data model into database tables.

To download the code for this stage of the project, click the following link and navigate to the source_code_step_3/ folder:

Get Source Code: Click here to get the source code you’ll use to build your to-do list app.

Each type of user data will require its own data model. Your to-do list app will contain just two basic types of data:

  1. A ToDoList with a title: You can have as many of these as you want.
  2. A ToDoItem that is linked to a particular list: Again, there’s no limit to the number of ToDoItem objects. Each ToDoItem will have its own title, a longer description, a created date, and a due date.

Your data models will form the backbone of your app. Next up, you’ll define them by editing the file models.py .

Define Your Data Models

Open the file models.py in your editor. It’s pretty minimal at the moment:

# todo_list/todo_app/models.py from django.db import models # Create your models here. 

This is just placeholder text to help you remember where to define data models. Replace this text with the code for your data models:

 1# todo_list/todo_app/models.py 2from django.utils import timezone 3 4from django.db import models 5from django.urls import reverse 6 7def one_week_hence(): 8 return timezone.now() + timezone.timedelta(days=7) 9 10class ToDoList(models.Model): 11 title = models.CharField(max_length=100, unique=True) 12 13 def get_absolute_url(self): 14 return reverse("list", args=[self.id]) 15 16 def __str__(self): 17 return self.title 18 19class ToDoItem(models.Model): 20 title = models.CharField(max_length=100) 21 description = models.TextField(null=True, blank=True) 22 created_date = models.DateTimeField(auto_now_add=True) 23 due_date = models.DateTimeField(default=one_week_hence) 24 todo_list = models.ForeignKey(ToDoList, on_delete=models.CASCADE) 25 26 def get_absolute_url(self): 27 return reverse( 28 "item-update", args=[str(self.todo_list.id), str(self.id)] 29 ) 30 31 def __str__(self): 32 return f"self.title>: due self.due_date>" 33 34 class Meta: 35 ordering = ["due_date"] 

The file models.py defines your entire data model. In it, you’ve defined one function and two data model classes:

  • Lines 7 to 8 define a stand-alone utility function, one_week_hence() , that’ll be useful for setting ToDoItem default due dates.
  • Lines 10 to 35 define two classes that extend Django’s django.db.models.Model superclass. That class does most of the heavy lifting for you. All you need to do in the subclasses is define the data fields in each model, as described below.

The Model superclass also defines an id field, which is automatically unique for each object and serves as its identifier.

The django.db.models submodule also has handy classes for all the field types that you might want to define. These allow you to set up useful default behavior:

  • Lines 11 and 20 declare title fields that are each limited to one hundred characters. In addition, ToDoList.title must be unique. You can’t have two ToDoList objects with the same title.
  • Line 21 declares a ToDoItem.description field that may be empty.
  • Lines 22 and 23 each provide useful defaults for their date fields. Django will automatically set .created_date to the current date the first time a ToDoItem object is saved, while .due_date uses one_week_hence() to set a default due date one week in the future. Of course, the application will allow the user to change the due date if this default value doesn’t suit them.
  • Line 24 declares what is perhaps the most interesting field, ToDoItem.todo_list . This field is declared as a foreign key. It links the ToDoItem back to its ToDoList , so that each ToDoItem must have exactly one ToDoList to which it belongs. In database lingo, this is a one-to-many relationship. The on_delete keyword in the same line ensures that if a to-do list is deleted, then all the associated to-do items will be deleted too.
  • Lines 16 to 17 and 31 to 32 declare .__str__() methods for each model class. This is the standard Python way of creating a readable representation of an object. It’s not strictly necessary to write this function, but it can help with debugging.
  • Lines 13 to 14 and 26 to 29 implement the .get_absolute_url() method, a Django convention for data models. This function returns the URL for the particular data item. This allows you to reference the URL conveniently and robustly in your code. The return statement of both implementations of .get_absolute_url() uses reverse() to avoid hard-coding the URL and its parameters.
  • Line 34 defines the nested Meta class, which allows you to set some useful options. Here, you’re using it to set a default ordering for ToDoItem records.

Now save the models.py file with its two model classes. With this file, you’ve just declared the data model for the entire app. Soon, you’ll use Django tooling to map the model to your database.

Create the Database

So far, you’ve defined the two model classes in your Python code. Now the magic happens! Use the command line to create and activate the migrations:

Windows Command Prompt

(venv) C:\> python manage.py makemigrations todo_app (venv) C:\> python manage.py migrate 
(venv) $ python manage.py makemigrations todo_app (venv) $ python manage.py migrate 

These two subcommands, makemigrations and migrate , are provided by manage.py and help to automate the process of keeping your physical database structure in line with the data model in your code.

With makemigrations , you’re telling Django that you’ve changed the application’s data model, and you’d like to record those changes. In this particular case, you’ve defined two brand-new tables, one for each of your data models. In addition, Django has automatically created its own data models for the admin interface and for its own internal use.

Each time you make a change to the data model and call makemigrations , Django adds a file to the folder todo_app/migrations/ . The files in this folder store a history of the changes that you’ve made to the database structure. This allows you to revert and then reapply those changes later if need be.

With the migrate command, you put those changes into effect by running commands against the database. Just like that, you’ve created your new tables, along with some useful admin tables.

Your data model is now mirrored in the database, and you’ve also created an audit trail in preparation for any structural changes that you may apply later.

Step 4: Add Your Sample To-Do Data

So now you have a data model and a database, but you don’t yet have any actual data in the form of to-do lists or items. Soon, you’ll be building web pages to create and manipulate these, but for now, you can create a little test data in the easiest way, by using Django’s ready-made admin interface. This tool enables you not only to manage model data, but also to authenticate users, display and handle forms, and validate input.

To download the code for this stage of the project, click the following link and navigate to the source_code_step_4/ folder:

Get Source Code: Click here to get the source code you’ll use to build your to-do list app.

Meet the Django Admin Interface

To use the admin interface, you should have a superuser. This will be someone with extraordinary powers who can be trusted with the keys to the whole Django server. Does this sound like you? Create your new superuser now:

Windows Command Prompt

(venv) C:\> python manage.py createsuperuser 
(venv) $ python manage.py createsuperuser 

Just follow the prompts to register yourself as a superuser. Your superpowers are installed!

Although you now have access to the admin interface, there’s one more step to complete before it can make use of your new data models. You need to register the models with the admin app, which you can do by editing the file admin.py :

# todo_list/todo_app/admin.py from django.contrib import admin from todo_app.models import ToDoItem, ToDoList admin.site.register(ToDoItem) admin.site.register(ToDoList) 

And now you’re ready to use the Django administration app. Launch the development server and start exploring. First, make sure the development server is running:

Windows Command Prompt

(venv) C:\> python manage.py runserver 
(venv) $ python manage.py runserver 

Now open a web browser and go to the address http://127.0.0.1:8000/admin/ . You should see the admin app’s login screen. Enter your newly minted credentials, and the admin landing page appears:

Landing page of the Django admin application for todo list project

Notice that it already displays links for To do lists and To do items. You’re ready to view and change the data.

Start a To-Do List

At the left of the main Django administration page, click on To do lists. On the next screen, click on the button at the top right that says ADD TO DO LIST.

You’re ready to create your first to-do list. Give it a title, for example “Things to do today,” and click the SAVE button on the extreme right of the screen. The new list now appears on a page headed Select to do list to change. You can ignore this and instead click on the + Add button that appears next to To do items on the left of the screen. A new form appears:

Djkango admin form for adding a todo item

Fill the item with some sample data. Give your item the title “Start my to-do list” and the description “First things first.” Leave the due date as it is, exactly one week from today. For Todo list, select your newly created to-do list title from the dropdown menu. Then hit SAVE.

That’s it. You’ve created one list and one item with the admin interface. Feel free to stay and explore the pages to get a feel for how they work. The admin interface is great for you, as the developer, to use for quick and dirty hacking on the data, but it’s not intended to be seen by regular users. Important business awaits. You need to create the public user interface!

Step 5: Create the Django Views

In this step, you’ll learn how to create the public interface for your app. In Django, this involves using views and templates. A view is the code that orchestrates web pages, the presentation logic of your web app. The template is the component that more closely resembles an HTML page, as you’ll see.

To download the code for this stage of the project, click the following link and navigate to the source_code_step_5/ folder:

Get Source Code: Click here to get the source code you’ll use to build your to-do list app.

Code Your First View

A view is Python code that tells Django how to navigate between pages and which data to send along to be displayed. A web application works by receiving an HTTP request from the browser, deciding what to do with it, and then sending back a response. The application then sits back and waits for the next request.

In Django, this request-response cycle is controlled by the view, which, in its most basic form, is a Python function living inside the file views.py . The view function’s main input data is an HttpRequest Python object, and its job is to return an HttpResponse Python object.

There are two basic approaches to coding a view. You can just create a function, as described above, or you can use a Python class, whose methods will handle the requests. In either case, you need to inform Django that your view is intended for handling a particular type of request.

There are some definite advantages to using a class:

  • Consistency: Each HTTP request is associated with a command to the server, known as its method or verb. This may be GET, POST, HEAD, or another, according to the response required. Each verb has its own matching method name in your class. For example, the method for handling an HTTP GET request is named .get() .
  • Inheritance: You can use the power of inheritance by extending existing classes that already do most of what you need the views to do.

In this tutorial, you’ll be using the class-based approach and taking advantage of Django’s pre-built generic views for maximum code reuse. Here’s where you can benefit from the wisdom of Django’s designers. There are many, many different databases and data models out there, but fundamentally they’re all based on units of information called records and four basic operations that you can do on them:

These activities are commonly known as CRUD, and they’re more or less standard across many applications.

For example, a user expects to be able to select a record, edit its fields, and save it, regardless of whether the record represents a to-do item, an inventory item, or anything else. So Django provides developers with class-based views, which are pre-built views implemented as classes that already contain most of the code to do these things.

All you, the developer, have to do is customize them for your data model and your application, perhaps tweaking their behavior here and there as you do so.

In your editor, open the file todo_app/views.py . Currently, it contains no useful code. Clear the file and create your first view:

# todo_list/todo_app/views.py from django.views.generic import ListView from .models import ToDoList class ListListView(ListView): model = ToDoList template_name = "todo_app/index.html" 

The ListListView class will display a list of the to-do list titles. As you can see, not much code is required here. You’re making use of the generic class django.views.generic.ListView . It already knows how to retrieve a list of objects from the database, so you only need to tell it two things:

  1. The data-model class that you’d like to fetch
  2. The name of the template that’ll format the list into a displayable form

In this case, the data-model class is ToDoList , which you created in Step 3. Now it’s time to learn about templates.

Understand Templates

A template is just a file containing HTML markup, with a few additional placeholders to accommodate dynamic data. Because you want to be able to reuse your code, you’re going to start by creating a base template that contains all the boilerplate HTML code that you want to appear on every page. The actual app pages will then inherit all this boilerplate, like how your views will inherit much of their functionality from base classes.

Note: You can learn more from the official documentation on templates.

Create a Base Template

Create a new folder inside the todo_app/ directory named templates/ . Now add a new file to this folder. You can call it base.html . Create your base template:

 1 2  3  4html lang="en"> 5 6head> 7  8 meta charset="utf-8"> 9 meta name="viewport" content="width=device-width, initial-scale=1"> 10 11 link rel="stylesheet" href="https://cdn.simplecss.org/simple.min.css"> 12 title>Django To-do Liststitle> 13head> 14 15body> 16 div> 17 h1 onclick="location.href=' url "index" %>'"> 18 Django To-do Lists 19 h1> 20 div> 21 div> 22  block content %> 23 This content will be replaced by different html code for each page. 24  endblock %> 25 div> 26body> 27 28html> 

For the most part, this is just a skeleton HTML page with the standard structure. Lines 22 to 24, however, declare special … tags. These placeholders reserve a space that’ll receive more HTML markup from the pages that inherit from this one.

Line 11 imports the open-source Simple.css library. Raw HTML without CSS renders in a way most people find, well, ugly. Just by importing Simple.css like this, you automatically make your site look much better, with no further effort. Now you can develop and test your site logic without hurting your eyes. Of course, you’ll still have the option of adding your own creative touches later!

Line 17 introduces the template syntax as the target of an onclick event handler. The Django template engine will find the urlpatterns entry named «index» in the URLConf file todo_app/urls.py and replace this template with the correct path. The effect of this is that a click on the Django To-do Lists heading will cause the browser to redirect itself to the URL named «index» , which will be your home page.

Note: This means that you can return to the app’s home page from anywhere in the app by clicking on the Django To-do Lists heading.

So base.html is a complete web page, but not very exciting as it stands. To make it more interesting, you need some markup to go between the and tags. The inheriting templates will each supply their own markup to fill this block.

Add a Home Page Template

Django’s convention for the templates belonging to an app is that they live in a folder named templates/ inside the app folder. So although the base template base.html went in the todo_app/templates/ folder, the others will all be placed inside a folder named todo_app/templates/todo_app/ :

Windows Command Prompt

(venv) C:\> mkdir todo_app\templates\todo_app 
(venv) $ mkdir todo_app/templates/todo_app 

Your first objective will be to code the template for the home page of your website, traditionally named index.html . Create a new file in your editor and save it in the folder that you’ve just created:

 1 2 3 extends "base.html" %> 4 block content %> 5  6 if object_list %> 7h3>All my listsh3> 8 endif %> 9ul> 10  for todolist in object_list %> 11 li> 12 div 13 role="button" 14 onclick="location.href=' url "list" todolist.id %>'"> 15  <todolist.title >> 16 div> 17 li> 18  empty %> 19 h4>You have no lists!h4> 20  endfor %> 21ul> 22 23 endblock %> 

Line 3 sets the scene: you’re extending the base template. That means that everything from the base template will appear in the rendered version of index.html , except everything between and including the and tags. Instead, that section of base.html will be replaced by the code inside the corresponding pair of and tags in index.html .

There’s more template magic to be found inside index.html , where more template tags allow some quite Python-like logic:

  • Lines 6 to 8 define a code block delimited by and tags. These ensure that the heading All my lists won’t appear if object_list is null or empty. ListView automatically provides the context variable object_list to the template, and object_list contains the list of to-do lists.
  • Lines 10 to 20 define a loop bracketed by the and tags. This construct renders the enclosed HTML once for each object in the list. As a nice bonus, the tag in line 18 lets you define what should be rendered instead, if the list is empty.
  • Line 15 demonstrates the mustache syntax. The double curly brackets ( > ) cause the template engine to emit HTML that displays the value of the enclosed variable. In this case, you’re rendering the title property of the loop variable todolist .

You’ve now created the home page of your application, which will display a list of all your to-do lists if you have any, or an informative message if not. Your user will be directed to other pages, or back to this one, by the URL dispatcher. You’ll see how the dispatcher does its work in the next section.

Build a Request Handler

The life of a web app is mostly spent waiting for HTTP Requests to arrive from the browser. The two most common HTTP request verbs are GET and POST. The action performed by a GET request is mostly defined by its URL, which not only routes the request back to the correct server, but also contains parameters that tell the server exactly what information the browser is requesting. A typical HTTP URL might look like http://example.com/list/3/item/4 .

This hypothetical URL, sent with a GET request, might be interpreted by the server at example.com as a request to use the default app to show item four from list three.

A POST request may also have URL parameters, but it behaves a little differently. POST sends some further information, besides the URL parameters, to the server. This information, which isn’t displayed in the browser’s search bar, might be used to update a record from a web form, for example.

Note: There’s lots more to learn about HTTP requests. The first section of Exploring HTTPS With Python offers more detail about how HTTP works. It then goes on to explore HTTPS, the secure form of the protocol.

Django’s URL dispatcher has the responsibilty of parsing URLs and forwarding the requests to the appropriate views. The URLs may be received from the browser or sometimes internally, from the server itself.

The URL dispatcher does its job by consulting what’s known as the URLconf, a set of URL patterns mapped to views. These mappings are conventionally stored in a file named urls.py . Once the URL dispatcher finds a match, then it calls the matching view with the URL parameters.

Back in Step 2, you created a urlpatterns item in the project-level URLconf file, todo_project/urls.py . That urlpattern sees to it that any HTTP request starting with http://example.com[:port]/. will be passed to your app. The app-level urls.py takes it from there.

You’ve already created the app-level URL file. Now it’s time to add the first route to that file. Edit the file todo_app/urls.py to add the route:

 1# todo_list/todo_app/urls.py 2from django.urls import path 3from . import views 4 5urlpatterns = [ 6 path("", views.ListListView.as_view(), name="index"), 7] 

Line 6 tells Django that if the rest of the URL is empty, your ListListView class should be called to handle the request. Notice that the name=»index» parameter matches the target of the macro that you saw in line 18 of the base.html template.

So now you have all the ingredients to produce your first home-baked view. The request-response cycle proceeds as follows:

  1. When the server receives a GET request with this URL from the browser, it creates an HTTPRequest object and sends it to the ListListView that you previously defined in views.py .
  2. This particular view is a ListView based on the ToDoList model, so it fetches all the ToDoList records from the database, turns them into ToDoList Python objects, and appends them to a list, named object_list by default.
  3. The view then passes the list to the template engine for display, using the specified template, index.html .
  4. The template engine builds the HTML code from index.html , automatically combining it with base.html and using the passed-in data plus the template’s embedded logic to populate the HTML elements.
  5. The view constructs an HttpResponse object containing the fully built HTML and returns this to Django.
  6. Django turns the HttpResponse into an HTTP message and sends that back to the browser.
  7. The browser, seeing a fully formed HTTP page, displays it to the user.

You’ve just created your first end-to-end Django request handler! However, you may have noticed that your index.html file references a URL name that doesn’t exist yet: «list» . That means that if you try to run your app now, it won’t work. You’ll need to define that URL and create the corresponding view to make the app work properly.

This new view, like ListListView and all the other views in this tutorial, will be class-based. Before diving into more code, it’s important to understand what that means.

Reuse Class-Based Generic Views

In this tutorial, you’re using class-based generic views. A class that’s intended to work as a view should extend the class django.views.View and override that class’s methods, such as .get() and .post() , that handle the corresponding HttpRequest types. Each of these methods accepts an HttpRequest and returns an HttpResponse .

Class-based generic views take reusability to the next level. Most of the expected functionality is already encoded in the base class. A view class based on the generic view class ListView , for example, needs to know just two things:

  1. What data type it’s listing
  2. What template it’ll use to render the HTML

With that information, it can render a list of objects. Of course, ListView , like other generic views, doesn’t limit you to this very basic pattern. You can tweak and subclass the base class to your heart’s content. But the basic functionality is already there, and it costs you nothing.

Subclass ListView to Display a List of To-Do Items

You’ve already created your first view to extend the generic class django.views.generic.list.ListView as a new subclass, ListListView , whose job is to display a list of to-do lists.

Now you can do something very similar to display a list of to-do items. You’ll start by creating another view class, this time called ItemListView . Like the class ListListView , ItemListView will extend the generic Django class ListView . Open views.py and add your new class:

 1# todo_list/todo_app/views.py 2from django.views.generic import ListView  3from .models import ToDoList, ToDoItem  4 5class ListListView(ListView): 6 model = ToDoList 7 template_name = "todo_app/index.html"  8  9class ItemListView(ListView): 10 model = ToDoItem 11 template_name = "todo_app/todo_list.html" 12 13 def get_queryset(self): 14 return ToDoItem.objects.filter(todo_list_id=self.kwargs["list_id"]) 15 16 def get_context_data(self): 17 context = super().get_context_data() 18 context["todo_list"] = ToDoList.objects.get(id=self.kwargs["list_id"]) 19 return context 

In your ItemListView implementation, you’re specializing ListView a little bit. When you show a list of ToDoItem objects, you don’t want to show every ToDoItem in the database, just those that belong to the current list. To do this, lines 13 to 14 override the ListView.get_queryset() method by using the model’s objects.filter() method to restrict the data returned.

Every descendant of the View class also has a .get_context_data() method. The return value from this is the template’s context , a Python dictionary that determines what data is available for rendering. The result of .get_queryset() is automatically included in context under the key object_list , but you’d like the template to be able to access the todo_list object itself, and not just the items within it that were returned by the query.

Lines 16 to 19 override .get_context_data() to add this reference to the context dictionary. It’s important that line 17 calls the superclass’s .get_context_data() first, so that the new data can be merged with the existing context instead of clobbering it.

Notice that both of the overridden methods make use of self.kwargs[«list_id»] . This implies that there must be a keyword argument named list_id passed to the class when it’s constructed. You’ll soon learn where this argument comes from.

Show the Items in a To-Do List

Your next task is to create a template for displaying the TodoItems in a given list. Once again, the in %> … construct will be indispensable. Create the new template, todo_list.html :

 1 2 extends "base.html" %> 3 4 block content %> 5div> 6 div> 7 div> 8 h3>Edit list:h3> 9 h5> <todo_list.title | upper >>h5> 10 div> 11 ul> 12  for todo in object_list %> 13 li> 14 div> 15 div 16 role="button" 17 onclick="location.href='#'"> 18  <todo.title >> 19 (Due  <todo.due_date | date:"l, F j" >>) 20 div> 21 div> 22 li> 23  empty %> 24 p>There are no to-do items in this list.p> 25  endfor %> 26 ul> 27 p> 28 input 29 value="Add a new item" 30 type="button" 31 onclick="location.href='#'" /> 32 p> 33 div> 34div> 35 endblock %> 

This template for displaying a single list with its to-do items is similar to index.html , with a couple of extra wrinkles:

  • Lines 9 and 19 exhibit a curious syntax. These expressions with a pipe symbol ( | ) are called template filters, and they provide a convenient way of formatting the title and the due date, respectively, using the pattern to the right of the pipe.
  • Lines 15 to 17 and 28 to 31 define a couple of button-like elements. Right now, their onclick event handlers do nothing useful, but you’ll soon be fixing that.

So you’ve coded an ItemListView class, but so far, there’s no way for your user to invoke it. You need to add a new route into urls.py so that ItemListView can be used:

 1# todo_list/todo_app/urls.py 2from todo_app import views 3 4urlpatterns = [ 5 path("", 6 views.ListListView.as_view(), name="index"),  7 path("list//",  8 views.ItemListView.as_view(), name="list"),  9] 

Line 7 declares a placeholder as the new route’s first parameter. This placeholder will match a positional parameter in the URL path that the browser returns. The syntax list// means that this entry will match a URL like list/3/ and pass the named parameter list_id = 3 to the ItemListView instance. If you revisit the ItemListView code in views.py , you’ll notice that it references this parameter in the form self.kwargs[«list_id»] .

Now you can view all of your lists, thanks to the route, view, and template that you’ve created. You’ve also created a route, view, and template for listing individual to-do items.

It’s time to test what you’ve done so far. Try running your development server now, in the usual way:

Windows Command Prompt

(venv) C:\> python manage.py runserver 
(venv) $ python manage.py runserver 

Depending on what you added from the admin interface, you should see one or more list names. You can click on each one to show the items that particular list contains. You can click on the main Django To-do Lists heading to navigate back to the app’s main page.

Your app can now display lists and items. You’ve implemented just the Read part of the CRUD operations. Run your development server now. You should be able to navigate back and forth between the list of to-do lists and the items in a single list, but you cannot yet add or delete lists, or add, edit, and remove items.

Step 6: Create and Update Model Objects in Django

In this step, you’ll enhance your app by enabling Creation and Update of lists and items. You’ll do this by extending some more of Django’s generic view classes. Through this process, you’ll notice how the logic already baked into these classes accounts for many of the typical CRUD use cases. But remember that you’re not limited to the pre-baked logic. You can override almost any part of the request-response cycle.

To download the code for this stage of the project, click the following link and navigate to the source_code_step_6/ folder:

Get Source Code: Click here to get the source code you’ll use to build your to-do list app.

The first item on the agenda will be to add the new views that support the Create and Update actions. Next, you’ll add URLs referencing those views, and finally, you’ll update the todo_items.html template to provide links allowing the user to navigate to the new URLs.

Add your new imports and view classes to views.py :

 1# todo_list/todo_app/views.py 2from django.urls import reverse 3 4from django.views.generic import ( 5 ListView,  6 CreateView,  7 UpdateView,  8) 9from .models import ToDoItem, ToDoList 10 11class ListListView(ListView): 12 model = ToDoList 13 template_name = "todo_app/index.html" 14 15class ItemListView(ListView): 16 model = ToDoItem 17 template_name = "todo_app/todo_list.html" 18 19 def get_queryset(self): 20 return ToDoItem.objects.filter(todo_list_id=self.kwargs["list_id"]) 21 22 def get_context_data(self): 23 context = super().get_context_data() 24 context["todo_list"] = ToDoList.objects.get(id=self.kwargs["list_id"]) 25 return context 26 27class ListCreate(CreateView): 28 model = ToDoList 29 fields = ["title"] 30 31 def get_context_data(self): 32 context = super(ListCreate, self).get_context_data() 33 context["title"] = "Add a new list" 34 return context 35 36class ItemCreate(CreateView): 37 model = ToDoItem 38 fields = [ 39 "todo_list", 40 "title", 41 "description", 42 "due_date", 43 ] 44 45 def get_initial(self): 46 initial_data = super(ItemCreate, self).get_initial() 47 todo_list = ToDoList.objects.get(id=self.kwargs["list_id"]) 48 initial_data["todo_list"] = todo_list 49 return initial_data 50 51 def get_context_data(self): 52 context = super(ItemCreate, self).get_context_data() 53 todo_list = ToDoList.objects.get(id=self.kwargs["list_id"]) 54 context["todo_list"] = todo_list 55 context["title"] = "Create a new item" 56 return context 57 58 def get_success_url(self): 59 return reverse("list", args=[self.object.todo_list_id]) 60 61class ItemUpdate(UpdateView): 62 model = ToDoItem 63 fields = [ 64 "todo_list", 65 "title", 66 "description", 67 "due_date", 68 ] 69 70 def get_context_data(self): 71 context = super(ItemUpdate, self).get_context_data() 72 context["todo_list"] = self.object.todo_list 73 context["title"] = "Edit item" 74 return context 75 76 def get_success_url(self): 77 return reverse("list", args=[self.object.todo_list_id]) 

There are three new view classes here, all derived from Django’s generic view classes. Two of the new classes extend django.view.generic.CreateView , while the third extends django.view.generic.UpdateView :

  • Lines 27 to 34 define ListCreate . This class defines a form containing the sole public ToDoList attribute, its title . The form itself also has a title, which is passed in the context data.
  • Lines 36 to 59 define the ItemCreate class. This generates a form with four fields. The .get_initial() and .get_context_data() methods are overridden to provide useful information to the template. The .get_success_url() method provides the view with a page to display after the new item has been created. In this case, it calls the list view after a successful form submit to display the full to-do list containing the new item.
  • Lines 61 to 77 define ItemUpdate , which is very similar to ItemCreate but supplies a more appropriate title.

You’ve now defined three new view classes for creating and updating to-do lists and their items. Your code and templates will instantiate these classes on demand, complete with the relevant list or item data.

Lists and Items

ListCreate and ItemCreate both extend the class CreateView . This is a generic view that can be used with any Model subclass. The Django documentation describes CreateView as follows:

A view that displays a form for creating an object, redisplaying the form with any validation errors highlighted, and eventually saving the object. (Source)

So CreateView can be a base class for any view designed to create objects.

ItemUpdate will extend the generic view class UpdateView . This is quite similar to CreateView , and you can use the same template for both. The main difference is that the ItemUpdate view will pre-populate the template form with the data from an existing ToDoItem .

The generic views know how to handle the POST request generated by the form on a successful submit action.

As always, the child classes need to be told which Model they’re based on. These models will be ToDoList and ToDoItem , respectively. The views also have a fields property that you can use to restrict which of the Model data fields are displayed to the user. For example, the ToDoItem.created_date field is completed automatically in the data model, and you probably don’t want the user to change it, so you can omit it from the fields array.

Now you need to define routes, so that the user can reach each of the new views with the appropriate data values set. Add the routes as new items in the urlpatterns array, naming them «list-add» , «item-add» , and «item-update» :

 1# todo_list/todo_app/urls.py 2from django.urls import path 3from todo_app import views 4 5urlpatterns = [ 6 path("", views.ListListView.as_view(), name="index"), 7 path("list//", views.ItemListView.as_view(), name="list"),  8 # CRUD patterns for ToDoLists  9 path("list/add/", views.ListCreate.as_view(), name="list-add"), 10 # CRUD patterns for ToDoItems 11 path( 12 "list//item/add/", 13 views.ItemCreate.as_view(), 14 name="item-add", 15 ), 16 path( 17 "list//item//", 18 views.ItemUpdate.as_view(), 19 name="item-update", 20 ), 21] 

Now you’ve associated names, URL patterns, and views with the three new routes, each of which corresponds to an action on the data.

Notice that the «item-add» and «item-update» URL patterns contain parameters, just like the «list» path. To create a new item, your view code needs to know the list_id of its parent list. To update an item, both its list_id and the item’s own ID, which is here called pk , must be known to the view.

New Views

Next, you’ll need to provide some links in your templates to activate the new views. Just before the tag in index.html , add a button:

p> input value="Add a new list" type="button" onclick="location.href=' url "list-add" %>'"/> p> 

A click on this button will now generate a request with the «list-add» pattern. If you look back at the corresponding urlpattern item in todo_app/urls.py , then you’ll see that the associated URL looks like «list/add/» , and it causes the URL dispatcher to instantiate a ListCreate view.

Now you’ll update the two dummy onclick events in todo_list.html :

 1 2 extends "base.html" %> 3 4 block content %> 5div> 6 div> 7 div> 8 h3>Edit list:h3> 9 h5> <todo_list.title | upper >>h5> 10 div> 11 ul> 12  for todo in object_list %> 13 li> 14 div> 15 div 16 role="button" 17 onclick="location.href= 18 ' url "item-update" todo_list.id todo.id %>'"> 19  <todo.title >> 20 (Due  <todo.due_date | date:"l, F j">>) 21 div> 22 div> 23 li> 24  empty %> 25 p>There are no to-do items in this list.p> 26  endfor %> 27 ul> 28 p> 29 input 30 value="Add a new item" 31 type="button" 32 onclick="location.href=' url "item-add" todo_list.id %>'" 33 /> 34 p> 35 div> 36div> 37 endblock %> 

The onclick event handlers now invoke the new URLs named «item-update» and «item-add» . Notice again the syntax in lines 18 and 32, where the urlpattern name is combined with data from context to construct hyperlinks.

For example, in lines 15 to 21, you’re setting up a button-like div element with an onclick event handler.

Notice that the «item-update» URL requires IDs for both the list and the item to be updated, whereas «item-add» required only todo_list.id .

You’ll need templates to render your new ListCreate , ItemCreate , and ItemUpdate views. The first one that you’ll tackle is the form for creating a new list. Create a new template file named todolist_form.html :

 1 2 extends "base.html" %> 3 4 block content %> 5 6h3> <title >>h3> 7div> 8 div> 9 form method="post"> 10  csrf_token %> 11  <form.as_p >> 12 input 13 value="Save" 14 type="submit"> 15 input 16 value="Cancel" 17 type="button" 18 onclick="location.href=' url "index" %>';"> 19 form> 20 div> 21div> 22 23 endblock %> 

This page contains a element in lines 9 to 19 that’ll generate a POST request when the user submits it, with the user-updated form contents as part of its payload. In this case, the form contains only the list title .

  • Line 10 uses the macro, which generates a Cross-Site Request Forgery token, a necessary precaution for modern web forms.
  • Line 11 uses the > tag to invoke the view class’s .as_p() method. This auto-generates the form contents from the fields attribute and the model structure. The form will be rendered as HTML inside a

    tag.

Next, you’ll create another form that’ll allow the user to create a new ToDoItem , or edit the details of an existing one. Add the new template todoitem_form.html :

 1 2 extends "base.html" %> 3 4 block content %> 5 6h3> <title >>h3> 7form method="post"> 8  csrf_token %> 9 table> 10  <form.as_table >> 11 table> 12 input 13 value="Submit" 14 type="submit"> 15 input 16 value="Cancel" 17 type="button" 18 onclick="location.href=' url "list" todo_list.id %>'"> 19form> 20 21 endblock %> 

This time, you’re rendering the form as a table (lines 9 to 11), because there are several fields per item. Both CreateView and UpdateView contain a .form member with convenient methods like form.as_p() and form.as_table() to perform an automatic layout. The Submit button will generate a POST request using the form’s contents. The Cancel button will redirect the user to the «list» URL, passing along the current list id as a parameter.

Run your development server again to verify that you can now create new lists and add items to those lists.

Step 7: Delete To-Do Lists and Items

You’ve written code to create and update both to-do lists and to-do items. But no CRUD application is complete without the Delete functionality. In this step, you’ll add links to the forms to allow the user to delete one item at a time, or even an entire list. Django provides generic views that handle these cases too.

To download the code for this stage of the project, click the following link and navigate to the source_code_step_7/ folder:

Get Source Code: Click here to get the source code you’ll use to build your to-do list app.

Make DeleteView Subclasses

You’ll start by adding view classes that extend django.views.generic.DeleteView . Open views.py and make sure that you have all the necessary imports:

# todo_list/todo_app/views.py from django.urls import reverse, reverse_lazy  from django.views.generic import ( ListView, CreateView, UpdateView, DeleteView, ) 

Also, add the two new view classes that support deleting objects. You’ll need one for lists and one for items:

# todo_list/todo_app/views.py class ListDelete(DeleteView): model = ToDoList # You have to use reverse_lazy() instead of reverse(), # as the urls are not loaded when the file is imported. success_url = reverse_lazy("index") class ItemDelete(DeleteView): model = ToDoItem def get_success_url(self): return reverse_lazy("list", args=[self.kwargs["list_id"]]) def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) context["todo_list"] = self.object.todo_list return context 

Both of your new classes extend django.views.generic.edit.DeleteView . The official Django documentation describes DeleteView like this:

A view that displays a confirmation page and deletes an existing object. The given object will only be deleted if the request method is POST. If this view is fetched via GET, it will display a confirmation page that should contain a form that POSTs to the same URL.(Source)

Define Deletion Confirmations and URLS

Because you’ll be offering the user the Delete option from the editing pages, you only need to create new templates for the corresponding confirmation pages. There’s even a default name for these confirmation templates: _confirm_delete.html . If such a template exists, your classes derived from DeleteView will automatically render it when the associated form is submitted.

Create a new template in the file todolist_confirm_delete.html :

  extends "base.html" %>  block content %> h3>Delete Listh3> p>Are you sure you want to delete the list i> <object.title >>i>?p> form method="POST">  csrf_token %> input value="Yes, delete." type="submit"> form> input value="Cancel" type="button" onclick="location.href=' url "index" %>';">  endblock %> 

The DeleteView subclass will still be in control when this template is rendered. By clicking the Yes, delete. button, you submit the form, and the class goes ahead and deletes the list from the database. If you click Cancel, it does nothing. In either case, Django then redirects you to the home page.

That’s it for deleting ToDoList objects. Now you can do the same for the deletion of ToDoItem objects. Create another new template, named todoitem_confirm_delete.html :

  extends "base.html" %>  block content %> h3>Delete To-do Itemh3> p>Are you sure you want to delete the item: b> <object.title >>b> from the list i> <todo_list.title >>i>?p> form method="POST">  csrf_token %> input value="Yes, delete." type="submit"> input value="Cancel" type="button" onclick="location.href=' url "list" todo_list.id %>';"> form>  endblock %> 

Exactly the same logic applies, though this time if the Cancel button is pressed, the user will be redirected to the «list» URL to display the parent list, rather than to the app’s index page.

Now you need to define routes for the deletion URLs. You can do that by adding the highlighted lines to the application’s urls.py :

 1# todo_list/todo_app/urls.py 2from django.urls import path 3from todo_app import views 4 5urlpatterns = [ 6 path("", views.ListListView.as_view(), name="index"), 7 path("list//", views.ItemListView.as_view(), name="list"), 8 # CRUD patterns for ToDoLists 9 path("list/add/", views.ListCreate.as_view(), name="list-add"), 10 path( 11 "list//delete/", views.ListDelete.as_view(), name="list-delete" 12 ), 13 # CRUD patterns for ToDoItems 14 path( 15 "list//item/add/", 16 views.ItemCreate.as_view(), 17 name="item-add", 18 ), 19 path( 20 "list//item//", 21 views.ItemUpdate.as_view(), 22 name="item-update", 23 ), 24 path( 25 "list//item//delete/", 26 views.ItemDelete.as_view(), 27 name="item-delete", 28 ), 29] 

These new URLs will load the DeleteView subclasses as views. There’s no need to define special URLs for delete confirmation because Django handles that requirement by default, rendering the confirmation page with the _confirm_delete templates that you’ve just added.

Enable Deletions

So far, you’ve created views and URLS to delete things, but there’s no mechanism for your user to invoke this functionality. You’ll fix that next.

You’ll start by adding a button to todoitem_form.html to allow the user to delete the current item. Open todoitem_form.html and add the highlighted lines:

  extends "base.html" %>  block content %> h3> <title >>h3> form method="post">  csrf_token %> table>  <form.as_table >> table> input value="Submit" type="submit"> input value="Cancel" type="button" onclick="location.href=' url "list" todo_list.id %>'">  if object %>  input  value="Delete this item"  type="button"  onclick="location.href=  ' url "item-delete" todo_list.id object.id %>'">   endif %> form>  endblock %> 

Recall that this view is used for both creating items and updating them. In the case of creation, there will be no item instance loaded in the form. So to avoid confusing the user, you need to wrap the new input element in the conditional template block … so that the Delete this item option only appears if the item already exists.

Now you need to add the user interface element for deleting an entire list. Add the highlighted lines to todolist.html :

  extends "base.html" %>  block content %> div> div> div> h3>Edit list:h3> h5> <todo_list.title | upper >>h5> div> ul>  for todo in object_list %> li> div> div role="button" onclick="location.href= ' url "item-update" todo_list.id todo.id %>'">  <todo.title >> (Due  <todo.due_date | date:"l, F j" >>) div> div> li>  empty %> p>There are no to-do items in this list.p>  endfor %> ul> p> input value="Add a new item" type="button" onclick="location.href= ' url "item-add" todo_list.id %>'" /> input  value="Delete this list"  type="button"  onclick="location.href=  ' url "list-delete" todo_list.id %>'" />  p> div> div>  endblock %> 

In this case, there’s always a ToDoList instance associated with the template, so it always makes sense to offer the Delete this list option.

Step 8: Use Your Django To-Do List App

Your project code is now complete. You can download the complete code for this project by clicking the following link and navigating to the source_code_step_final folder:

Get Source Code: Click here to get the source code you’ll use to build your to-do list app.

You’ve built the entire to-do list application. That means you’re ready to put the whole app through its paces!

One more time, fire up your development server. If the console displays errors, then you’ll have to resolve them before continuing. Otherwise, use your browser to navigate to http://localhost:8000/ . If all is well, you should be greeted by the home page of your application:

To-do list home page before adding lists

The app heading Django To-do Lists will appear on every page. It serves as a link back to the home page, allowing the user to return there from anywhere in the application.

There may be some data already in the app, depending on your previous testing. Now you can start exercising the app logic.

  • Click on Add a new list. A new screen appears, offering a blank text box for the new list’s title.
  • Give your new list a name and press Save. You’re taken to the Edit List page, with the message There are no to-do items in this list.
  • Click on Add a new item. The Create a new item form appears. Fill in a title and a description, and notice that the default due date is exactly one week ahead. You can change it if you like.

The form allows you to fill in and edit all the relevant fields of the to-do item. Here’s how this might look:

Django form for creating a new to-do list item

Click again on Django To-do Lists to return to the home page. Now you can continue to test the app’s navigation and functionality by adding more lists, adding more items to lists, modifying item details, and deleting items and lists.

Here’s an example of how your home page might look after you’ve added a couple of lists:

List of to-do lists

And this is how one of those lists might look after you click on its link:

List of to-do items

If something doesn’t work, try to use the explanations above, along with the step-by-step code downloads, to figure out what might be wrong. Or if you still can’t solve it, you can download the full project code and check where yours is different. Click on the link below and navigate to the source_code_final/ folder:

Get Source Code: Click here to get the source code you’ll use to build your to-do list app.

If you were able to interact with your app as described here, then you can feel confident that you’ve built a functioning application. Congratulations!

Conclusion

So now you’ve coded a full-fledged, database-backed Django web application from scratch.

Along the way, you’ve learned about web applications, as well as Django and its architecture. You’ve applied modern object-oriented principles and inheritance to achieve code reuse and improve maintainability.

In this tutorial, you’ve learned how to:

  • Use Django to create a web app
  • Structure a data model with one-to-many relationships
  • Explore your data model and add test data through the Django admin interface
  • Display your lists by coding templates
  • Handle the standard database operations through class-based views
  • Create URL configurations to control the Django URL dispatcher and route requests to the proper views

Django’s class-based views are designed to help you to bootstrap an application in minimum time. They cover the majority of use cases in their unmodified form. But because they’re class-based, they’re not restrictive. You can change almost anything you might want to change about view behavior.

Class-based views aren’t the only way to go. For a basic application, you may find that the older function-based view approach is simpler and easier to understand. And even if you decide that classes are best, you’re not tied to using Django’s generic views. You can plug any function or method that fulfills the request-response contract into your application, and you’ll continue to benefit from Django’s rich infrastructure and ecosystem.

With the experience that you’ve gained in this tutorial, you know how to handle the nuts and bolts of building a web app. It’s up to you to apply these skills to your next creative idea!

Next Steps

Now that you’ve completed this tutorial, it’s time to think about where you can go from here. By further developing this app, you can build your knowledge and consolidate your skills while creating an attractive addition to your programming portfolio. Here are a few suggestions for what you might do next:

  • Style the application: The to-do list application’s user interface (UI) is unapologetically bland and unadorned. Simple.css has helped you to create an acceptable UI, but nothing more than that. You might want to investigate some ways to improve it:
    • The Simple.css website has links to several sites that have built on the base CSS from Simple.css to produce something more attractive.
    • There are many other free CSS libraries out there! For instance, you could investigate Bootstrap, Semantic UI, or Font Awesome.
    • You can also use raw CSS. CSS is an extremely powerful styling tool, and if you’re willing to invest the time to learn its ins and outs, there’s no limit to what you can make it do.
    • Get Started with Django Part 1: Build a Portfolio App
    • Make a Location-Based Web App with Django and GeoDjango
    • Customize the Django Admin with Python
    • Build a Social Network With Django

    Mark as Completed

    Get a short & sweet Python Trick delivered to your inbox every couple of days. No spam ever. Unsubscribe any time. Curated by the Real Python team.

    Python Tricks Dictionary Merge

    About Charles de Villiers

    Charles teaches Physics and Math. When he isn’t teaching or coding, he spends way too much time playing online chess.

    Each tutorial at Real Python is created by a team of developers so that it meets our high quality standards. The team members who worked on this tutorial are:

    Aldren Santos

    Geir Arne Hjelle

    Kate Finegan

    Martin Breuss

    Sadie Parker

    Master Real-World Python Skills With Unlimited Access to Real Python

    Join us and get access to thousands of tutorials, hands-on video courses, and a community of expert Pythonistas:

    Master Real-World Python Skills
    With Unlimited Access to Real Python

    Join us and get access to thousands of tutorials, hands-on video courses, and a community of expert Pythonistas:

    What Do You Think?

    Rate this article:

    What’s your #1 takeaway or favorite thing you learned? How are you going to put your newfound skills to use? Leave a comment below and let us know.

    Commenting Tips: The most useful comments are those written with the goal of learning from or helping out other students. Get tips for asking good questions and get answers to common questions in our support portal. Looking for a real-time conversation? Visit the Real Python Community Chat or join the next “Office Hours” Live Q&A Session. Happy Pythoning!

    5. Data Structures¶

    This chapter describes some things you’ve learned about already in more detail, and adds some new things as well.

    5.1. More on Lists¶

    The list data type has some more methods. Here are all of the methods of list objects:

    Add an item to the end of the list. Equivalent to a[len(a):] = [x] .

    list. extend ( iterable )

    Extend the list by appending all the items from the iterable. Equivalent to a[len(a):] = iterable .

    Insert an item at a given position. The first argument is the index of the element before which to insert, so a.insert(0, x) inserts at the front of the list, and a.insert(len(a), x) is equivalent to a.append(x) .

    Remove the first item from the list whose value is equal to x. It raises a ValueError if there is no such item.

    Remove the item at the given position in the list, and return it. If no index is specified, a.pop() removes and returns the last item in the list. (The square brackets around the i in the method signature denote that the parameter is optional, not that you should type square brackets at that position. You will see this notation frequently in the Python Library Reference.)

    Remove all items from the list. Equivalent to del a[:] .

    Return zero-based index in the list of the first item whose value is equal to x. Raises a ValueError if there is no such item.

    The optional arguments start and end are interpreted as in the slice notation and are used to limit the search to a particular subsequence of the list. The returned index is computed relative to the beginning of the full sequence rather than the start argument.

    Return the number of times x appears in the list.

    list. sort ( * , key = None , reverse = False )

    Sort the items of the list in place (the arguments can be used for sort customization, see sorted() for their explanation).

    Reverse the elements of the list in place.

    Return a shallow copy of the list. Equivalent to a[:] .

    An example that uses most of the list methods:

    >>> fruits = ['orange', 'apple', 'pear', 'banana', 'kiwi', 'apple', 'banana'] >>> fruits.count('apple') 2 >>> fruits.count('tangerine') 0 >>> fruits.index('banana') 3 >>> fruits.index('banana', 4) # Find next banana starting at position 4 6 >>> fruits.reverse() >>> fruits ['banana', 'apple', 'kiwi', 'banana', 'pear', 'apple', 'orange'] >>> fruits.append('grape') >>> fruits ['banana', 'apple', 'kiwi', 'banana', 'pear', 'apple', 'orange', 'grape'] >>> fruits.sort() >>> fruits ['apple', 'apple', 'banana', 'banana', 'grape', 'kiwi', 'orange', 'pear'] >>> fruits.pop() 'pear' 

    You might have noticed that methods like insert , remove or sort that only modify the list have no return value printed – they return the default None . 1 This is a design principle for all mutable data structures in Python.

    Another thing you might notice is that not all data can be sorted or compared. For instance, [None, ‘hello’, 10] doesn’t sort because integers can’t be compared to strings and None can’t be compared to other types. Also, there are some types that don’t have a defined ordering relation. For example, 3+4j < 5+7j isn’t a valid comparison.

    5.1.1. Using Lists as Stacks¶

    The list methods make it very easy to use a list as a stack, where the last element added is the first element retrieved (“last-in, first-out”). To add an item to the top of the stack, use append() . To retrieve an item from the top of the stack, use pop() without an explicit index. For example:

    >>> stack = [3, 4, 5] >>> stack.append(6) >>> stack.append(7) >>> stack [3, 4, 5, 6, 7] >>> stack.pop() 7 >>> stack [3, 4, 5, 6] >>> stack.pop() 6 >>> stack.pop() 5 >>> stack [3, 4] 

    5.1.2. Using Lists as Queues¶

    It is also possible to use a list as a queue, where the first element added is the first element retrieved (“first-in, first-out”); however, lists are not efficient for this purpose. While appends and pops from the end of list are fast, doing inserts or pops from the beginning of a list is slow (because all of the other elements have to be shifted by one).

    To implement a queue, use collections.deque which was designed to have fast appends and pops from both ends. For example:

    >>> from collections import deque >>> queue = deque(["Eric", "John", "Michael"]) >>> queue.append("Terry") # Terry arrives >>> queue.append("Graham") # Graham arrives >>> queue.popleft() # The first to arrive now leaves 'Eric' >>> queue.popleft() # The second to arrive now leaves 'John' >>> queue # Remaining queue in order of arrival deque(['Michael', 'Terry', 'Graham']) 

    5.1.3. List Comprehensions¶

    List comprehensions provide a concise way to create lists. Common applications are to make new lists where each element is the result of some operations applied to each member of another sequence or iterable, or to create a subsequence of those elements that satisfy a certain condition.

    For example, assume we want to create a list of squares, like:

    >>> squares = [] >>> for x in range(10): . squares.append(x**2) . >>> squares [0, 1, 4, 9, 16, 25, 36, 49, 64, 81] 

    Note that this creates (or overwrites) a variable named x that still exists after the loop completes. We can calculate the list of squares without any side effects using:

    squares = list(map(lambda x: x**2, range(10))) 
    squares = [x**2 for x in range(10)] 

    which is more concise and readable.

    A list comprehension consists of brackets containing an expression followed by a for clause, then zero or more for or if clauses. The result will be a new list resulting from evaluating the expression in the context of the for and if clauses which follow it. For example, this listcomp combines the elements of two lists if they are not equal:

    >>> [(x, y) for x in [1,2,3] for y in [3,1,4] if x != y] [(1, 3), (1, 4), (2, 3), (2, 1), (2, 4), (3, 1), (3, 4)] 

    and it’s equivalent to:

    >>> combs = [] >>> for x in [1,2,3]: . for y in [3,1,4]: . if x != y: . combs.append((x, y)) . >>> combs [(1, 3), (1, 4), (2, 3), (2, 1), (2, 4), (3, 1), (3, 4)] 

    Note how the order of the for and if statements is the same in both these snippets.

    If the expression is a tuple (e.g. the (x, y) in the previous example), it must be parenthesized.

    >>> vec = [-4, -2, 0, 2, 4] >>> # create a new list with the values doubled >>> [x*2 for x in vec] [-8, -4, 0, 4, 8] >>> # filter the list to exclude negative numbers >>> [x for x in vec if x >= 0] [0, 2, 4] >>> # apply a function to all the elements >>> [abs(x) for x in vec] [4, 2, 0, 2, 4] >>> # call a method on each element >>> freshfruit = [' banana', ' loganberry ', 'passion fruit '] >>> [weapon.strip() for weapon in freshfruit] ['banana', 'loganberry', 'passion fruit'] >>> # create a list of 2-tuples like (number, square) >>> [(x, x**2) for x in range(6)] [(0, 0), (1, 1), (2, 4), (3, 9), (4, 16), (5, 25)] >>> # the tuple must be parenthesized, otherwise an error is raised >>> [x, x**2 for x in range(6)] File "", line 1 [x, x**2 for x in range(6)] ^^^^^^^ SyntaxError: did you forget parentheses around the comprehension target? >>> # flatten a list using a listcomp with two 'for' >>> vec = [[1,2,3], [4,5,6], [7,8,9]] >>> [num for elem in vec for num in elem] [1, 2, 3, 4, 5, 6, 7, 8, 9] 

    List comprehensions can contain complex expressions and nested functions:

    >>> from math import pi >>> [str(round(pi, i)) for i in range(1, 6)] ['3.1', '3.14', '3.142', '3.1416', '3.14159'] 

    5.1.4. Nested List Comprehensions¶

    The initial expression in a list comprehension can be any arbitrary expression, including another list comprehension.

    Consider the following example of a 3×4 matrix implemented as a list of 3 lists of length 4:

    >>> matrix = [ . [1, 2, 3, 4], . [5, 6, 7, 8], . [9, 10, 11, 12], . ] 

    The following list comprehension will transpose rows and columns:

    >>> [[row[i] for row in matrix] for i in range(4)] [[1, 5, 9], [2, 6, 10], [3, 7, 11], [4, 8, 12]] 

    As we saw in the previous section, the inner list comprehension is evaluated in the context of the for that follows it, so this example is equivalent to:

    >>> transposed = [] >>> for i in range(4): . transposed.append([row[i] for row in matrix]) . >>> transposed [[1, 5, 9], [2, 6, 10], [3, 7, 11], [4, 8, 12]] 

    which, in turn, is the same as:

    >>> transposed = [] >>> for i in range(4): . # the following 3 lines implement the nested listcomp . transposed_row = [] . for row in matrix: . transposed_row.append(row[i]) . transposed.append(transposed_row) . >>> transposed [[1, 5, 9], [2, 6, 10], [3, 7, 11], [4, 8, 12]] 

    In the real world, you should prefer built-in functions to complex flow statements. The zip() function would do a great job for this use case:

    >>> list(zip(*matrix)) [(1, 5, 9), (2, 6, 10), (3, 7, 11), (4, 8, 12)] 

    See Unpacking Argument Lists for details on the asterisk in this line.

    5.2. The del statement¶

    There is a way to remove an item from a list given its index instead of its value: the del statement. This differs from the pop() method which returns a value. The del statement can also be used to remove slices from a list or clear the entire list (which we did earlier by assignment of an empty list to the slice). For example:

    >>> a = [-1, 1, 66.25, 333, 333, 1234.5] >>> del a[0] >>> a [1, 66.25, 333, 333, 1234.5] >>> del a[2:4] >>> a [1, 66.25, 1234.5] >>> del a[:] >>> a [] 

    del can also be used to delete entire variables:

    >>> del a 

    Referencing the name a hereafter is an error (at least until another value is assigned to it). We’ll find other uses for del later.

    5.3. Tuples and Sequences¶

    We saw that lists and strings have many common properties, such as indexing and slicing operations. They are two examples of sequence data types (see Sequence Types — list, tuple, range ). Since Python is an evolving language, other sequence data types may be added. There is also another standard sequence data type: the tuple.

    A tuple consists of a number of values separated by commas, for instance:

    >>> t = 12345, 54321, 'hello!' >>> t[0] 12345 >>> t (12345, 54321, 'hello!') >>> # Tuples may be nested: . u = t, (1, 2, 3, 4, 5) >>> u ((12345, 54321, 'hello!'), (1, 2, 3, 4, 5)) >>> # Tuples are immutable: . t[0] = 88888 Traceback (most recent call last): File "", line 1, in TypeError: 'tuple' object does not support item assignment >>> # but they can contain mutable objects: . v = ([1, 2, 3], [3, 2, 1]) >>> v ([1, 2, 3], [3, 2, 1]) 

    As you see, on output tuples are always enclosed in parentheses, so that nested tuples are interpreted correctly; they may be input with or without surrounding parentheses, although often parentheses are necessary anyway (if the tuple is part of a larger expression). It is not possible to assign to the individual items of a tuple, however it is possible to create tuples which contain mutable objects, such as lists.

    Though tuples may seem similar to lists, they are often used in different situations and for different purposes. Tuples are immutable , and usually contain a heterogeneous sequence of elements that are accessed via unpacking (see later in this section) or indexing (or even by attribute in the case of namedtuples ). Lists are mutable , and their elements are usually homogeneous and are accessed by iterating over the list.

    A special problem is the construction of tuples containing 0 or 1 items: the syntax has some extra quirks to accommodate these. Empty tuples are constructed by an empty pair of parentheses; a tuple with one item is constructed by following a value with a comma (it is not sufficient to enclose a single value in parentheses). Ugly, but effective. For example:

    >>> empty = () >>> singleton = 'hello', # >>> len(empty) 0 >>> len(singleton) 1 >>> singleton ('hello',) 

    The statement t = 12345, 54321, ‘hello!’ is an example of tuple packing: the values 12345 , 54321 and ‘hello!’ are packed together in a tuple. The reverse operation is also possible:

    >>> x, y, z = t 

    This is called, appropriately enough, sequence unpacking and works for any sequence on the right-hand side. Sequence unpacking requires that there are as many variables on the left side of the equals sign as there are elements in the sequence. Note that multiple assignment is really just a combination of tuple packing and sequence unpacking.

    5.4. Sets¶

    Python also includes a data type for sets. A set is an unordered collection with no duplicate elements. Basic uses include membership testing and eliminating duplicate entries. Set objects also support mathematical operations like union, intersection, difference, and symmetric difference.

    Curly braces or the set() function can be used to create sets. Note: to create an empty set you have to use set() , not <> ; the latter creates an empty dictionary, a data structure that we discuss in the next section.

    Here is a brief demonstration:

    >>> basket = ‘apple’, ‘orange’, ‘apple’, ‘pear’, ‘orange’, ‘banana’> >>> print(basket) # show that duplicates have been removed >>> ‘orange’ in basket # fast membership testing True >>> ‘crabgrass’ in basket False >>> # Demonstrate set operations on unique letters from two words . >>> a = set(‘abracadabra’) >>> b = set(‘alacazam’) >>> a # unique letters in a >>> a b # letters in a but not in b >>> a | b # letters in a or b or both >>> a & b # letters in both a and b >>> a ^ b # letters in a or b but not both

    Similarly to list comprehensions , set comprehensions are also supported:

    >>> a = x for x in ‘abracadabra’ if x not in ‘abc’> >>> a

    5.5. Dictionaries¶

    Another useful data type built into Python is the dictionary (see Mapping Types — dict ). Dictionaries are sometimes found in other languages as “associative memories” or “associative arrays”. Unlike sequences, which are indexed by a range of numbers, dictionaries are indexed by keys, which can be any immutable type; strings and numbers can always be keys. Tuples can be used as keys if they contain only strings, numbers, or tuples; if a tuple contains any mutable object either directly or indirectly, it cannot be used as a key. You can’t use lists as keys, since lists can be modified in place using index assignments, slice assignments, or methods like append() and extend() .

    It is best to think of a dictionary as a set of key: value pairs, with the requirement that the keys are unique (within one dictionary). A pair of braces creates an empty dictionary: <> . Placing a comma-separated list of key:value pairs within the braces adds initial key:value pairs to the dictionary; this is also the way dictionaries are written on output.

    The main operations on a dictionary are storing a value with some key and extracting the value given the key. It is also possible to delete a key:value pair with del . If you store using a key that is already in use, the old value associated with that key is forgotten. It is an error to extract a value using a non-existent key.

    Performing list(d) on a dictionary returns a list of all the keys used in the dictionary, in insertion order (if you want it sorted, just use sorted(d) instead). To check whether a single key is in the dictionary, use the in keyword.

    Here is a small example using a dictionary:

    >>> tel = 'jack': 4098, 'sape': 4139> >>> tel['guido'] = 4127 >>> tel >>> tel['jack'] 4098 >>> del tel['sape'] >>> tel['irv'] = 4127 >>> tel >>> list(tel) ['jack', 'guido', 'irv'] >>> sorted(tel) ['guido', 'irv', 'jack'] >>> 'guido' in tel True >>> 'jack' not in tel False 

    The dict() constructor builds dictionaries directly from sequences of key-value pairs:

    >>> dict([(‘sape’, 4139), (‘guido’, 4127), (‘jack’, 4098)])

    In addition, dict comprehensions can be used to create dictionaries from arbitrary key and value expressions:

    >>> x: x**2 for x in (2, 4, 6)>

    When the keys are simple strings, it is sometimes easier to specify pairs using keyword arguments:

    >>> dict(sape=4139, guido=4127, jack=4098)

    5.6. Looping Techniques¶

    When looping through dictionaries, the key and corresponding value can be retrieved at the same time using the items() method.

    >>> knights = 'gallahad': 'the pure', 'robin': 'the brave'> >>> for k, v in knights.items(): . print(k, v) . gallahad the pure robin the brave 

    When looping through a sequence, the position index and corresponding value can be retrieved at the same time using the enumerate() function.

    >>> for i, v in enumerate(['tic', 'tac', 'toe']): . print(i, v) . 0 tic 1 tac 2 toe 

    To loop over two or more sequences at the same time, the entries can be paired with the zip() function.

    >>> questions = ['name', 'quest', 'favorite color'] >>> answers = ['lancelot', 'the holy grail', 'blue'] >>> for q, a in zip(questions, answers): . print('What is your ? It is .'.format(q, a)) . What is your name? It is lancelot. What is your quest? It is the holy grail. What is your favorite color? It is blue. 

    To loop over a sequence in reverse, first specify the sequence in a forward direction and then call the reversed() function.

    >>> for i in reversed(range(1, 10, 2)): . print(i) . 9 7 5 3 1 

    To loop over a sequence in sorted order, use the sorted() function which returns a new sorted list while leaving the source unaltered.

    >>> basket = ['apple', 'orange', 'apple', 'pear', 'orange', 'banana'] >>> for i in sorted(basket): . print(i) . apple apple banana orange orange pear 

    Using set() on a sequence eliminates duplicate elements. The use of sorted() in combination with set() over a sequence is an idiomatic way to loop over unique elements of the sequence in sorted order.

    >>> basket = ['apple', 'orange', 'apple', 'pear', 'orange', 'banana'] >>> for f in sorted(set(basket)): . print(f) . apple banana orange pear 

    It is sometimes tempting to change a list while you are looping over it; however, it is often simpler and safer to create a new list instead.

    >>> import math >>> raw_data = [56.2, float('NaN'), 51.7, 55.3, 52.5, float('NaN'), 47.8] >>> filtered_data = [] >>> for value in raw_data: . if not math.isnan(value): . filtered_data.append(value) . >>> filtered_data [56.2, 51.7, 55.3, 52.5, 47.8] 

    5.7. More on Conditions¶

    The conditions used in while and if statements can contain any operators, not just comparisons.

    The comparison operators in and not in are membership tests that determine whether a value is in (or not in) a container. The operators is and is not compare whether two objects are really the same object. All comparison operators have the same priority, which is lower than that of all numerical operators.

    Comparisons may be combined using the Boolean operators and and or , and the outcome of a comparison (or of any other Boolean expression) may be negated with not . These have lower priorities than comparison operators; between them, not has the highest priority and or the lowest, so that A and not B or C is equivalent to (A and (not B)) or C . As always, parentheses can be used to express the desired composition.

    The Boolean operators and and or are so-called short-circuit operators: their arguments are evaluated from left to right, and evaluation stops as soon as the outcome is determined. For example, if A and C are true but B is false, A and B and C does not evaluate the expression C . When used as a general value and not as a Boolean, the return value of a short-circuit operator is the last evaluated argument.

    It is possible to assign the result of a comparison or other Boolean expression to a variable. For example,

    >>> string1, string2, string3 = '', 'Trondheim', 'Hammer Dance' >>> non_null = string1 or string2 or string3 >>> non_null 'Trondheim' 

    Note that in Python, unlike C, assignment inside expressions must be done explicitly with the walrus operator := . This avoids a common class of problems encountered in C programs: typing = in an expression when == was intended.

    5.8. Comparing Sequences and Other Types¶

    Sequence objects typically may be compared to other objects with the same sequence type. The comparison uses lexicographical ordering: first the first two items are compared, and if they differ this determines the outcome of the comparison; if they are equal, the next two items are compared, and so on, until either sequence is exhausted. If two items to be compared are themselves sequences of the same type, the lexicographical comparison is carried out recursively. If all items of two sequences compare equal, the sequences are considered equal. If one sequence is an initial sub-sequence of the other, the shorter sequence is the smaller (lesser) one. Lexicographical ordering for strings uses the Unicode code point number to order individual characters. Some examples of comparisons between sequences of the same type:

    (1, 2, 3)  (1, 2, 4) [1, 2, 3]  [1, 2, 4] 'ABC'  'C'  'Pascal'  'Python' (1, 2, 3, 4)  (1, 2, 4) (1, 2)  (1, 2, -1) (1, 2, 3) == (1.0, 2.0, 3.0) (1, 2, ('aa', 'ab'))  (1, 2, ('abc', 'a'), 4) 

    Note that comparing objects of different types with < or >is legal provided that the objects have appropriate comparison methods. For example, mixed numeric types are compared according to their numeric value, so 0 equals 0.0, etc. Otherwise, rather than providing an arbitrary ordering, the interpreter will raise a TypeError exception.

    Other languages may return the mutated object, which allows method chaining, such as d->insert(«a»)->remove(«b»)->sort(); .

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *