17.02.2020       Выпуск 322 (17.02.2020 - 23.02.2020)       Статьи

Создаем Todo приложение c помощью Django. Часть 1

Джанго это мощный фреймворк для создания веб-приложений. Изначально Django был создан для того, чтобы быстро создавать, например, новостные сайты (или другие сайты, который нужно создавать максимально быстро). И после нативного PHP не покидает ощущение, что ты едешь на очень быстрой машине разработки. Чтобы посмотреть все его возможности для быстрой разработки, мы с вами попробуем создать простое Todo — приложение.

Читать>>




Экспериментальная функция:

Ниже вы видите текст статьи по ссылке. По нему можно быстро понять ссылка достойна прочтения или нет

Просим обратить внимание, что текст по ссылке и здесь может не совпадать.

И снова здравствуйте. В преддверии старта курса «Web-разработчик на Python» наш внештатный автор подготовил интересный материал, которым с радостью делимся с вами.

Джанго это мощный фреймворк для создания веб-приложений. Изначально Django был создан для того, чтобы быстро создавать, например, новостные сайты (или другие сайты, который нужно создавать максимально быстро). И после нативного PHP не покидает ощущение, что ты едешь на очень быстрой машине разработки. Чтобы посмотреть все его возможности для быстрой разработки, мы с вами попробуем создать простое Todo — приложение.

Начнем с формулировки краткого т.з. У нас будет небольшое веб-приложение с версткой на Bulma (да, я очень люблю Bulma. Возможно, когда-нибудь я сверну на Bootstrap или Picnic, но всему свое время). У нас (пока) нет авторизаций и пользователь может создавать, редактировать и удалять либо категорию дел, либо карточку todo, которая связана с какой-либо категорией, которую создал пользователь. Todo карточку или категорию можно удалить, поставив галочку на checkbox и нажав кнопку удалить.

Основные концепции Django

Немного поговорим о Django. Django реализует архитектурный паттерн MVT (Модель Представление Шаблон), которая немного отличается от знакомого большинству MVC (Модель Представление Контроллер) на котором работает Ruby on Rails и Laravel.

Модель (Model)

Модель в Django описывает схему данных в БД. С Django ORM, вы можете самостоятельно описывать поля и любые другие типы данных, и совершать миграции для упрощения разработки.

Представление (View)

В представлении в Django вы задаете основную логику и алгоритмы приложения, получаете различные данные из базы данных или манипулируете ими. Представление обычно базируется на функциях request\response. Response представляет из себя обычно HTTP redirect, HTTP error(404), MimeTypes или какой-либо шаблон.

Шаблон

Шаблон в Django это простой HTML код со специальным шаблонным языком Django. DTL (Django Template Language) — это язык, с помощью которого вы можете динамически менять содержимое страницы (к примеру, изменять имя пользователя на странице, в зависимости от того, как зовут авторизовавшегося пользователя).

Настройки

Файл настроек в Django, в котором находятся все настройки вашего веб-приложения. Он включает в себя секретный ключ, папки с шаблонами, middlewares (которые отвечают, например за то, чтобы ваши приватные альбомы не увидели другие пользователи), подключение к базе данных, и много всего остального.

Url

Файл настройки роутинга — примерно то же самое, что и в Angular или Laravel. Это связывает представление с url запросами.

Страница Админа

Так как Django изначально был спроектирован для быстрого прототипирования и развертывания новостных сайтов, админка включена в комплект по умолчанию.

Установка Python и Django

Творческое отступление

Автор этой статьи (то есть я) кроме написания статей занимается еще и обучением основам питона детей. По долгу службы я ставил Python и его пакеты на достаточно большое количество разнообразных машин. И кроме переустановок питона, один раз для установки пакета глобально сработал даже даунгрейд версии языка. Да, так тоже бывает.

Версии Python

До последнего времени активно поддерживались и развивались две основные ветки Python: 2.7 и 3.x. Я буду использовать версию 3.7.3 в данной статье, но на самом деле это не так важно. Если вы правда хотите знать разницу между ними, есть специальная

вики

. С другой стороны, сейчас уже нет никакого смысла использовать Python версии 2.7 — обновление языка остановилось на 2.7.17 (если я правильно понимаю документацию на официальном сайте). Это означает, что есть смысл переводить проекты написанные на Python 2.7.x на новую ветку, а вот новые писать на 2 версии совсем бессмысленно.

Инсталляция Python

Если вы работаете на Mac или Ubuntu — у вас уже скорее всего установлен Python, однако 2 версии. Python третьей версии придется скачивать отдельно, и вызывать его в командной строке вы сможете через python3. В любом случае, лучше всего скачать последний релиз

здесь

.

Создание своего виртуального окружения

На самом деле первое приложение на Django вы можете начать разрабатывать и не создавая свое виртуальное окружение, однако навык создания виртуального окружения может пригодится если вы, например, разрабатываете приложение с определенной версией библиотеки и не хотите устанавливать библиотеки глобально и замусоривать ваш system.

Так как же использовать virtual env?

1) Самый простой вариант. Вы можете скачать замечательный IDE от JET BRAINS PyCharm Community Edition

отсюда

. После установки PyCharm создайте новый проект, и Pycharm по умолчанию предложит вам создать Virtual Env, в котором будет возможность установить нужную версию Django (или по умолчанию последнюю, которая на момент написания данной статьи 3.0.2):

pip3 install django

2) Чуть более хардкорный вариант:

А что, если вы хотите запустить Django в virtual env, к примеру, в любимой папке?

Во, первых, создаем папку, в которой мы будет творить:

	mkdir myfirstdjango && cd myfirstdjango

Дальше вводим следующие команды для активации venv, где django_env имя нашего виртуального окружения:

	python3 -m venv django_env
	source django_env/bin/activate

Далее наше виртуальное окружение активировалось. Можем поставить необходимые пакеты. В нашем случае это Django:

	pip3 install django

Если вы захотите выключить виртуальное окружение, чтобы вернуться в ваш глобальный python (вернуться в контекст system), введите следующую команду:

    deactivate

Хорошо, надеюсь с виртуальным окружением мы разобрались. Если возникли какие-то проблемы и дополнительные вопросы, полезные ссылочки можно найти

здесь

и

здесь

.

Создание самого проекта

Допустим вы выбрали какой-то из способов создания своего виртуального окружения (или даже делаете все глобально, что же, никто не запрещает вам это делать). Теперь проходим в папку проекта и начинаем его создание:

    django-admin startproject todo  #создаем корневую папку вашего проекта
    cd todo #проходим в нее
    python manage.py startapp todolist  #как бы подприложение todolist
	python3 manage.py runserver 8100 #поднимаем сервер на нашем любимом незанятом порте, 8000 по умолчанию
 

Так, после того как Django открыл стартовую страницу, необходимо проинсталлировать наше приложение todolist в основное приложение. Открываем

settings.py

и добавляем в уже имеющийся список приложений наш собственный todolist:

INSTALLED_APPS = [
	#предустановленные админки, аутентификации и остальное из коробки, не вижу смысла здесь перечислять
    'todolist',
]

Следующим шагом будет связывание приложения с базой данных. Если базы данных — это не то, с чем вы хотите возиться, вам стоит воспользоваться решением по умолчанию — SQlite. Я же решил воспользоваться PostgreSQL — она популярна и классически связана с Django, кроме того, потом мы можем захотеть увеличить производительность приложения. Инструкций как устанавливать PostgreSQL на все операционные системы достаточно много. Я разрабатываю под MacOS и не без небольших танцев с бубном я поставил эту базу данных, скачав сам Postgres.app

отсюда

. Что касается интерфейсов для БД, то здесь я воспользовался

Postico

и пробной версии для разработки приложения нам вполне хватит (хотя в принципе можно обойтись и без неё, потому что все наше взаимодействие с базой данных будет построено через само веб-приложение и миграции). Кроме того, пришлось поставить

psycopg2

в виртуальное окружение проекта (без этого драйвера с БД ваше приложение работать не будет).

Дальше нужно настроить работу статики. По-прежнему редактируем файл

settings.py

, теперь в самом конце добавляем работу со статикой:

STATIC_URL = '/static/'
PROJECT_ROOT = os.path.dirname(os.path.abspath(__file__))
STATIC_ROOT = os.path.join(PROJECT_ROOT, 'static')
STATICFILES_STORAGE = 'whitenoise.django.GzipManifestStaticFilesStorage'

Для того, чтобы у вас заработала статика, проверьте что в списке

INSTALLED_APPS

находился пакет, отвечающий за статику:

django.contrib.staticfiles,

на тот случай, если произойдет ошибка.

И последнее в подготовительных работах, нам нужно ещё настроить базовую работу url в проекте:

	from django.conf.urls import url
	from django.contrib import admin
	from todolist.views import todo
	from todolist.views import category
	from todolist.views import redirect_view
 
urlpatterns = [
	url(r'$^', redirect_view ),
	url(r'^admin/', admin.site.urls),
	url(r'^todo/', todo, name="TodoList"),
	url(r'^category/', category, name="Category"),
]

Я добавил редирект так как хочу, чтобы с дефолтной страницы

localhost

сразу переходил на подстраницу

category

(чтобы не дай бог пользователь не потерялся). Также у нас есть роутинг на две страницы: категорий и дел.

Итак, надеюсь ваше приложение не упало. Далее мы можем наконец-то переходить к созданию самого приложения:

Создание модели Todo и Категорий

Дальше приступим к созданию модели, которая будет базово взаимодействовать с нашей базой данных. Для создания модели открываем файл

models.py

в нашем todolist и начинаем творить. Начнем с того, что создадим таблицу категорий:

from django.utils import timezone #мы будем получать дату создания todo
from django.db import models
 
class Category(models.Model): # Таблица категория которая наследует models.Model
    name = models.CharField(max_length=100) #varchar.Нам потребуется только имя категории
	class Meta:
    	verbose_name = ("Category") # человекочитаемое имя объекта
    	verbose_name_plural = ("Categories")  #человекочитаемое множественное имя для Категорий
    def __str__(self):
        return self.name  # __str__ применяется для отображения объекта в интерфейсе

Отлично! Да, здесь у нас будет только две колонки в таблице Категорий: id и name. Дальше создадим таблицу для наших дел. Думаю, из комментариев все понятно:

class TodoList(models.Model):
	title = models.CharField(max_length=250)
	content = models.TextField(blank=True) #текстовое поле
	created = models.DateField(default=timezone.now().strftime("%Y-%m-%d")) # дата создания
	due_date = models.DateField(default=timezone.now().strftime("%Y-%m-%d")) #до какой даты нужно было сделать дело
	category = models.ForeignKey(Category, default="general",on_delete=models.PROTECT) # foreignkey с помощью которой мы будем осуществлять связь с таблицей Категорий
	class Meta: #используем вспомогательный класс мета для сортировки наших дел
        ordering = ["-created"] #сортировка дел по времени их создания
	def __str__(self):
    	return self.title

После того, как ваша модель будет готова, необходимо создать миграции:

	python3 manage.py makemigrations

И потом запускаете сами миграции:

	python3 manage.py migrate

Создание view

Откроем файл

view.py

в todolist и отредактируем его. Для начала добавим необходимые импорты и редирект с главной на category:

from django.shortcuts import render, redirect #для отображения и редиректа берем необходимые классы
from django.http import HttpResponse
from .models import TodoList, Category #не забываем наши модели
 
def redirect_view(request):
	return redirect("/category") # редирект с главной на категории

Потом начинаем создание нашего дела. У экземпляра дела будут поля самого текста, даты, до которой должно быть закончено дело, категория дела, и объединенный контент:

def todo(request):
	todos = TodoList.objects.all() #запрашиваем все объекты todo через менеджер объектов
    categories = Category.objects.all() #так же получаем все Категории

После этого добавим функции добавления и удаления дел:

if request.method == "POST": #проверяем то что метод именно POST
	if "Add" in request.POST: #проверяем метод добавления todo
    	title = request.POST["description"] #сам текст
    	date = str(request.POST["date"]) #дата, до которой должно быть закончено дело
        category = request.POST["category_select"] #категория, которой может выбрать или создать пользователь.
        content = title + " -- " + date + " " + category # полный склеенный контент
    	Todo = TodoList(title=title, content=content, due_date=date, category=Category.objects.get(name=category))
    	Todo.save() # сохранение нашего дела
        return redirect("/todo") # перегрузка страницы (ну вот так у нас будет устроено очищение формы)
    if "Delete" in request.POST: #если пользователь собирается удалить одно дело
        checkedlist = request.POST.getlist('checkedbox') # берем список выделенные дел, которые мы собираемся удалить
        for i in range(len(checkedlist)): #мне почему-то не нравится эта конструкция
            todo = TodoList.objects.filter(id=int(checkedlist[i]))
        	todo.delete() #удаление дела
return render(request, "todo.html", {"todos": todos, "categories": categories})

С тудушками все. Дальше можем перейти к странице Категорий. Создаем функцию категорий, в которой у нас тоже будет функция добавления и удаления категории. Принципиально здесь ничего нового не будет, у нас так же здесь будет возможность добавления и удаления:

def category(request):
	categories = Category.objects.all()  #запрашиваем все объекты Категорий
	if request.method == "POST": #проверяем что это метод POST
    	if "Add" in request.POST: #если собираемся добавить
        	name = request.POST["name"] #имя нашей категории
        	category = Category(name=name) #у нашей категории есть только имя
            category.save() # сохранение нашей категории
        	return redirect("/category")
    	if "Delete" in request.POST: # проверяем есть ли удаление
            check = request.POST.getlist('check') #немного изменил название массива в отличии от todo, что бы было меньше путаницы в коде
            for i in range(len(check)):
            	try:
                	сateg = Category.objects.filter(id=int(check[i]))
                	сateg.delete()   #удаление категории
            	except BaseException: # вне сомнения тут нужно нормально переписать обработку ошибок, но на первое время хватит и этого
                	return HttpResponse('<h1>Сначала удалите карточки с этими категориями)</h1>')
	return render(request, "category.html", {"categories": categories})

На этом мы заканчиваем с файлом

view

и можем переходить к шаблонам:

Работа с шаблонами

Как вы помните, чтобы не писать css лишний раз, я воспользовался

bulma.css

для упрощения верстки. Т.к. наши страницы категорий и todo буду очень схожи, я создал три файла:

base.html

, который будет включать в себя все одинаковое, что у нас есть на страницах, а в

category.html

,

todo.html

будут располагаются отличия:

Создаем

base.html

и редактируем его:

<!DOCTYPE html>
<html lang="ru">
<head>
  <meta charset="UTF-8">
  <title> Приложение для дел</title>
  {% load static %}
	<link rel="shortcut icon" type="image/png" href="{% static 'favicon.png' %}"/>
   <link rel="stylesheet" type="text/css" href="{% static 'bulma.min.css' %}">
	<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css">
</head>
<body>
<div django-app="TaskManager">
	<nav class ="navbar is-success" role="navigation" aria-label="main navigation">
     	<div class="navbar-menu ">
        	<div class="navbar-start">
        	<a class="navbar-item" href="../category"> Категории</a>
        	<a class="navbar-item" href="../todo"> Список дел </a>
        	</div>
         </div>
	</nav>
<!--     в блок контент мы будем подгружать наши странички категорий и тудушек-->
{% block content %}
{% endblock %}
</div>
</body>
</html>

Дальше у нас пойдут страницы

todo.html

и

category.html

:

Тудушка:
{% extends 'base.html' %}
{% block content %}
<div class="columns has-background-black-ter is-centered has-text-white-bis" style="min-height:101vh;">
	<!-- на самом деле можно было обойтись и hero, но я обратился к inline-css -->
	<div class="column is-half">
    	<h1 class="is-size-3 has-text-centered"> Список дел </h1>
    	<form action="" method="post">
        	{% csrf_token %}
        	<!-- csrf для базовой безопасности нашего приложения -->
            <div class="field has-text-centered">
            	<label for="description" class="label has-text-white-bis">Введите дело</label>
            	<div class="control">
             	   <input type="text" id="description" class="input" placeholder="Чем собираетесь заняться?"
                    	name="description" required>
            	</div>
        	</div>
        	<div class="columns">
            	<div class="column">
                	<label for="category">Категории</label>
                	<div class="control">
                    	<div class="select">
                        	<select id="category" class="select" name="category_select" required>
                            	<!--поставишь такой required, и не надо пустые поляв бд валидизировать. Не повторять в продакшене-->
                            	<option class="disabled" value="">Выберите категорию дела</option>
                    	        {% for category in categories %}
 	                           <option class="" value="{{ category.name }}" name="{{ category.name }}">
                                    {{ category.name }}</option>
                            	{% endfor %}
                        	</select>
    	                </div>
                	</div>
            	</div>
            	<div class="column">
                	<label for="dueDate">Выберите дату</label>
                	<input type="date" id="dueDate" class="input calendar" name="date" required>
            	</div>
        	</div>
        	<div class="column">
            	<div class="field">
                	<button class="button is-primary" name="Add" type="submit">
                    	<span class="icon is-small">
                        	<i class="fa fa-plus"></i>
                    	</span>
                    	<span>Добавить задание</span>
                	</button>
             	   <button class="button is-link" name="Delete" formnovalidate="" type="submit">
                    	<span class="icon is-small">
                        	<i class="fa fa-trash-o"></i>
                    	</span>
                    	<span>
                        	Удалить дело
                    	</span>
                	</button>
            	</div>
        	</div>
        	<div class="list is-hoverable">
            	{% for todo in todos %}
            	<!-- шаблонный язык django- for loop -->
            	<div class="list-item">
                	<label class="checkbox">
                    	<input type="checkbox" class=" checkbox" name="checkedbox" value="{{ todo.id }}">
                    	<span class="complete-">{{ todo.title }}</span>
                	</label>
                	<span class=" category-{{ todo.category }} has-text-info">{{ todo.category }}</span>
                	<strong class="is-pulled-right"><i class="fa fa-calendar"></i>{{ todo.created }} -
                    	{{ todo.due_date }}</strong>
            	</div>
            	{% endfor %}
        	</div>
    	</form>
	</div>
</div>
{% endblock %}

И

category.html

. В ней у нас не особо много чего меняется, принципиально никак не отличается от

todo.html

:

{% extends 'base.html' %}
{% block content %}
<div class="columns has-background-link has-text-white is-centered" style="min-height: 101vh;">
	<div class="column is-half">
    	<h1 class="is-size-4 has-text-centered"> Отредактируйте ваши категории </h1>
    	<form action="" method="post">
        	{% csrf_token %}
        	<!-- csrf для базовой безопасности нашего приложения -->
            <div class="field has-text-centered">
            	<label for="description" class="label has-text-white-bis"> Введите категории </label>
            	<div class="control">
     	           <input type="text" id="description" class="input" placeholder="Какого рода у вас дела?"
                    	name="name" required>
            	</div>
            	<div class="field">
                	<button class="button is-primary" name="Add" type="submit">
                    	<span class="icon is-small">
                        	<i class="fa fa-plus"></i>
                    	</span>
                    	<span>Добавить категорию</span>
                	</button>
                	<button class="button is-danger" name="Delete" formnovalidate="" type="submit">
                    	<span class="icon is-small">
           	             <i class="fa fa-trash-o"></i>
                    	</span>
	                    <span> Удалить категорию </span>
                	</button>
            	</div>
            </div>
 
            <!--  cписок наших категорий -->
            <div class="list is-hoverable">
            	{% for category in categories %}
            	<div class="list-item">
                	<label class="checkbox">
                    	<input type="checkbox" class="checkbox" name="check" value="{{category.id}}">
                    	<span class="complete-">{{ category.name }}</span>
                	</label>
            	</div>
            	{% endfor %}
 
        	</div>
    	</form>
	</div>
    {% endblock %}
 

Всем спасибо! На этом все. Возможно, где-то не идеальна верстка, или есть другие предложения по улучшению приложения, всех жду в комментарии. По традиции, несколько полезных ссылок:

  1. Официальная документация Django, если вы хотите разрабатывать на новой 3 версии
  2. В русскоязычной интернете не так много хороший гайдов по поводу ООП, но этот мне кажется самым полезным
  3. Русскоязычная документация на PostgreSQL





Разместим вашу рекламу

Пиши: mail@pythondigest.ru

Нашли опечатку?

Выделите фрагмент и отправьте нажатием Ctrl+Enter.

Система Orphus