Простейшее пособие по Django и Python
Автор делал пособие для себя, чтоб вспомнить основы и азы.
Версия Django на момент написания 4.1
Пособие дает простейшее представление с нуля до простешего 3х страничного сайта без всякой динамики и прочего.
Немного навигации, немного ORM, самые основы, не более.
Запуск django под windows
В windows powershell от администратора делаем алиасы на установленный python:
Set-Alias python "C:\Users\Your_UserName\AppData\Local\Programs\Python\Python310\python.exe"
Set-Alias pip "C:\Users\Your_UserName\AppData\Local\Programs\Python\Python310\scripts\pip.exe"
Разрешаем выполнение сценариев:
Set-ExecutionPolicy Unrestricted
СОздаем виртуальное окружение и далее работаем в нем:
cd C:\\python\django
python -m venv site1
.\site1\Scripts\activate
Установка и запуск проекта:
(site1) PS C:\python\django\site1\app> pip install django
(site1) PS C:\python\django\site1\app> django-admin.exe startproject app
(site1) PS C:\python\django\site1\app> cd app
(site1) PS C:\python\django\site1\app> python manage.py runserver 0.0.0.0:8000
Проверяем:
http://127.0.0.1:8000/
Приложение
Создадим отдельную, самостоятельную часть сайта
Например - форум, опросник, основное приложение, чат, подсайт и т.п.
Такие части называются приложениями
Создадим базовый функционал сайта с названием "people":
(site1) PS C:\python\django\site1\app> python manage.py startapp people
Создастя каталог приложения:
C:\python\django\site1\app\people
Файл указывает, что приложение надо рассматривать как пакет:
app/people/__init__.py
Папка для миграций нашего приложения:
app/people/migrations
Связь с админкой сайта:
app/people/admin.py
Для хранения ORM моделей, представления данных из БД:
app/people/models.py
Модуль для тестирующих процедур:
app/people/tests.py
Модуль представлений (контроллеров):
app/people/views.py
Приложение надо зарегистрировать, чтоб проект знал о нем
Приложения прописываюстя тут:
C:\python\django\site1\app\app\settings.py
...
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'people', # <-- Добавить строку
]
...
Django, обращаясь к пакету people идет в:
C:\python\django\site1\app\people\apps.py
...и находит в нем class PeopleConfig
Упростим дальнейшую работу с классом:
...
INSTALLED_APPS = [
...
'people.apps.PeopleConfig', # <-- Добавить строку
]
...
Сделаем обработчик главной страницы приложения
Можно сделать в виде функции или класса
С помощью функции в app/people/views.py:
from django.http import HttpResponse
from django.shortcuts import render
def index(request):
return HttpResponse("Главная страница")
На входе запроса:
Переменная request - ссылка на класс HttpRequest
Через request будет доступна информация по данному запросу
На выходе запроса:
from django.http import HttpResponse - импортируем класс
Экземпляр класса HttpResponse - формирует ответ
Функцию с именем index надо связать с url адресом в
C:\python\django\site1\app\app\urls.py
...
from people.views import index
urlpatterns = [
path('admin/', admin.site.urls),
path('people/', index), # http://0.0.0.0:8000:/people/
]
Мы импортировали фугкцию index из представления приложения people
Далее мы связали функцию с маршрутизацией
Проверяем:
http://127.0.0.1:8000/people/
Мы сделали:
1. app/app/settings.py <- app/people/apps.py - связь проекта и приложения
2. app/app/urls.py <- app/people/views.py - связь маршрутизации и представления
Добавим функционал
В app/people/views.py:
from django.http import HttpResponse
from django.shortcuts import render
def index(request):
return HttpResponse("Главная страница")
def categories(request):
return HttpResponse("<h1>Категории статей</h1>")
В app/app/urls.py:
...
from people.views import *
urlpatterns = [
path('admin/', admin.site.urls),
path('', index), # http://0.0.0.0:8000:/
path('ct/', categories), # http://0.0.0.0:8000:/ct/
]
import * - импортировать сразу все из people.views
path('', index) - переделали маршрутизацию главной страницы
path('ct/', categories) - добавили маршрутизацию
Проверяем - http://127.0.0.1:8000/ct/
Перепишем функционал на более правильный и удобный
В app/app/urls.py:
...
from people.views import *
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('people/', include('people.urls')),
]
С помощью функционала include мы импортировали файл urls прилоржения people
В app/people/urls.py:
from django.urls import path
from .views import *
urlpatterns = [
path('', index), # http://0.0.0.0:8000:/people/
path('ct/', categories), # http://0.0.0.0:8000:/people/ct/
]
Проверяем:
http://127.0.0.1:8000/people/
http://127.0.0.1:8000/people/ct/
Мы сделали связи:
1. app/app/settings.py <- app/people/apps.py - проекта и приложения
2. app/app/urls.py <- app/people/urls.py - маршрутизаций проекта и приложения
2. app/people/urls.py <- app/people/views.py - маршрутизации и представления
Добавим динамическую маршрутизацию
В app/app/urls.py делаем заглавную страницу на приложение:
...
urlpatterns = [
path('admin/', admin.site.urls),
path('', include('people.urls')),
]
В app/people/urls.py добавим числовые подзапросы в url:
from django.urls import path, re_path
from .views import *
urlpatterns = [
path('', index), # http://0.0.0.0:8000:/people/
path('ct/<int:catid>/', categories), # http://0.0.0.0:8000:/people/ct/число/
re_path(r'^arch/(?P<year>[0-9]{4})/', arch), # http://127.0.0.1:8000/arch/год/
]
Здесь <int:catid>:
int - тип параметра
catid - произвольное имя параметра, которым мы сможем оперировать
По умолчанию доступны следующие конвертеры пути:
Доступные типы переменных:
str - непустая строка
int - ноль или любое положительное цело
slug - строка заголовка, состоящей из букв или цифр ASCII, дефиса, подчеркивания
Пример: build-your-1st-django-site
uuid - форматированный UUID
Пример: 075194d3-6885-417e-a8a8-6c931e272f00
path - любая непустая строка включая разделитель пути, '/'
Подробнее в актуальной документации, ищем "URL dispatcher":
https://docs.djangoproject.com/en/4.1/topics/http/urls/
re_path - позволяет создать регулярное выражение для url
arch - часть url
<year> - имя переменной
Все остальное - шаблон регулярного выражения (4 числа, формат года)
В app/people/views.py пропишем параметр из app/people/urls.py:
...
def categories(request, catid):
get = ''
if(request.GET):
print(request.GET)
for key in request.GET:
get = get + key + ' ->' + request.GET[key] + ', '
else:
get = "ничего не передали"
return HttpResponse(f"<h1>Категории статей</h1>"
f"<p>{catid}</p>"
f"<p>GET часть: {get}</p>"
f"")
def arch(request, year):
return HttpResponse(f"<h1>Архив статей</h1><p>За {year} год</p>")
В categories мы добавили функционал работы с GET запросом
Функционал имеется в переменной request в GET
Получаем словарь GET
Gеребираем все его значения и ключи
Выводим в консоль и браузер
Делается проверка, передавалось вообще что-то с помощью GET
Проверяем:
http://127.0.0.1:8000/ct/1/
http://127.0.0.1:8000/arch/2022/
http://127.0.0.1:8000/ct/1/?var1=test1&var2=test2
Отключим дебаг для несуществующих страниц - ошибка 404
Правим app/app/settings.py:
DEBUG = False
ALLOWED_HOSTS = ['*']
Теперь нам доступны спецобработчики:
handler400
handler403
handler404
handler500
Прописываем обработку специальной переменной в app/app/urls.py:
handler404 = pageNotFound
Добавляем в app/people/views.py:
from django.http import HttpResponse, HttpResponseNotFound, Http404
def categories(request, catid):
if int(catid) > 7:
raise Http404()
def pageNotFound(request, exception):
return HttpResponseNotFound('<h1>Запрошенная страница не существует</h1>')
Если число категории больше 7 - задействуем обработку 404 ошибки
Проверьте - http://127.0.0.1:8000/ct/9/
Вместо 404 ошибки сделаем редирект
В app/people/urls.py добавим имя, к которому мы будем маршрутизировать редирект:
...
urlpatterns = [
path('', index, name='red_url'), # http://0.0.0.0:8000:/people/
...
]
...
Изменяем в app/people/views.py:
from django.shortcuts import render, redirect
def categories(request, catid):
if int(catid) > 7:
# 302 code
#return redirect('red_url')
# 301 code
return redirect('red_url', permanent=True)
О миграциях и бд
Настройки, к какому типу БД обращается Django указываются в settings.py
По умолчанию это sqlite3(указан драйвер и путь к файлу БД):
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': BASE_DIR / 'db.sqlite3',
}
}
Для получения визуального доступа к БД можете использовать:
https://sqlitestudio.pl/
У меня БД находится тут:
C:/python/django/site1/app/db.sqlite3
В django мы описываем ORM модели на базе классов в models.py
Опишем модель (таблицу в БД) тут app/people/models.py:
from django.db import models
class People(models.Model):
name = models.CharField(max_length=50)
biography = models.TextField(blank=True)
birthday = models.DateField(null=True)
photo = models.ImageField(upload_to="photos/%Y/%m/%d/")
publish = models.BooleanField(default=True)
Типы полей описаны в документации:
https://docs.djangoproject.com/en/4.1/topics/db/models/
https://docs.djangoproject.com/en/4.1/topics/db/models/#field-types
Самостоятельно разберитесь, что за типы и параметры в таблице
Для поля ImageField необходимо сделать донастройку:
https://docs.djangoproject.com/en/4.1/ref/models/fields/#django.db.models.ImageField
https://docs.djangoproject.com/en/4.1/ref/settings/#std-setting-MEDIA_ROOT
https://docs.djangoproject.com/en/4.1/ref/settings/#std-setting-MEDIA_URL
Доустановите необходимый для ImageField пакет Pillow:
PS C:\python\django\site1\app> python -m pip install Pillow
app/app/settings.py добавьте:
import os
from pathlib import Path
...
DEBUG = True
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
MEDIA_URL = '/media/'
Добавим еще один маршрут, в случае включения константы дебага settings.DEBUG
app/app/urls.py:
from django.conf.urls.static import static
from app import settings
...
if settings.DEBUG:
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
Т.е. мы сделали:
app/people/models.py - описали таблицу для БД
app/app/settings.py - настроили дебаг и url
app/app/urls.py - включили обработку маршрута, когда включен дебаг в settings.py
Миграции - модули python, при выполнении создаются новые или изменяются таблицы
Папка миграций нашего приложения people:
app/people/migrations
Создадим файл миграций:
(site1) PS C:\python\django\site1\app> python manage.py makemigrations
Migrations for 'people':
people\migrations\0001_initial.py
- Create model People
Посмотрим sql запрос, который выполнится файлом 0001_initial.py приложения people:
(site1) PS C:\python\django\site1\app> python manage.py sqlmigrate people 0001
BEGIN;
--
-- Create model People
--
CREATE TABLE "people_people" ("id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, "name" varchar(50) NOT NULL,
"biography" text NOT NULL, "birthday" date NOT NULL, "photo" varchar(100) NOT NULL, "publish" bool NOT NULL);
COMMIT;
Выполним миграцию в БД:
(site1) PS C:\python\django\site1\app> python manage.py migrate
CRUD
Create, Read, Update, Delete операции с таблицами в БД
Войдем в shell:
(site1) PS C:\python\django\site1\app> python manage.py shell
Python 3.10.8 (tags/v3.10.8:aaaf517, Oct 11 2022, 16:50:30) [MSC v.1933 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
Импортируем из приложения app модель People
>>> from people.models import People
Запишем значения в строку, id - автоматический:
>>> People(name='Andy', biography='Born in USSR')
<People: People object (None)>
Сохраним все действия в переменную:
>>> tr1= _
Методом save сохраним данные в таблицу:
>>> tr1.save()
Назначенный id записи:
>>> tr1
<People: People object (1)>
Узнаем его id:
>>> tr1.pk
1
Обратимся к какому-либо полю:
>>> tr1.name
'Andy'
Посмотрим sql запросы после выполнения команд:
>>> from django.db import connection
>>> connection.queries
[{'sql': 'INSERT INTO "people_people" ("name", "biography", "birthday", "photo", "publish") VALUES (\'Andy\', \'Born in USSR\', NULL, \'\', 1) RETURNING "people_people"."id"', 'time': '0.016'}]
>>> tr2 = People(name='Boris', biography='Born in RSFSR')
>>> tr2.save()
>>> connection.queries
[{'sql': 'INSERT INTO "people_people" ("name", "biography", "birthday", "photo", "publish") VALUES (\'Andy\
', \'Born in USSR\', NULL, \'\', 1) RETURNING "people_people"."id"', 'time': '0.016'}, {'sql': 'INSERT INTO
"people_people" ("name", "biography", "birthday", "photo", "publish") VALUES (\'Boris\', \'Born in RSFSR\', NULL, \'\', 1) RETURNING "people_people"."id"', 'time': '0.000'}]
Еще вариант добавления записи:
>>> tr3 = People()
>>> tr3.name = 'Candy'
>>> tr3.biography = 'Born in SNG'
>>> tr3.save()
Еще варианты добавления записи, метод save уже не нужен:
>>> tr4 = People.objects.create(name='Drago', biography='Born in RF')
>>> People.objects.create(name='Batya', biography='Born in BR')
Теперь сделаем выборку
app/people/models.py добавим:
...
class People(models.Model):
...
def __str__(self):
return self.name
Это нужно, чтоб вместо заголовков object:
>>> People.objects.all()
<QuerySet [<People: People object (1)>, <People: People object (2)>, <People: People object (3)>, <People: People object (4)>, <People: People object (5)>]>
Отображалась нужная информация, перезайдите:
>>> exit()
(site1) PS C:\python\django\site1\app> python manage.py shell
>>> from people.models import People
>>> People.objects.all()
<QuerySet [<People: Andy>, <People: Boris>, <People: Candy>, <People: Drago>, <People: Batya>]>
Присвойте переменное последние результаты:
>>> w = _
Обратитесь к записям:
>>> w[0]
<People: Andy>
>>> w[0].biography
'Born in USSR'
КОличество записей:
>>> len(w)
5
Перебор записей:
>>> for i in w: print(i.name)
Andy
Boris
Candy
Drago
Batya
Выборка определенной записи по условию:
>>> People.objects.filter(name='Andy')
>>> People.objects.filter(pk=3)
Выборка по условию __gte >= или __lte <=:
>>> People.objects.filter(pk__gte=4)
>>> People.objects.filter(pk__lte=2)
Выборка определенной записи по обратному условию:
>>> People.objects.exclude(pk=3)
Выбирает только одну запись, если будет больше - вернет ошибку:
>>> People.objects.get(pk=3)
Сортировка выборки:
>>> People.objects.filter(pk__gte=2).order_by('name')
Изменение записис из метода filter:
>>> People.objects.filter(pk=3).update(name='Corny')
Изменение записис из метода get:
>>> u = People.objects.get(name='Andy')
>>> u.name = 'Anna'
>>> u.save()
Удаление записей:
>>> d = People.objects.filter(pk__gte=4)
>>> d.delete()
Шаблоны
Если посмотреть app/people/views.py обнаружим import render
render - функция шаблонизатора
Создадим подкаталоги и файл html
app/people/templates/people/index.html:
<!DOCTYPE html><html lang="en"><head>
<meta charset="UTF-8"><title>Title</title>
</head><body></body></html>
app/people/views.py пропишем путь:
...
def index(request):
return render(request, 'people/index.html')
...
Указывать templates не надо т.к. в app/app/settings.py:
TEMPLATES = [
...
'APP_DIRS': True,
...
Уже указано искать шаблоны в подпапках приложений
Создадим еще один шаблон app/people/templates/people/about.html:
<!DOCTYPE html><html lang="en">
<head>
<meta charset="UTF-8"><title>{{title}}</title>
</head>
<body><h1>{{about}}</h1></body></html>
{{title}} и {{about}} - переменные, передадим их разными способами из views.py
Создадим представление для шаблона app/people/views.py:
...
ix = {'Главная': '/', 'Статьи по категориям': '/ct/1/', 'Архив': '/arch/2022/', 'Информация': '/about/'}
def index(request):
return render(request, 'people/index.html', {'ix': ix, 'title': 'Главная страница'})
def about(request):
return render(request, 'people/about.html', {'title': 'Информация', 'about': 'О компании'})
...
И маршрут для about app/people/urls.py:
...
urlpatterns = [
...
path('about/', about, name='about')
]
...
Переберем переданный словарь ix из views.py:
app/people/templates/people/index.html:
<!DOCTYPE html><html lang="en"><head>
<meta charset="UTF-8"><title>{{title}}</title>
</head><body>
<ul>
{% for key, value in ix.items %}
<li><a href="{{ value }}"> {{ key }} </a></li>
{% endfor %}
</ul>
</body></html>
Проверяем - http://127.0.0.1:8000/
Проверяем - http://127.0.0.1:8000/about/
Создадим базовый шаблон, избавимся от хардконинга html
app/people/templates/people/base.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>{{ title }}</title>
</head>
<body>
{% block body %}
{% endblock %}
</body>
</html>
Будем передавать контент в блок body базового шаблона
index.html меняем на:
{% extends 'people/base.html' %}
{% block body %}
<ul>
{% for key, value in ix.items %}
<li><a href="{{ value }}"> {{ key }} </a></li>
{% endfor %}
</ul>
{% endblock %}
about.html меняем на:
{% extends 'people/base.html' %}
{% block body %}
<h1>{{about}}</h1>
{% endblock %}
Хранить значение нужно в БД
Для этого перепишем импорт значений из нашей таблицы
app/people/views.py:
from .models import *
...
def about(request):
people = People.objects.all()
return render(request, 'people/about.html', {'people': people, 'title': 'Информация', 'about': 'О компании'})
about.html меняем на:
{% extends 'people/base.html' %}
{% block body %}
<h2>Наши сторудники</h2>
<ul>
{% for p in people %}
<li>{{p.name}}: {{p.biography}}</li>
{% endfor %}
</ul>
{% endblock %}
Статические файлы
Пропишите в app/app/settings.py:
STATIC_URL = 'static/'
STATIC_ROOT = os.path.join(BASE_DIR, 'static')
STATICFILES_DIRS = []
Имеем структуру:
site1
-app
--app
---static - нестандартный путь, переменная STATICFILES_DIRS
--people
---static - нестандартный путь, переменная STATICFILES_DIRS
----people
-----js
-----css
------styles.css
-----images
--static - стандартный путь проекта, переменная STATIC_ROOT
Переменная STATIC_URL отвечает за префикс URL статических файлов
app/people/static/people/css/styles.css:
h2 { color: green; text-decoration: underline; }
Положим файл:
app/people/static/people/images/dj.jpg
Создать стандартную папку static, которая включит все подключенные папки:
(site1) PS C:\python\django\site1\app> python manage.py collectstatic
Подключим статику в базовый шаблон app/people/templates/people/base.html:
{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>{{ title }}</title>
<link type="text/css" href="{% static 'people/css/styles.css' %}" rel='stylesheet' />
</head>
<body>
<h2>Site powered by</h2>
<a href="{% url 'red_url' %}"><img src="{% static 'people/images/dj.jpg' %}" alt="dj_image"></a>
{% block body %}
{% endblock %}
</body>
</html>
Так-же воспользуемся тегом url к имени из app/people/urls.py в ссылке <a href>
Фильтры
Для манипуляции с данными, поступающими в шаблоны используются встроенные фильтры:
https://docs.djangoproject.com/en/4.1/ref/templates/builtins/#built-in-filter-reference
На примере обрезающего текс по длине фильтра
Изменим в app/people/templates/people/about.html:
...
<li>{{p.name}}: {{p.biography|truncatechars:10}}</li>
..
Мы обезали вывод текста из biography
Немного упростим формирование url
В app/people/views.py заменим длинный список в render
...
def about(request):
people = People.objects.all()
data = {
'people': people,
'title': 'Информация',
'about': 'О компании'
}
return render(request, 'people/about.html', context=data)
...
Ключевое слово context дает указать словарь данных
Добавим в app/people/models.py:
...
class Article(models.Model):
category = models.IntegerField(blank=False)
name = models.CharField(max_length=50)
content = models.TextField(blank=True)
author = models.ForeignKey('People', on_delete=models.CASCADE, null=True)
def __str__(self):
return self.name
...
Связь между таблицами People(id) - Article(author_id)
models.CASCADE:
при удалении записи из первичной модели (People)
удаляться все связанные записи из вторичной модели (Article)
Документация по созданию связей между моделями:
https://docs.djangoproject.com/en/4.1/topics/db/models/#relationships
Добавим записей
(site1) PS C:\python\django\site1\app> python manage.py makemigrations
(site1) PS C:\python\django\site1\app> python manage.py migrate
(site1) PS C:\python\django\site1\app> python manage.py shell
>>> from people.models import Article
Article.objects.create(category=1, name='Auto tunung', content='Any content in article', author_id=1)
Article.objects.create(category=1, name='Moto tunung', content='Any content in article', author_id=1)
Article.objects.create(category=2, name='Air tunung', content='Any content in article', author_id=2)
Article.objects.create(category=2, name='Spring tunung', content='Any content in article', author_id=2)
Делаем шаблон people/templates/people/ct.html:
{% extends 'people/base.html' %}
{% block body %}
<h2>Категории статей</h2>
<ul>
{% for a in article %}
<li><a href="{% url 'post_url' a.pk %}">{{a.name}}</a> Автор: {{a.author.name}}</li>
{% endfor %}
</ul>
{% endblock %}
post_url - имя маршрута из app/people/urls.py
a.author.name - значение по связанной таблице
Изменим app/people/views.py, добавив обработчик запроса url статьи:
...
def categories(request):
article = Article.objects.all()
data = { 'article': article }
return render(request, 'people/ct.html', context=data)
def show_post(request, post_id):
return HttpResponse(f"Отображение статьи с id = {post_id}")
...
Маршрутизацию привяжем к имени post_url app/people/urls.py:
...
path('ct/', categories), # http://0.0.0.0:8000:/ct/
path('ct/<int:post_id>/', show_post, name='post_url'), # http://0.0.0.0:8000:/ct/число/
...
Проверяем:
http://127.0.0.1:8000/ct/
http://127.0.0.1:8000/ct/3/
Второй вариант
Правим app/people/models.py:
...
from django.urls import reverse
...
class Article(models.Model):
category = models.IntegerField(blank=False)
name = models.CharField(max_length=50)
content = models.TextField(blank=True)
author = models.ForeignKey('People', on_delete=models.CASCADE, null=True)
def __str__(self):
return self.name
def get_url(self):
return reverse('post_url', kwargs={'post_id': self.pk})
Изменяем шаблон people/templates/people/ct.html:
{% extends 'people/base.html' %}
{% block body %}
<h2>Категории статей</h2>
<ul>
{% for a in article %}
<li><a href="{{ a.get_url }}">{{a.name}}</a> Автор: {{a.author.name}}</li>
{% endfor %}
</ul>
{% endblock %}
ct.html выводит список статей
В url статьи вызывается обработчик с именем post_url из urls.py
Обработчик post_url вызывает функцию show_post с переданной ей post_id из views.py
Перепишем обработчик app/people/views.py:
...
def show_post(request, post_id):
article = Article.objects.get(pk=post_id)
return HttpResponse(f"Отображение статьи с id = {post_id}, Именем: {article.name}<br>{article.content}")
...
Тут мы выбрали данные из связанной таблицы и вывели их
В app/people/views.py подправим вывод на главной ссылок:
ix = {'Главная': '/', 'Статьи по категориям': '/ct/1/'
Заменим на:
ix = {'Главная': '/', 'Статьи по категориям': '/ct/'
Админка
Русифицируем в app/app/settings.py:
LANGUAGE_CODE = 'ru'
Создадим суперюзера для админки:
(site1) PS C:\python\django\site1\app> python manage.py createsuperuser
Проверяем:
http://127.0.0.1:8000/admin/login/?next=/admin/
Импортируем приложения в админку app/people/admin.py:
from django.contrib import admin
from .models import *
class PeopleAdmin(admin.ModelAdmin):
list_display = ('id', 'name', 'biography', 'birthday')
list_display_links = ('id', 'name')
search_fields = ('name', 'biography')
list_editable = ('biography', 'birthday')
admin.site.register(People, PeopleAdmin)
class ArticleAdmin(admin.ModelAdmin):
list_display = ('id', 'name', 'content', 'author')
list_display_links = ('id', 'name')
search_fields = ('name', 'content')
list_editable = ('content', 'author')
list_filter = ('name', 'author')
admin.site.register(Article, ArticleAdmin)
В классах ...Admin мы прописали отображение стобцов и поиск в админке
list_editable - доступные для редактирования значения в списке вывода админки
list_filter - фильтр вывода в админке по критериям
Перепишем app/people/models.py:
...
class People(models.Model):
...
name = models.CharField(max_length=50, verbose_name='Имя')
...
class Meta:
verbose_name = 'Авторы'
verbose_name_plural = 'Авторы'
ordering = ['name']
...
class Article(models.Model):
...
name = models.CharField(max_length=50, verbose_name='Название')
...
def get_absolute_url(self):
return reverse('post_url', kwargs={'post_id': self.pk})
class Meta:
verbose_name = 'Статьи'
verbose_name_plural = 'Статьи'
ordering = ['author', 'name']
...
verbose_name='Название\Имя' - отображение в админке внутри модели
Применим изменения:
(site1) PS C:\python\django\site1\app> python manage.py makemigrations
(site1) PS C:\python\django\site1\app> python manage.py migrate
Перепишем app/people/templates/people/ct.html:
<li><a href="{{ a.get_url }}"
меняем на
<li><a href="{{ a.get_absolute_url }}"
Отображение раздела приложения People в админке меняем в app/people/apps.py:
from django.apps import AppConfig
class PeopleConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'people'
verbose_name = 'Авторы и статьи'
Заметьте, в админке слева Peoples изменилось на Статьи
ordering - сортировка в выводе по столбцам
В записи админки приложения Articles увидите справа "СМОТРЕТЬ НА САЙТЕ":
http://127.0.0.1:8000/admin/people/article/4/change/
Добвать ДР и фото у авторов с помощью админки:
http://127.0.0.1:8000/admin/people/people/1/change/
http://127.0.0.1:8000/admin/people/people/2/change/
http://127.0.0.1:8000/admin/people/people/3/change/
Подправим вывод статьи для вывода фото автора app/people/views.py:
...
def show_post(request, post_id):
article = Article.objects.get(pk=post_id)
return HttpResponse(f"Отображение статьи с id = {post_id}, "
f"Именем: {article.name}"
f"<br>{article.content}"
f"<br>Фото автора:"
f"<br><img src='{article.author.photo.url}'>")
Теги шаблонов
simple_tag: обрабатывает данные и возвращает строку
inclusion_tag: обрабатывает данные и возвращает отображаемый шаблон
assignment_tag: обрабатывает данные и устанавливает переменную в контексте
Мы рассмотрим первый прмиер с simple_tag
Создадим структуру:
people/
__init__.py
models.py
...
templatetags/
__init__.py
custom_tags.py
Файл указывает, что приложение надо рассматривать как пакет:
__init__.py
Модуль (custom_tags.py ) должен содержать переменную с именем register
register является экземпляром библиотеки шаблонов, в котором зарегистрированы все теги
Создадим простой тег, который будет подсчитывать % выведенных статей
custom_tags.py:
from django import template
register = template.Library()
from people.models import Article, People
@register.simple_tag
def count_articles(article_pk):
return (article_pk*100)/Article.objects.count()
Добавим в шаблон использование модуля custom_tags.py в ct.html:
{% extends 'people/base.html' %}
{% load custom_tags %}
{% block body %}
<h2>Категории статей</h2>
<ul>
{% for a in article %}
<li><a href="{{ a.get_absolute_url }}">{{a.name}}</a>
Автор: {{a.author.name}}
% выведенных статей: {% count_articles a.pk %}
</li>
{% endfor %}
</ul>
{% endblock %}
Определим желаемое имя тега
Перепишем custom_tags.py:
...
@register.simple_tag(name='ct_as')
...
Перепишем ct.html:
...
% выведенных статей: {% ct_as a.pk %}
...
Добавим еще тэг app/people/templatetags/custom_tags.py:
...
@register.simple_tag()
def get_authors():
return People.objects.all()
...
Однако значения тэга нельзя перебрать
Для этотго существует ключевое слово as (ссылка на данные)
ct.html:
...
{% get_authors as gt_as %}
<p>Наши авторы:
{% for a in gt_as %} {{a.name}} {% endfor %}
</p>
...
Вариант inclusion_tag - возвращающий шаблон
Перепишем app/people/templatetags/custom_tags.py:
...
@register.inclusion_tag('people/show_authors.html')
def get_authors():
authors = People.objects.all()
return {"authors": authors}
...
Перпешем кусок из ct.html в app/people/templates/people/show_authors.html:
<p>Наши авторы:
{% for a in authors %} {{a.name}} {% endfor %}
</p>
authors в show_authors.html - return {"authors": authors} из custom_tags.py
Вызовем тэг app/people/templates/people/ct.html:
...
{% get_authors %}
...
Добавим тэг с условием выборки в app/people/templatetags/custom_tags.py:
...
@register.simple_tag()
def get_author(author):
a = People.objects.filter(name=author).get()
return f", ДР: {a.birthday}"
...
Перепишем app/people/templates/people/show_authors.html:
{% load custom_tags %}
<p>Наши авторы:
{% for a in authors %}
{{a.name}} {% get_author a.name %}
{% endfor %}
</p>
Slug - слаг
Фрагмент URL, состоящий из латинских букв, цифр, символов подчеркивания и дефиса
Добавим поле в модель app/people/models.py и возврат абсолютного url:
...
class Article(models.Model):
category = models.IntegerField(blank=False)
name = models.CharField(max_length=50, verbose_name='Название')
content = models.TextField(blank=True)
author = models.ForeignKey('People', on_delete=models.CASCADE, null=True)
slug = models.SlugField(max_length=50, unique=True, db_index=True, verbose_name="URL")
...
def get_absolute_url(self):
return reverse('post_url', kwargs={'post_slug': self.slug})
...
Удалите файл бд и файлы миграции, иначе модель не пересоберется
Чтоб не заполнять слаг руками, скорректируйте app/people/admin.py:
...
class ArticleAdmin(admin.ModelAdmin):
...
prepopulated_fields = {"slug": ("name",)}
...
Это даст формирование слага на основе значения в name
Скорректируем маршрутизация для слага app/people/urls.py:
...
path('ct/<slug:post_slug>/', show_post, name='post_url'),
...
Скорректируем вывод страницы app/people/views.py:
def show_post(request, post_slug):
article = Article.objects.get(slug=post_slug)
return HttpResponse(f"Отображение статьи с id = {article.pk}, "
f"Именем: {article.name}"
f"<br>{article.content}")
(site1) PS C:\python\django\site1\app> python manage.py makemigrations
(site1) PS C:\python\django\site1\app> python manage.py migrate
(site1) PS C:\python\django\site1\app> python manage.py createsuperuser
(site1) PS C:\python\django\site1\app> python manage.py shell
>>> from people.models import People
>>> People.objects.create(name='Human1', biography='Born in RU')
>>> People.objects.create(name='Human2', biography='Born in USSR')
Далее через админку добавьте 4 статьи, как ранее
Обратите, поле slug заполниться автоматически
Проверьте формирование ссылок на странице:
http://127.0.0.1:8000/ct/
Проверьте по аналогии со статьей:
http://127.0.0.1:8000/ct/auto-tunung/
Формы с GET и POST
Get
Заглушка отображения успеха app/people/templates/people/add_author.html:
<!DOCTYPE html><html lang="en">
<head><meta charset="UTF-8"><title>Успех</title></head>
<body>
<h2>Внесено</h2>
<p>{{ name }}</p><p>{{ biography }}</p>
</body>
</html>
Добавим в app/people/views.py:
...
def add_author(request):
name = request.GET['name']
biography = request.GET['biography']
row = People(name = name, biography = biography)
row.save()
return render(request, 'people/add_author.html', {
'name': name, 'biography': biography
} )
В row мы заносим данные из формы и сохраняем в базу данных
Добавим в app/people/urls.py:
...
urlpatterns = [
...
path('add_author/', add_author, name='add_author')
]
Дополним app/people/templates/people/ct.html:
...
<h3>GET</h3>
<form action="{% url 'add_author' %}" method="GET">
<label>Имя</label><input name="name">
<label>Биография</label><input name="biography">
<button type="submit">Внести</button>
</form>
{% endblock %}
В form action указано имя add_author из urls.py
Post
Меняем в app/people/views.py:
...
def add_author(request):
name = request.POST['name']
biography = request.POST['biography']
...
Меняем в app/people/templates/people/ct.html:
...
<h3>POST</h3>
<form action="{% url 'add_author' %}" method="POST">
{% csrf_token %}
<label>Имя</label><input name="name">
<label>Биография</label><input name="biography">
<button type="submit">Внести</button>
</form>
{% endblock %}