Работа с моделями Django


Содержание

django-models Начало работы с django-models

замечания

В этом разделе представлен обзор того, что такое модели django, и почему разработчик может захотеть его использовать.

Следует также упомянуть о любых крупных предметах в моделях джанго, а также ссылки на связанные темы. Поскольку документация для моделей django является новой, возможно, вам придется создавать начальные версии этих связанных тем.

Пример модели Django

Простым примером может служить приложение для управления библиотекой; у вас было бы 2 модели, например, student и book

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

Теперь вы можете просто импортировать эту модель в свои views или в проект и взаимодействовать с ней, просто создав объект этой модели.

Django имеет множество встроенных полей , или даже вы можете создавать свои собственные.

Django также поддерживает отношения между моделями, many-to-many , one-to-one , many-to-one .

Подробный документ Django для моделей

Установка или настройка

Как правило, каждая модель сопоставляется с одной таблицей базы данных. Мы должны писать тип поля, ограничения, размер и т. Д. В файле model.py приложения. Это создаст необходимую таблицу и поля в базе данных.

Затем нам нужно сообщить Django в settinggs.py о приложении, которое будет использовать эту модель.

Мы почти закончили. Затем нам нужно перенести это приложение, чтобы таблицы базы данных были созданы. В терминальном типе:

migrate будет создавать необходимые базы данных, проверяя app_installed в setting.py

Посредством makemigrations Django будет знать изменения, внесенные в модели.

Вот и все. Ваша база данных создана, и вы можете увидеть схему в терминале

Что такое модели Django?

Модель Django model обычно ссылается на таблицу в базе данных, атрибуты этой модели становятся столбцом этой таблицы. В более реальном примере вы создадите модель для любого объекта в своем приложении и сохраните его атрибуты с django fields которые автоматически обрабатывают преобразования типов данных для используемой базы данных.

Одной из замечательных особенностей Django является ORM , вам не нужно писать какой-либо запрос к базе данных, и даже рекомендуется не писать его при использовании Django. ORM преобразует ваши Django models и все операции, которые вы выполняете с ним, в соответствующие запросы к базе данных. Это означает, что все манипуляции, которые вы должны выполнить, теперь теперь с объектами python, созданными из этой модели, и все базовые элементы базы данных будут позаботиться о ORM Django. Есть множество настроек и настроек, с которыми вы могли бы справиться.

Работа с моделями Django

Я расскажу про модели и Django ORM с разных сторон:

  • Создадим несколько моделей.
  • Поработаем с ними через интерфейс администратора.
  • Поработаем с ними через консоль.
  • Посмотрим, как они выглядят в базе данных.

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

Создание моделей в Django

В предыдущем руководстве (Создание приложения Django и подключение к базе данных) вы научились создавать БД MySQL и приложения Django, а также настраивать их взаимодействие.

Данное руководство научит создавать модели Django, которые определяют поля и поведение данных приложения. Эти модели отображают данные приложения Django в БД. Django использует модели для генерации таблиц базы данных через API объектного-реляционного отображения данных (ORM).

Требования

  • Сервер Ubuntu 16.04.
  • Установленная система MySQL.
  • Приложение Django, которое взаимодействует с БД MySQL.

Все необходимые инструкции вы найдете в руководстве Создание приложения Django и подключение к базе данных.

1: Создание приложения Django

Согласно философии модульности Django создайте в проекте Django-приложение, которое содержит все файлы, необходимые для создания блога.

python3 manage.py startapp blogsite

В каталоге появится такая структура каталогов:

my_blog_app/
└── blog
├── blog
│ ├── __init__.py
│ ├── __pycache__
│ │ ├── __init__.cpython-35.pyc
│ │ ├── settings.cpython-35.pyc
│ │ ├── urls.cpython-35.pyc
│ │ └── wsgi.cpython-35.pyc
│ ├── settings.py
│ ├── urls.py
│ └── wsgi.py
├── blogsite
│ ├── admin.py
│ ├── apps.py
│ ├── __init__.py
│ ├── migrations
│ │ └── __init__.py
│ ├── models.py
│ ├── tests.py
│ └── views.py
└── manage.py

В данном руководстве сосредоточим внимание на файле models.py.

2: Создание модели Post

Сначала нужно открыть и отредактировать файл models.py и добавить в него код для создания модели Post. Модель Post содержит следующие поля базы данных:

  • title – заголовок публикации.
  • slug – валидные URL-адреса, которые хранятся и генерируются для веб-страниц.
  • content – текст публикации/ заметки.
  • created_on – дата создания заметки.
  • author – автор этой публикации.

Перейдите в каталог, в котором хранится models.py:

С помощью команды cat выведите содержимое файла в терминал:

Файл должен содержать следующий код, который импортирует модели, а также комментарий, описывающий, что нужно поместить в этот файл models.py.

from django.db import models
# Create your models here.

С помощью текстового редактора добавьте в файл следующий код.

В файле уже есть код для импорта API-интерфейсов моделей, вы можете удалить комментарий. Затем импортируйте slugify для создания слагов из строк, и User для аутентификации:

from django.db import models
from django.template.defaultfilters import slugify
from django.contrib.auth.models import User

Затем добавьте в класс моделей класс метода Post с перечисленными полями БД (title, slug, content, created_on и author).

.
class Post(models.Model):
title = models.CharField(max_length=255)
slug = models.SlugField(unique=True, max_length=255)
content = models.TextField()
created_on = models.DateTimeField(auto_now_add=True)
author = models.TextField()

Затем добавьте функции для генерации URL-адреса и функции сохранения постов. Это создаст уникальную ссылку, соответствующую каждому уникальному сообщению.

.
@models.permalink
def get_absolute_url(self):
return (‘blog_post_detail’, (),
<
‘slug’: self.slug,
>)
def save(self, *args, **kwargs):
if not self.slug:
self.slug = slugify(self.title)
super(Post, self).save(*args, **kwargs)


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

.
class Meta:
ordering = [‘created_on’]
def __unicode__(self):
return self.title

Добавьте в файл модель Comment. Для этого добавьте новый класс Comment с models.Models и следующими полями базы данных:

  • name – имя пользователя, который публикует комментарий.
  • email – адрес электронной почты пользователя, который публикует комментарий.
  • text – текст комментария.
  • post – запись, к которой относится комментарий.
  • created_on – время создания комментария.

class Comment(models.Model):
name = models.CharField(max_length=42)
email = models.EmailField(max_length=75)
website = models.URLField(max_length=200, null=True, blank=True)
content = models.TextField()
post = models.ForeignKey(Post)
created_on = models.DateTimeField(auto_now_add=True)
В результате файл models.py должен выглядеть так:
from django.db import models
from django.template.defaultfilters import slugify
from django.contrib.auth.models import User
class Post(models.Model):
title = models.CharField(max_length=255)
slug = models.SlugField(unique=True, max_length=255)
content = models.TextField()
created_on = models.DateTimeField(auto_now_add=True)
author = models.TextField()
@models.permalink
def get_absolute_url(self):
return (‘blog_post_detail’, (),
<
‘slug’: self.slug,
>)
def save(self, *args, **kwargs):
if not self.slug:
self.slug = slugify(self.title)
super(Post, self).save(*args, **kwargs)
class Meta:
ordering = [‘created_on’]
def __unicode__(self):
return self.title
class Comment(models.Model):
name = models.CharField(max_length=42)
email = models.EmailField(max_length=75)
website = models.URLField(max_length=200, null=True, blank=True)
content = models.TextField()
post = models.ForeignKey(Post)
created_on = models.DateTimeField(auto_now_add=True)

Сохраните и закройте файл.

Теперь нужно откорректировать файл settings.py.

3: Обновление параметров

Создав модели для приложения, нужно сообщить проекту о существовании приложения blogsite. Для этого добавьте приложение в раздел INSTALLED_APPS в файле settings.py.

Откройте каталог, в котором хранится settings.py:

Затем откройте этот файл:

Открыв файл, добавьте blogsite в раздел INSTALLED_APPS, как показано ниже.

# Application definition
INSTALLED_APPS = [
‘blogsite’,
‘django.contrib.admin’,
‘django.contrib.auth’,
‘django.contrib.contenttypes’,
‘django.contrib.sessions’,
‘django.contrib.messages’,
‘django.contrib.staticfiles’,
]

Сохраните и закройте файл.

4: Миграция

Создав модели Post и Comment, нужно активировать изменения, чтобы схема БД MySQL могла их распознавать и создавать необходимые таблицы.

Для этого нам нужно войти на сервер MySQL.

Примечание: В этом примере используется аккаунт root без пароля, но вы должны использовать имя пользователя и пароль, которые вы настроили для MySQL.

Введите команду SHOW DATABASES; и вы увидите:

Откройте БД blog_data и запросите ее таблицы (если такие есть).

Чтобы просмотреть таблицы в БД blog_data, введите:

Эта таблица django_migrations, которую вы видите в выводе, сообщает системе миграции Django текущее состояние базы данных, благодаря чему БД знает, какие миграции необходимо выполнить.

Теперь нужно активировать изменения, внесенные в models.py.

Закройте MySQL (CTRL + D).

Сначала изменения моделей нужно спакетировать в индивидуальные файлы с помощью makemigrations (эти файлы похожи на коммиты в git).

/my_blog_app/blog/blogsite/migrations и запустите ls. Вы увидите только файл __init__.py. После миграции файлов станет больше.

Откройте этот каталог:

/my_blog_app/blog
python3 manage.py makemigrations

В окне терминала появится такой вывод:

Migrations for ‘blogsite’:
blogsite/migrations/0001_initial.py
— Create model Comment
— Create model Post
— Add field post to comment

Если теперь вернуться к каталогу /

/my_blog_app/blog/blogsite/migrations (в котором еще недавно был только файл __init__.py), вы увидите в нем два новых файла — __pycache__ и 0001_initial.py. Файл 0001_initial.py был сгенерирован автоматически во время запуска makemigrations. Подобный файл генерируется во время каждого запуска makemigrations.

Если вы хотите просмотреть содержимое этого файла, введите:

Теперь перейдите в /

Поскольку вы создали файл миграции, вы должны применить изменения, которые содержат эти файлы, с помощью команды migrate. Но сначала просмотрите текущие миграции, используя команду showmigrations.

python3 manage.py showmigrations
admin
[ ] 0001_initial
[ ] 0002_logentry_remove_auto_add
auth
[ ] 0001_initial
[ ] 0002_alter_permission_name_max_length
[ ] 0003_alter_user_email_max_length
[ ] 0004_alter_user_username_opts
[ ] 0005_alter_user_last_login_null
[ ] 0006_require_contenttypes_0002
[ ] 0007_alter_validators_add_error_messages
[ ] 0008_alter_user_username_max_length
blogsite
[ ] 0001_initial
contenttypes
[ ] 0001_initial
[ ] 0002_remove_content_type_name
sessions
[ ] 0001_initial

Вы увидите миграцию для blogsite, которая содержит миграцию 0001_initial моделей Post и Comment.

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

python3 manage.py sqlmigrate blogsite 0001_initial

Вы увидите SQL-запрос:

BEGIN;

— Create model Comment

CREATE TABLE `blogsite_comment` (`id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY, `name` varchar(42) NOT NULL, `email` varchar(75) NOT NULL, `website` varchar(200) NULL, `content` longtext NOT NULL, `created_on` datetime(6) NOT NULL);

— Create model Post

CREATE TABLE `blogsite_post` (`id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY, `title` varchar(255) NOT NULL, `slug` varchar(255) NOT NULL UNIQUE, `content` longtext NOT NULL, `created_on` datetime(6) NOT NULL, `author` longtext NOT NULL);

— Add field post to comment

ALTER TABLE `blogsite_comment` ADD COLUMN `post_id` integer NOT NULL;
ALTER TABLE `blogsite_comment` ADD CONSTRAINT `blogsite_comment_post_id_de248bfe_fk_blogsite_post_id` FOREIGN KEY (`post_id`) REFERENCES `blogsite_post` (`id`);
COMMIT;

Теперь нужно выполнить миграцию, чтобы применить изменения в БД MySQL:

python3 manage.py migrate

Operations to perform:
Apply all migrations: admin, auth, blogsite, contenttypes, sessions
Running migrations:
Applying contenttypes.0001_initial. OK
Applying auth.0001_initial. OK
Applying admin.0001_initial. OK
Applying admin.0002_logentry_remove_auto_add. OK
Applying contenttypes.0002_remove_content_type_name. OK
Applying auth.0002_alter_permission_name_max_length. OK
Applying auth.0003_alter_user_email_max_length. OK
Applying auth.0004_alter_user_username_opts. OK
Applying auth.0005_alter_user_last_login_null. OK
Applying auth.0006_require_contenttypes_0002. OK
Applying auth.0007_alter_validators_add_error_messages. OK
Applying auth.0008_alter_user_username_max_length. OK
Applying blogsite.0001_initial. OK
Applying sessions.0001_initial. OK

Миграция успешно выполнена.

Важно иметь в виду, что в миграции Django и MySQL есть 3 нюанса (согласно документации Django).

  1. Нет поддержки транзакций в операциях изменения схемы. Другими словами, если миграция не сможет пройти успешно, вам придется вручную вводить изменения, чтобы попытаться выполнить другую миграцию. Откат до более ранней точки невозможен до внесения каких-либо изменений в неудачную миграцию.
  2. В большинстве операций по изменению схемы MySQL полностью перепишет таблицы. В худшем случае задержка пропорциональна количеству строк в таблице. Согласно документации Django, на это уходит одна минута на миллион строк.
  3. В MySQL существуют небольшие ограничения на длину имен для столбцов, таблиц и индексов. Лимиты зависят от сервера бэкэнда. Хотя некоторые другие серверы могут поддерживать более высокие лимиты, созданные в Django, бэкэнд MySQL не сможет создать такие же индексы.

Обязательно оцените преимущества и недостатки каждой базы данных, которую вы собираетесь использовать с Django.

5: Проверка схемы базы данных

После завершения миграции нужно проверить таблицы MySQL, которые были созданы с помощью моделей Django.

Для этого войдите в MySQL:

Запросите список баз данных:

Выберите БД blog_data:

Запросите список таблиц:

SHOW TABLES;
+—————————-+
| Tables_in_blog_data |
+—————————-+
| auth_group |
| auth_group_permissions |
| auth_permission |
| auth_user |
| auth_user_groups |
| auth_user_user_permissions |
| blogsite_comment |
| blogsite_post |
| django_admin_log |
| django_content_type |
| django_migrations |
| django_session |
+—————————-+


В БД появились таблицы blogsite_comment и blogsite_post. Это модели, которые вы разработали ранее. Убедитесь, что они содержат необходимые поля:

DESCRIBE blogsite_comment;
+————+—————+——+——+———+—————-+
| Field | Type | Null | Key | Default | Extra |
+————+—————+——+——+———+—————-+
| id | int(11) | NO | PRI | NULL | auto_increment |
| name | varchar(42) | NO | | NULL | |
| email | varchar(75) | NO | | NULL | |
| website | varchar(200) | YES | | NULL | |
| content | longtext | NO | | NULL | |
| created_on | datetime(6) | NO | | NULL | |
| post_id | int(11) | NO | MUL | NULL | |
+————+—————+——+——+———+—————-+
7 rows in set (0.01 sec)
DESCRIBE blogsite_post;
+————+—————+——+——+———+—————-+
| Field | Type | Null | Key | Default | Extra |
+————+—————+——+——+———+—————-+
| id | int(11) | NO | PRI | NULL | auto_increment |
| title | varchar(255) | NO | | NULL | |
| slug | varchar(255) | NO | UNI | NULL | |
| content | longtext | NO | | NULL | |
| created_on | datetime(6) | NO | | NULL | |
| author | longtext | NO | | NULL | |
+————+—————+——+——+———+—————-+
6 rows in set (0.01 sec)

Таблицы базы данных были успешно сгенерированы на основе моделей Django.

Заключение

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

Работа с моделями Django

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

В программировании существует особая концепция, она называется объектно-ориентированным программированием . Идея заключается в том, что вместо скучной последовательности инструкций мы моделируем вещи и описываем, как они взаимодействуют друг с другом.

Так что же такое объект? Это совокупность поведения и свойств. Звучит странно, но мы приведем пример.

Если мы хотим смоделировать кошку, то создадим объект Cat , который обладает определенными свойствами, например, color (цвет), age (возраст), mood (настроение: плохое, хорошее, сонное ;)), owner (хозяин, например, другой объект — Person — или, если кошка дикая, это свойство будет пустым).

Объект Cat будет иметь набор определённых действий: purr (мурчать), scratch (царапаться) или feed (кормить, где мы дадим кошке немного CatFood — кошачьей еды, которая так же может быть отдельным объектом со своими свойствами, например, taste — вкусом).

Основная идея, таким образом, заключается в описании объекта в коде, используя его параметры ( свойства объекта ) и доступные ему действия ( методы ).

Так как же мы смоделируем запись в блоге? Нам же нужен блог, верно?

Для начала стоит ответить на вопрос: что такое запись в блоге? Какие свойства она имеет?

Ну, запись наверняка содержит какой-то текст и заголовок, верно? Было бы неплохо также знать, кто её написал — так что нам нужен автор. Ну и в заключении, нам нужно знать, когда запись создана и когда опубликована.

Какие вещи можно сделать с записью в блоге? Было бы неплохо иметь метод для её публикации, согласна?

Так что нам пригодится метод publish .

Ну и раз уж мы определились с тем, что хотим получить, давай начнем моделирование в Django!

Модель в Django

Зная, что представляет из себя объект, мы можем создать модель Django для записи в блоге.

Модель в Django — это объект определённого свойства: он хранится в базе данных . База данных представляет собой совокупность различных данных. Это то место, где ты будешь хранить информацию о своих пользователях, записях в блоге и т.д. Мы будем использовать базу данных SQLite для хранения информации. Это стандартная база данных в Django — её сейчас вполне хватит для наших нужд.

Ты можешь представить модель в базе данных как электронную таблицу с колонками (полями) и строками (данными).

Для аккуратности мы создадим отдельное приложение в нашем проекте. Очень удобно иметь хорошо организованное рабочее место с самого начала. Для создания приложения нам понадобится набрать следующую инструкцию в командной строке (из директории djangogirls , где находится файл manage.py ):

<% filename %>Mac OS X и Linux:

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

После того, как приложение создано, нам нужно сообщить Django, что теперь он должен его использовать. Мы сделаем это с помощью файла mysite/settings.py . Нам нужно найти INSTALLED_APPS и добавить к списку ‘blog’, прямо перед ] . Конечный результат должен выглядеть следующим образом:

Создание модели записи в блоге

В файле blog/models.py мы определяем все Модели — модель записи для блога также пойдёт сюда.

Открой файл blog/models.py , удали весь текст и вставь на его место следующий код:

Убедись, что использовала два символа нижнего подчёркивания ( _ ) с обеих сторон от метода str . Это соглашение часто используется при программировании на Python, и иногда его называют «dunder» (сокращение от англ. «double-underscore»).

Смотрится страшно, да? Но не волнуйся, мы объясним, что значит каждая строка кода!

Строки, начинающиеся с from или import , открывают доступ к коду из других файлов. Так что вместо того, чтобы копировать и вставлять один и тот же код во все файлы, ты можешь сослаться на него при помощи from . import . .

class Post(models.Model): — эта строка определяет нашу модель ( объект ).

  • class — это специальное ключевое слово для определения объектов.
  • Post — это имя нашей модели, мы можем поменять его при желании (специальные знаки и пробелы использовать нельзя). Всегда начинай имена классов с прописной буквы.
  • models.Model означает, что объект Post является моделью Django, так Django поймет, что он должен сохранить его в базу данных.

Дальше мы задаем свойства, о которых уже говорили: title , text , created_date , published_date и author . Чтобы это сделать, нам нужно определиться с типом полей (это текст? число? дата? ссылка на другой объект? например, на пользователя?).

  • models.CharField — так мы определяем текстовое поле с ограничением на количество символов.
  • models.TextField — так определяется поле для неограниченно длинного текста. Выглядит подходящим для содержимого поста, верно?
  • models.DateTimeField — дата и время.
  • models.ForeignKey — ссылка на другую модель.

Мы не будем объяснять каждую запятую, поскольку на это уйдет слишком много времени. Ознакомься с официальной документаций Django: если хочешь узнать больше о полях моделей и о том, как определять разные объекты, то эта ссылка может помочь: (https://docs.djangoproject.com/en/1.11/ref/models/fields/#field-types).

Что насчёт def publish(self): ? Это как раз метод публикации для записи, о котором мы говорили. def означает, что создаётся функция/метод, а publish — это название этого метода. Можно изменить имя метода, если хочешь. Существует правило для имён функций: нужно использовать строчные буквы, а пробелы заменять на подчёркивания. Например, метод, вычисляющий среднюю цену, может называться calculate_average_price .

Цукерберг рекомендует:  Создание простейшего REST-сервера на JavaScript с Node.js

Методы часто возвращают что-то. Например, метод __str__ . В наше случае после вызова метода __str__() мы получим текст (строку) с заголовком записи.

Также обрати внимание, что оба метода def publish(self): и def __str__(self): внутри класса имеют дополнительный отступ. Поскольку в Python важны отступы, нам необходимо использовать их для методов внутри класса — иначе методы не будут принадлежать к классу, и при запуске программы может получиться что-то неожиданное.

Если тема моделей тебе до сих пор непонятна — не стесняйся обратиться к тренеру! Мы знаем, что она действительно сложна, особенно когда ты изучаешь параллельно объекты и функции. Но мы надеемся, что всё это больше не кажется тебе магией!

Создаём таблицы моделей в базе данных

Последним шагом будет добавление нашей модели в базу данных. Сначала мы должны дать Django знать, что сделали изменения в нашей модели (мы её только что создали!). Набери python manage.py makemigrations blog . Должно получиться примерно так:

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

Django создал для нас файл с миграцией для базы данных. Набери python manage.py migrate blog , результат должен быть следующим:

Ура! Модель записи для блога теперь в базе данных, и было бы неплохо посмотреть на неё, верно? Тогда переходи к следующей главе!

Django: Размещайте логику вне шаблонов (и представлений)

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

Я действительно не понимал, что это значит, пока не начал страдать от последствий наличия логики в моих файлах .html. После трех лет работы с Django я теперь стараюсь держать бизнес-логику подальше не только от шаблонов, но и от представлений.

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

Приложение: простой блог

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

Пусть у нас будет такая простая модель:

Худший вариант: логика в шаблонах

Допустим на главной странице блога index.html мы хотим отобразить заголовки последних 10 постов и дату их публикации. Заголовок также должен быть ссылкой на представление post-detail, где будет отображаться контент поста.


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

В index.html мы проверяем, является ли request.user администратором, и если так и есть, мы не фильтруем никаких сообщений. В блоке elif, который применяется ко всем остальным посетителям, мы проверяем свойство is_draft, равное False, перед отображением сообщения:

Мы также добавляем некоторую разметку Bootstrap, чтобы администратор мог ясно видеть, является ли определенный пост черновиком или его публикация запланировано на будущее. Нам не нужна эта разметка для обычных посетителей, потому что они не должны видеть эти сообщения.

На самом деле подобный дизайн просто ужасен по нескольким причинам:

  1. Нет разделения интересов: почему шаблон решает, какие сообщения показывать?
  2. Нарушает принцип DRY (не повторяй себя): взгляни на тег span, содержащий дату. Мы повторяем его в обоих пунктах нашего оператора if.
  3. Многословие: наш index.html отображает только ссылки на наши сообщения, но он уже выглядит очень загроможденным.
  4. Читаемость и удобство обслуживания: шаблонизаторы Jinja/Django очень полезны и функциональны, но так же они известны загроможденным синтаксисом. Если вы вернетесь к этому шаблону через 6 месяцев, сможете ли вы быстро разобраться, что происходит? Вспомните ли вы, что если вы добавите div содержащий имя автора сообщения, вы должны будете сделать это в обоих пунктах if?

Лучший способ

Если вместо этого мы напишем как то так:

Тогда наш файл index.html будет выглядеть как то так:

В этом случае мы разместили бизнес-логику вне шаблона. Основной шаблон должен отвечать только за отображение элементов.

Что мы получаем в этом случае:

  • DRYness: мы больше не повторяем HTML для рендеринга поста.
  • Возможность повторного использования: поскольку index.html больше не принимает решение о том, отображать ли сообщение, мы можем использовать его позже в других представлениях (например, в архиве).
  • Удобочитаемость: теперь гораздо понятнее, что происходит в index.html, и его будет легче понять, когда мы вернемся к нему в будущем.

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

Пусть наш блог будет развиваться и допустим нам потребовалось создать новое представление featured_posts, в котором мы хотим отображать только сообщения, помеченные как выделенные, используя поле is_highlighted в модели.

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

Мы уже видим тут две проблемы:

  1. Наш код становится все более детализированным, а в нем всего два поля для фильтрации. Представьте себе 3 или 4 (например, авторы и теги). В реальных приложениях у вас часто будет больше полей для фильтрации.
  2. Мы переносим детали реализации наших моделей в наши представления: теперь наше представление должно знать, что в наших моделях есть поле is_highlighted.

Что еще хуже, подумайте о том, что произойдет, если мы решим, что сообщения, размещаемые в разделе featured_posts, должны соответствовать еще двум критериям:

  • is_published должно быть True
  • счетчик likes должен быть не менее 3

Теперь нам нужно обновить код в двух наших представлениях, чтобы включить новые критерии:

Теперь представьте себе, какую работу вы должны выполнить, когда у вас есть 7 представлений и еще два критерия для фильтрации.

Еще более лучшие способы

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

Методы класса

Мы добавили в модель два метода, которые мы можем использовать в наших представлениях следующим образом:

Посмотрите, насколько чище становится наша панель с этими изменениями:

Более того, изменить наши критерии для того, что считается «featured» публикацией, так же просто, как изменить одну строку в Post.featured():

Теперь все представления, которые вызывают этот метод модели, будут обновлены соответствующим образом.

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

Вот почему мы использовали логический оператор AND (&):

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

Индивидуальные менеджеры моделей

Я не буду вдаваться в подробности о менеджерах (managers) и querysets, поскольку это выходит за рамки этого поста. Давайте избавимся от наших методов модели и определим наш файл models.py следующим образом:

Следует отметить поле objects, которое мы добавили в Post, которое инструктирует эту модель использовать PostQuerySet в качестве менеджера.

Теперь рассмотрим наше представление панели инструментов:

Обратите внимание на то, что эти два метода менеджера теперь связаны друг с другом:

Используя PostQuerySet в файле models.py, мы расширяем имеющиеся у нас методы менеджера, поэтому наряду с get, filter, aggregate и т. д. теперь мы имеем published и featured.

Преимущества использования менеджеров моделей перед методами классов:

  1. Цепочность и ясность: Post.objects.featured().published() выглядит более питонным и естественным, чем Post.featured() & Post.published().
  2. Возможность повторного использования: во многих случаях вы можете повторно использовать один и тот же менеджер для более чем одной модели. Возможно, в будущем вы создадите модель ShortNote, и вы сможете управлять ей с помощью того же PostQuerySet. При использовании методов модели вам придется переопределить пользовательские фильтры внутри вашей модели ShortNote.

Есть еще несколько преимуществ, таких как возможность определения нескольких менеджеров в одной модели, но они выходят за рамки этого поста.

Заключение

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

Загрузка файлов в Django | Создание загрузочной формы

В данном руководстве показано, как реализовать загрузку файлов с Django. Для этого создадим простой клон Instagram, который будет работать с изображениями.

Содержание

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

Для простоты папку с обучающим примером лучше поместить на рабочем столе, но в общем и целом расположение особого значения не имеет. Главное, чтобы директория проекта была легко доступной.

Откройте командную строку и создайте директорию insta для хранения файлов. Для установки как Django, так и Pillow мы будем использовать Pipenv . Pillow является библиотекой для обработки изображений. Для загрузки других типов файлов Pillow не понадобится.

Активируем новую виртуальную среду:

Об активации виртуальной среды сообщит изменение в (insta) . Вы также можете в любое время ввести команду exit для выхода и pipenv shell для повторного входа.

Создание проекта и приложения в Django

Создадим новый проект Django под названием insta_project и новое приложение, которое назовем posts .


Так как мы добавили новое приложение, мы должны сообщить об этом Django в нижней части конфигурации INSTALLED_APPS в settings.py .

Теперь запускаем python manage.py migrate для установки базы данных нового проекта.

Создаем модели в Django

Лучше всего начинать с модели базы данных. В нашем случае у модели Post будет только два поля: title и cover . Ниже мы также добавим метод __str__() , чтобы title отобразился в интерфейсе администратора Django.

Местоположение загружаемых файлов image будет в MEDIA_ROOT/images . В Django локацией для MEDIA_ROOT по умолчанию является папка, откуда будут загружаться все файлы пользователя.

В случае, когда вместо изображения требуется загрузить другой файл, нужно просто поменять ImageField на FileField .

Настройка MEDIA_ROOT в Django 2

Откройте insta_project/settings.py в вашем текстовом редакторе. Нам потребуется добавить две новые конфигурации. По умолчанию MEDIA_URL и MEDIA_ROOT являются пустыми и не отображаются на экране, поэтому их необходимо настроить:

  • MEDIA_ROOT является путем файловой системы, куда пользователи будут загружать файлы;
  • MEDIA_URL представляет собой URL, который мы можем использовать в шаблонах для файлов.

Название media использовать не обязательно, можно выбрать любое, просто в Django media используется по умолчанию. Мы также создаем папку images внутри для простоты навигации.

Панель администратора (админка) в Django

Сейчас мы обновим файл posts/admin.py , после чего в Django появится возможность использовать приложение Post от имени администратора.

Все настроено! Генерируем новый файл миграции.

Теперь запускаем migrate для обновления базы данных.

Теперь можно создать аккаунт superuser для доступа к интерфейсу администратора, после чего выполнить runserver для первого запуска локального веб-сервера:

Если набрать в адресной строке браузера http://127.0.0.1:8000/admin , появится возможность зайти в админку Django. Вы будете направлены на следующую страницу:

Нажмите на + Add возле Posts . Здесь можно добавить все, что хотите, однако в данном руководстве мы создаем запись с изображением талисмана Django — пони.

Скачать картинку можно тут Django Pony.

После нажатия «Save» вы будете перенаправлены на страницу Posts , где расположены все имеющиеся записи.

Теперь если вы загляните в папку media в вашем проекте, то увидите, что в директории images появилось изображение djangopony.png . Как и было сказано ранее, MEDIA_URL нужен именно для этого.

Итак, с основами мы разобрались. Теперь разберемся с тем, как отображать записи, использовать urls.py , views.py и шаблоны файлов.

Настройка urls.py в Django

Аспектом работы с Django, который может несколько запутать, является тот факт, что зачастую для одной веб-страницы требуется 4 разных, но взаимосвязанных файла: models.py , urls.py , views.py и html-шаблоны. Здесь мы будем разбирать понятия в следующем порядке: модели (models) -> urls -> представления (views) -> шаблоны (templates). С моделью мы уже разобрались, так что перейдем к URL.

Нам понадобятся обновить файл urls.py . Вначале на проектном уровне insta_project/urls.py мы добавим импорты для settings , include и static .

После этого мы определим путь для приложения posts . Стоит отметить, что если настройки в режиме DEBUG , то MEDIA_URL также нужно добавить. В противном случае не получится увидеть загружаемые изображения.

Documentation

Models¶

A model is the single, definitive source of information about your data. It contains the essential fields and behaviors of the data you’re storing. Generally, each model maps to a single database table.

  • Each model is a Python > django.db.models.Model .
  • Each attribute of the model represents a database field.
  • With all of this, Django gives you an automatically-generated database-access API; see Making queries .

Quick example¶

This example model defines a Person , which has a first_name and last_name :

first_name and last_name are fields of the model. Each field is specified as a class attribute, and each attribute maps to a database column.

The above Person model would create a database table like this:

Some technical notes:

  • The name of the table, myapp_person , is automatically derived from some model metadata but can be overr >Table names for more details.
  • An id field is added automatically, but this behavior can be overr >Automatic primary key fields .
  • The CREATE TABLE SQL in this example is formatted using PostgreSQL syntax, but it’s worth noting Django uses SQL tailored to the database backend specified in your settings file .

Using models¶

Once you have defined your models, you need to tell Django you’re going to use those models. Do this by editing your settings file and changing the INSTALLED_APPS setting to add the name of the module that contains your models.py .

For example, if the models for your application live in the module myapp.models (the package structure that is created for an application by the manage.py startapp script), INSTALLED_APPS should read, in part:

When you add new apps to INSTALLED_APPS , be sure to run manage.py migrate , optionally making migrations for them first with manage.py makemigrations .

Fields¶

Field types¶

Each field in your model should be an instance of the appropriate Field class. Django uses the field class types to determine a few things:

  • The column type, which tells the database what kind of data to store (e.g. INTEGER , VARCHAR , TEXT ).
  • The default HTML widget to use when rendering a form field (e.g. type=»text»> , ).
  • The minimal validation requirements, used in Django’s admin and in automatically-generated forms.

Django ships with dozens of built-in field types; you can find the complete list in the model field reference . You can easily write your own fields if Django’s built-in ones don’t do the trick; see Writing custom model fields .

Field options¶

There’s also a set of common arguments available to all field types. All are optional. They’re fully explained in the reference , but here’s a quick summary of the most often-used ones:

null If True , Django will store empty values as NULL in the database. Default is False . blank

If True , the field is allowed to be blank. Default is False .

A sequence of 2-tuples to use as choices for this field. If this is given, the default form widget will be a select box instead of the standard text field and will limit choices to the choices given.

A choices list looks like this:

A new migration is created each time the order of choices changes.

The first element in each tuple is the value that will be stored in the database. The second element is displayed by the field’s form widget.

Given a model instance, the display value for a field with choices can be accessed using the get_FOO_display() method. For example:


If True , this field is the primary key for the model.

The primary key field is read-only. If you change the value of the primary key on an existing object and then save it, a new object will be created alongside the old one. For example:

Again, these are just short descriptions of the most common field options. Full details can be found in the common model field option reference .

Automatic primary key fields¶

By default, Django gives each model the following field:

This is an auto-incrementing primary key.

If you’d like to specify a custom primary key, just specify primary_key=True on one of your fields. If Django sees you’ve explicitly set Field.primary_key , it won’t add the automatic id column.

Each model requires exactly one field to have primary_key=True (either explicitly declared or automatically added).

Verbose field names¶

Each field type, except for ForeignKey , ManyToManyField and OneToOneField , takes an optional first positional argument – a verbose name. If the verbose name isn’t given, Django will automatically create it using the field’s attribute name, converting underscores to spaces.

In this example, the verbose name is «person’s first name» :

In this example, the verbose name is «first name» :

The convention is not to capitalize the first letter of the verbose_name . Django will automatically capitalize the first letter where it needs to.

Relationships¶

Clearly, the power of relational databases lies in relating tables to each other. Django offers ways to define the three most common types of database relationships: many-to-one, many-to-many and one-to-one.

Many-to-one relationships¶

To define a many-to-one relationship, use django.db.models.ForeignKey . You use it just like any other Field type: by including it as a class attribute of your model.

ForeignKey requires a positional argument: the class to which the model is related.

For example, if a Car model has a Manufacturer – that is, a Manufacturer makes multiple cars but each Car only has one Manufacturer – use the following definitions:

You can also create recursive relationships (an object with a many-to-one relationship to itself) and relationships to models not yet defined ; see the model field reference for details.

It’s suggested, but not required, that the name of a ForeignKey field ( manufacturer in the example above) be the name of the model, lowercase. You can, of course, call the field whatever you want. For example:

ForeignKey fields accept a number of extra arguments which are explained in the model field reference . These options help define how the relationship should work; all are optional.

For details on accessing backwards-related objects, see the Following relationships backward example .

Many-to-many relationships¶

To define a many-to-many relationship, use ManyToManyField . You use it just like any other Field type: by including it as a class attribute of your model.

ManyToManyField requires a positional argument: the class to which the model is related.

For example, if a Pizza has multiple Topping objects – that is, a Topping can be on multiple pizzas and each Pizza has multiple toppings – here’s how you’d represent that:

As with ForeignKey , you can also create recursive relationships (an object with a many-to-many relationship to itself) and relationships to models not yet defined .

It’s suggested, but not required, that the name of a ManyToManyField ( toppings in the example above) be a plural describing the set of related model objects.

It doesn’t matter which model has the ManyToManyField , but you should only put it in one of the models – not both.

Generally, ManyToManyField instances should go in the object that’s going to be edited on a form. In the above example, toppings is in Pizza (rather than Topping having a pizzas ManyToManyField ) because it’s more natural to think about a pizza having toppings than a topping being on multiple pizzas. The way it’s set up above, the Pizza form would let users select the toppings.

Цукерберг рекомендует:  Хочу всё знать. Язык Solidity

ManyToManyField fields also accept a number of extra arguments which are explained in the model field reference . These options help define how the relationship should work; all are optional.

Extra fields on many-to-many relationships¶

When you’re only dealing with simple many-to-many relationships such as mixing and matching pizzas and toppings, a standard ManyToManyField is all you need. However, sometimes you may need to associate data with the relationship between two models.

For example, cons > ManyToManyField to represent this relationship. However, there is a lot of detail about the membership that you might want to collect, such as the date at which the person joined the group.

For these situations, Django allows you to specify the model that will be used to govern the many-to-many relationship. You can then put extra fields on the intermediate model. The intermediate model is associated with the ManyToManyField using the through argument to point to the model that will act as an intermediary. For our musician example, the code would look something like this:

When you set up the intermediary model, you explicitly specify foreign keys to the models that are involved in the many-to-many relationship. This explicit declaration defines how the two models are related.

There are a few restrictions on the intermediate model:

Now that you have set up your ManyToManyField to use your intermediary model ( Membership , in this case), you’re ready to start creating some many-to-many relationships. You do this by creating instances of the intermediate model:

You can also use add() , create() , or set() to create relationships, as long as you specify through_defaults for any required fields:

You may prefer to create instances of the intermediate model directly.

If the custom through table defined by the intermediate model does not enforce uniqueness on the (model1, model2) pair, allowing multiple values, the remove() call will remove all intermediate model instances:

The clear() method can be used to remove all many-to-many relationships for an instance:

Once you have established the many-to-many relationships, you can issue queries. Just as with normal many-to-many relationships, you can query using the attributes of the many-to-many-related model:

As you are using an intermediate model, you can also query on its attributes:

If you need to access a membership’s information you may do so by directly querying the Membership model:

Another way to access the same information is by querying the many-to-many reverse relationship from a Person object:

One-to-one relationships¶

To define a one-to-one relationship, use OneToOneField . You use it just like any other Field type: by including it as a class attribute of your model.

This is most useful on the primary key of an object when that object “extends” another object in some way.

OneToOneField requires a positional argument: the class to which the model is related.

For example, if you were building a database of “places”, you would build pretty standard stuff such as address, phone number, etc. in the database. Then, if you wanted to build a database of restaurants on top of the places, instead of repeating yourself and replicating those fields in the Restaurant model, you could make Restaurant have a OneToOneField to Place (because a restaurant “is a” place; in fact, to handle this you’d typically use inheritance , which involves an implicit one-to-one relation).

OneToOneField fields also accept an optional parent_link argument.

OneToOneField > primary_key argument if you like). Thus, it’s now possible to have multiple fields of type OneToOneField on a single model.

Models across files¶

It’s perfectly OK to relate a model to one from another app. To do this, import the related model at the top of the file where your model is defined. Then, just refer to the other model class wherever needed. For example:

Field name restrictions¶


Django places some restrictions on model field names:

A field name cannot be a Python reserved word, because that would result in a Python syntax error. For example:

A field name cannot contain more than one underscore in a row, due to the way Django’s query lookup syntax works. For example:

A field name cannot end with an underscore, for similar reasons.

These limitations can be worked around, though, because your field name doesn’t necessarily have to match your database column name. See the db_column option.

SQL reserved words, such as join , where or select , are allowed as model field names, because Django escapes all database table names and column names in every underlying SQL query. It uses the quoting syntax of your particular database engine.

Custom field types¶

Meta options¶

Give your model metadata by using an inner class Meta , like so:

Model metadata is “anything that’s not a field”, such as ordering options ( ordering ), database table name ( db_table ), or human-readable singular and plural names ( verbose_name and verbose_name_plural ). None are required, and adding class Meta to a model is completely optional.

A complete list of all possible Meta options can be found in the model option reference .

Model attributes¶

Model methods¶

Define custom methods on a model to add custom “row-level” functionality to your objects. Whereas Manager methods are intended to do “table-wide” things, model methods should act on a particular model instance.

This is a valuable technique for keeping business logic in one place – the model.

For example, this model has a few custom methods:

The last method in this example is a property .

The model instance reference has a complete list of methods automatically given to each model . You can override most of these – see overriding predefined model methods, below – but there are a couple that you’ll almost always want to define:

A Python “magic method” that returns a string representation of any object. This is what Python and Django will use whenever a model instance needs to be coerced and displayed as a plain string. Most notably, this happens when you display an object in an interactive console or in the admin.

You’ll always want to define this method; the default isn’t very helpful at all.

This tells Django how to calculate the URL for an object. Django uses this in its admin interface, and any time it needs to figure out a URL for an object.

Any object that has a URL that uniquely identifies it should define this method.

Overriding predefined model methods¶

There’s another set of model methods that encapsulate a bunch of database behavior that you’ll want to customize. In particular you’ll often want to change the way save() and delete() work.

You’re free to override these methods (and any other model method) to alter behavior.

A > save() for documentation of the parameters it accepts):

You can also prevent saving:

It’s important to remember to call the super > super().save(*args, **kwargs) business – to ensure that the object still gets saved into the database. If you forget to call the superclass method, the default behavior won’t happen and the database won’t get touched.

It’s also important that you pass through the arguments that can be passed to the model method – that’s what the *args, **kwargs bit does. Django will, from time to time, extend the capabilities of built-in model methods, adding new arguments. If you use *args, **kwargs in your method definitions, you are guaranteed that your code will automatically support those arguments when they are added.

Overridden model methods are not called on bulk operations

Note that the delete() method for an object is not necessarily called when deleting objects in bulk using a QuerySet or as a result of a cascading delete . To ensure customized delete logic gets executed, you can use pre_delete and/or post_delete signals.

Unfortunately, there isn’t a workaround when creating or updating objects in bulk, since none of save() , pre_save , and post_save are called.

Executing custom SQL¶

Another common pattern is writing custom SQL statements in model methods and module-level methods. For more details on using raw SQL, see the documentation on using raw SQL .

Model inheritance¶

The only decision you have to make is whether you want the parent models to be models in their own right (with their own database tables), or if the parents are just holders of common information that will only be visible through the child models.

There are three styles of inheritance that are possible in Django.

  1. Often, you will just want to use the parent >Abstract base classes are what you’re after.
  2. If you’re sub >Multi-table inheritance is the way to go.
  3. Finally, if you only want to modify the Python-level behavior of a model, without changing the models fields in any way, you can use Proxy models .

Abstract base classes¶

Abstract base > abstract=True in the Meta class. This model will then not be used to create any database table. Instead, when it is used as a base class for other models, its fields will be added to those of the child class.

The Student model will have three fields: name , age and home_group . The CommonInfo model cannot be used as a normal Django model, since it is an abstract base class. It does not generate a database table or have a manager, and cannot be instantiated or saved directly.

Fields inherited from abstract base > None .

For many uses, this type of model inheritance will be exactly what you want. It provides a way to factor out common information at the Python level, while still only creating one database table per child model at the database level.

Meta inheritance¶

When an abstract base >Meta inner >Meta >Meta . If the child wants to extend the parent’s Meta class, it can subclass it. For example:

Django does make one adjustment to the Meta >Meta attribute, it sets abstract=False . This means that children of abstract base > abstract=True each time.

To work around this problem, when you are using related_name or related_query_name in an abstract base > ‘%(app_label)s’ and ‘%(class)s’ .

  • ‘%(class)s’ is replaced by the lowercased name of the child > ‘%(app_label)s’ is replaced by the lowercased name of the app the child class is contained within. Each installed application name must be unique and the model class names within each app must also be unique, therefore the resulting name will end up being different.

For example, given an app common/models.py :

Along with another app rare/models.py :

The reverse name of the common.ChildA.m2m field will be common_childa_related and the reverse query name will be common_childas . The reverse name of the common.ChildB.m2m field will be common_childb_related and the reverse query name will be common_childbs . Finally, the reverse name of the rare.ChildB.m2m field will be rare_childb_related and the reverse query name will be rare_childbs . It’s up to you how you use the ‘%(class)s’ and ‘%(app_label)s’ portion to construct your related name or related query name but if you forget to use it, Django will raise errors when you perform system checks (or run migrate ).

Multi-table inheritance¶

All of the fields of Place will also be available in Restaurant , although the data will reside in a different database table. So these are both possible:


If you have a Place that is also a Restaurant , you can get from the Place object to the Restaurant object by using the lowercase version of the model name:

However, if p in the above example was not a Restaurant (it had been created directly as a Place object or was the parent of some other > p.restaurant would raise a Restaurant.DoesNotExist exception.

The automatically-created OneToOneField on Restaurant that links it to Place looks like this:

Meta and multi-table inheritance¶

In the multi-table inheritance situation, it doesn’t make sense for a child >Meta >Meta options have already been applied to the parent class and applying them again would normally only lead to contradictory behavior (this is in contrast with the abstract base class case, where the base class doesn’t exist in its own right).

If the parent has an ordering and you don’t want the child to have any natural ordering, you can explicitly disable it:

Inheritance and reverse relations¶

This results in the error:

Adding related_name to the customers field as follows would resolve the error: models.ManyToManyField(Place, related_name=’provider’) .

Proxy models¶

When using multi-table inheritance , a new database table is created for each subclass of a model. This is usually the desired behavior, since the subclass needs a place to store any additional data fields that are not present on the base class. Sometimes, however, you only want to change the Python behavior of a model – perhaps to change the default manager, or add a new method.

This is what proxy model inheritance is for: creating a proxy for the original model. You can create, delete and update instances of the proxy model and all the data will be saved as if you were using the original (non-proxied) model. The difference is that you can change things like the default model ordering or the default manager in the proxy, without having to alter the original.

Proxy models are declared like normal models. You tell Django that it’s a proxy model by setting the proxy attribute of the Meta > True .

For example, suppose you want to add a method to the Person model. You can do it like this:

The MyPerson > Person > Person will also be accessible through MyPerson , and vice-versa:

You could also use a proxy model to define a different default ordering on a model. You might not always want to order the Person model, but regularly order by the last_name attribute when you use the proxy. This is easy:

Now normal Person queries will be unordered and OrderedPerson queries will be ordered by last_name .

Proxy models inherit Meta attributes in the same way as regular models .

QuerySet s still return the model that was requested¶

There is no way to have Django return, say, a MyPerson object whenever you query for Person objects. A queryset for Person objects will return those types of objects. The whole point of proxy objects is that code relying on the original Person will use those and your own code can use the extensions you included (that no other code is relying on anyway). It is not a way to replace the Person (or any other) model everywhere with something of your own creation.

Base class restrictions¶

A proxy model must inherit from exactly one non-abstract model class. You can’t inherit from multiple non-abstract models as the proxy model doesn’t provide any connection between the rows in the different database tables. A proxy model can inherit from any number of abstract model classes, providing they do not define any model fields. A proxy model may also inherit from any number of proxy models that share a common non-abstract parent class.

Proxy model managers¶

If you don’t specify any model managers on a proxy model, it inherits the managers from its model parents. If you define a manager on the proxy model, it will become the default, although any managers defined on the parent classes will still be available.

Continuing our example from above, you could change the default manager used when you query the Person model like this:

If you wanted to add a new manager to the Proxy, without replacing the existing default, you can use the techniques described in the custom manager documentation: create a base class containing the new managers and inherit that after the primary base class:

You probably won’t need to do this very often, but, when you do, it’s possible.

Differences between proxy inheritance and unmanaged models¶

Proxy model inheritance might look fairly similar to creating an unmanaged model, using the managed attribute on a model’s Meta class.

With careful setting of Meta.db_table you could create an unmanaged model that shadows an existing model and adds Python methods to it. However, that would be very repetitive and fragile as you need to keep both copies synchronized if you make any changes.

On the other hand, proxy models are intended to behave exactly like the model they are proxying for. They are always in sync with the parent model since they directly inherit its fields and managers.

The general rules are:

  1. If you are mirroring an existing model or database table and don’t want all the original database table columns, use Meta.managed=False . That option is normally useful for modeling database views and tables not under the control of Django.
  2. If you are wanting to change the Python-only behavior of a model, but keep all the same fields as in the original, use Meta.proxy=True . This sets things up so that the proxy model is an exact copy of the storage structure of the original model when data is saved.

Multiple inheritance¶

Just as with Python’s sub >Meta ) appears in will be the one that is used; for example, this means that if multiple parents contain a Meta class, only the first one is going to be used, and all others will be ignored.

Generally, you won’t need to inherit from multiple parents. The main use-case where this is useful is for “mix-in” classes: adding a particular extra field or method to every class that inherits the mix-in. Try to keep your inheritance hierarchies as simple and straightforward as possible so that you won’t have to struggle to work out where a particular piece of information is coming from.

Note that inheriting from multiple models that have a common id primary key field will raise an error. To properly use multiple inheritance, you can use an explicit AutoField in the base models:

Or use a common ancestor to hold the AutoField . This requires using an explicit OneToOneField from each parent model to the common ancestor to avoid a clash between the fields that are automatically generated and inherited by the child:

Field name “hiding” is not permitted¶

In normal Python > author , you can’t create another model field or define an attribute called author in any class that inherits from that base class.

This restriction doesn’t apply to model fields inherited from an abstract model. Such fields may be overr > field_name = None .

Some fields define extra attributes on the model, e.g. a ForeignKey defines an extra attribute with _id appended to the field name, as well as related_name and related_query_name on the foreign model.

These extra attributes cannot be overridden unless the field that defines it is changed or removed so that it no longer defines the extra attribute.

Overr > Model.__init__ ) and serialization. These are features which normal Python class inheritance doesn’t have to deal with in quite the same way, so the difference between Django model inheritance and Python class inheritance isn’t arbitrary.

Цукерберг рекомендует:  Используем тег hr с фоновым изображением

This restriction only applies to attributes which are Field instances. Normal Python attributes can be overridden if you wish. It also only applies to the name of the attribute as Python sees it: if you are manually specifying the database column name, you can have the same column name appearing in both a child and an ancestor model for multi-table inheritance (they are columns in two different database tables).

Django will raise a FieldError if you override any model field in any ancestor model.

Organizing models in a package¶

The manage.py startapp command creates an application structure that includes a models.py file. If you have many models, organizing them in separate files may be useful.

To do so, create a models package. Remove models.py and create a myapp/models/ directory with an __init__.py file and the files to store your models. You must import the models in the __init__.py file.

For example, if you had organic.py and synthetic.py in the models directory:

Explicitly importing each model rather than using from .models import * has the advantages of not cluttering the namespace, making code more readable, and keeping code analysis tools useful.

The Models Reference Covers all the model related APIs including model fields, related objects, and QuerySet .

Работа с моделями Django

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

В программировании существует особая концепция, она называется объектно-ориентированным программированием . Идея заключается в том, что вместо скучной последовательности инструкций мы моделируем вещи и описываем, как они взаимодействуют друг с другом.


Так что же такое объект? Это совокупность поведения и свойств. Звучит странно, но мы приведем пример.

Если мы хотим смоделировать кошку, то создадим объект Cat , который обладает определенными свойствами, например, color (цвет), age (возраст), mood (настроение: плохое, хорошее, сонное ;)), owner (хозяин, например, другой объект — Person — или, если кошка дикая, это свойство будет пустым).

Объект Cat будет иметь набор определённых действий: purr (мурчать), scratch (царапаться) или feed (кормить, где мы дадим кошке немного CatFood — кошачьей еды, которая так же может быть отдельным объектом со своими свойствами, например, taste — вкусом).

Основная идея, таким образом, заключается в описании объекта в коде, используя его параметры ( свойства объекта ) и доступные ему действия ( методы ).

Так как же мы смоделируем запись в блоге? Нам же нужен блог, верно?

Для начала стоит ответить на вопрос: что такое запись в блоге? Какие свойства она имеет?

Ну, запись наверняка содержит какой-то текст и заголовок, верно? Было бы неплохо также знать, кто её написал — так что нам нужен автор. Ну и в заключении, нам нужно знать, когда запись создана и когда опубликована.

Какие вещи можно сделать с записью в блоге? Было бы неплохо иметь метод для её публикации, согласна?

Так что нам пригодится метод publish .

Ну и раз уж мы определились с тем, что хотим получить, давай начнем моделирование в Django!

Модель в Django

Зная, что представляет из себя объект, мы можем создать модель Django для записи в блоге.

Модель в Django — это объект определённого свойства: он хранится в базе данных . База данных представляет собой совокупность различных данных. Это то место, где ты будешь хранить информацию о своих пользователях, записях в блоге и т.д. Мы будем использовать базу данных SQLite для хранения информации. Это стандартная база данных в Django — её сейчас вполне хватит для наших нужд.

Ты можешь представить модель в базе данных как электронную таблицу с колонками (полями) и строками (данными).

Для аккуратности мы создадим отдельное приложение в нашем проекте. Очень удобно иметь хорошо организованное рабочее место с самого начала. Для создания приложения нам понадобится набрать следующую инструкцию в командной строке (из директории djangogirls , где находится файл manage.py ):

<% filename %>Mac OS X и Linux:

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

После того, как приложение создано, нам нужно сообщить Django, что теперь он должен его использовать. Мы сделаем это с помощью файла mysite/settings.py . Нам нужно найти INSTALLED_APPS и добавить к списку ‘blog’, прямо перед ] . Конечный результат должен выглядеть следующим образом:

Создание модели записи в блоге

В файле blog/models.py мы определяем все Модели — модель записи для блога также пойдёт сюда.

Открой файл blog/models.py , удали весь текст и вставь на его место следующий код:

Убедись, что использовала два символа нижнего подчёркивания ( _ ) с обеих сторон от метода str . Это соглашение часто используется при программировании на Python, и иногда его называют «dunder» (сокращение от англ. «double-underscore»).

Смотрится страшно, да? Но не волнуйся, мы объясним, что значит каждая строка кода!

Строки, начинающиеся с from или import , открывают доступ к коду из других файлов. Так что вместо того, чтобы копировать и вставлять один и тот же код во все файлы, ты можешь сослаться на него при помощи from . import . .

class Post(models.Model): — эта строка определяет нашу модель ( объект ).

  • class — это специальное ключевое слово для определения объектов.
  • Post — это имя нашей модели, мы можем поменять его при желании (специальные знаки и пробелы использовать нельзя). Всегда начинай имена классов с прописной буквы.
  • models.Model означает, что объект Post является моделью Django, так Django поймет, что он должен сохранить его в базу данных.

Дальше мы задаем свойства, о которых уже говорили: title , text , created_date , published_date и author . Чтобы это сделать, нам нужно определиться с типом полей (это текст? число? дата? ссылка на другой объект? например, на пользователя?).

  • models.CharField — так мы определяем текстовое поле с ограничением на количество символов.
  • models.TextField — так определяется поле для неограниченно длинного текста. Выглядит подходящим для содержимого поста, верно?
  • models.DateTimeField — дата и время.
  • models.ForeignKey — ссылка на другую модель.

Мы не будем объяснять каждую запятую, поскольку на это уйдет слишком много времени. Ознакомься с официальной документаций Django: если хочешь узнать больше о полях моделей и о том, как определять разные объекты, то эта ссылка может помочь: (https://docs.djangoproject.com/en/1.11/ref/models/fields/#field-types).

Что насчёт def publish(self): ? Это как раз метод публикации для записи, о котором мы говорили. def означает, что создаётся функция/метод, а publish — это название этого метода. Можно изменить имя метода, если хочешь. Существует правило для имён функций: нужно использовать строчные буквы, а пробелы заменять на подчёркивания. Например, метод, вычисляющий среднюю цену, может называться calculate_average_price .

Методы часто возвращают что-то. Например, метод __str__ . В наше случае после вызова метода __str__() мы получим текст (строку) с заголовком записи.

Также обрати внимание, что оба метода def publish(self): и def __str__(self): внутри класса имеют дополнительный отступ. Поскольку в Python важны отступы, нам необходимо использовать их для методов внутри класса — иначе методы не будут принадлежать к классу, и при запуске программы может получиться что-то неожиданное.

Если тема моделей тебе до сих пор непонятна — не стесняйся обратиться к тренеру! Мы знаем, что она действительно сложна, особенно когда ты изучаешь параллельно объекты и функции. Но мы надеемся, что всё это больше не кажется тебе магией!

Создаём таблицы моделей в базе данных

Последним шагом будет добавление нашей модели в базу данных. Сначала мы должны дать Django знать, что сделали изменения в нашей модели (мы её только что создали!). Набери python manage.py makemigrations blog . Должно получиться примерно так:

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

Django создал для нас файл с миграцией для базы данных. Набери python manage.py migrate blog , результат должен быть следующим:

Ура! Модель записи для блога теперь в базе данных, и было бы неплохо посмотреть на неё, верно? Тогда переходи к следующей главе!

Создание модели ORM, вывод из базы данных в шаблоны Django

Теория

В данном посте будем работать с базой данных в Django и в частности, рассмотрим такой механизм работы с БД, как ORM.

ORM(Object Relation Mapping) — Объектно ориентированное наложение базы данных. Т.е., это означает, что мы создаем в базе данных таблицы и связи на основе классов и их свойств, которых мы определяем в файле models.py в приложении Django.

Примечание. Процесс экспорта из models.py в базу данных в Django называется миграцией БД.

Плюс ORM в том, что вам не обязательно вникать в суть того, какие именно БД вы будете использовать. Достаточно установить в систему сервер БД и указать в файлах настройки, что мы будем использовать эту БД, а все ньюансы CRUD с базой данных возьмет на себя общий интерфейс ORM.

В дальнейшем, чтобы, делать выборку или чтобы хранить, редактировать или удалить данные мы будем использовать только функции ORM.

Каждый объект класса в models.py — это отдельная таблица в БД, который, после миграции используется в файле views.py для вывода в шаблон приложения.

Практика

Файл models.py

Сначала нам нужно создать таблицу постов с полями, для этого нужно открыть файл blog/models.py и создать там класс таблицы постов с полями

Класс Post имеет 6 членов. 4 свойства и 2 — метода:

  • title — поле заголовка;
  • slug — поле URL;
  • body — поле текста;
  • date_pub — поле даты;
  • get_absolute_url() — абсолютный URL на текущий пост;
  • __str__() — переопределение объекта на вывод нужного значения.

Значения полям устанавливаются при помощи объекта models, который реализует при помощи методов функционал ORM. В данном примере используются 4 видов данных:

  • models.CharField(…) — значение символьного поля;
  • models.SlugField(…) — значение поля постоянной ссылки;
  • models.TextField(…) — значение многострочного поля;
  • models.DateTimeField(…) — значение поля даты и времени.


Когда модель таблицы в виде класса готова, то самое время наложить эту модель на реальную БД и этим занимается процесс миграции Django. Миграция состоит из 2-х шагов операций и команд:

  • makemigrations — который отвечает за создание новых миграций на основе изменений, внесенных вами в ваши модели. После этой команды в корневой папке приложения, где есть изменения в моделях появится папка migrations, в которой будут все нужные файлы для дальнейшего их отражения в БД;
  • migrate — который несет ответственность за применение и неприменение миграций.

В консоли это выглядит так

После миграций можно эксперементировать в консоли с работой с созданной БД. Для входа в консоль shell можно выполнить команду

После данной команды консоль будет принимать уже команды python через >>> в пределах текущего проекта.

Некоторые команды работы с БД:

  • p=Post(title=’New post title 1′, slug = ‘new-post-1’, body = ‘Body text 1 …’) — заполнение нового экземпляра(строки) таблицы;
  • p.save() — сохранение экземпляра(строки) таблицы;
  • p. >Пример работы в консоли

Файл views.py

Файл views.py является связующим звеном между моделью и шаблоном. Открываем файл blog/views.py редактируем. Обновления в файле выделены

До этого мы грузили все посты из списка, теперь грузим из реального БД в виде posts_list(), а видом отдельного активного поста будет заниматься метод post_detail().

Наследование моделей в django

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

  • Абстрактное наследование — абстрактная модель не создает таблицы. Используется для сокращения объема кода и лаконичного стиля его написания
  • Multitable наследование — довольно интересный вид наследования. Это наследование требует особого внимания: родительская модель создает таблицу со всеми полями, как обычная модель. Но! Она будет хранить в себе кроме своих значений так же значения полей всех моделей, наследованных от нее. Что касается дочерних моделей, то они создают таблицы с полями, определенными непосредственно в них — без унаследованных полей — + поле с именем вида имяМодели_ptr_id. Это поле является первичным ключом к родительской модели и является уникальным.
  • Proxy-наследование — позволяет только переопределение поведения в дочерней модели
  • Так же считаю, стоит отметить работу с GenericForeignKey, хотя этот паттерн и не относится к наследованию моделей, но очень тесно с ним связан

Абстрактное наследование:

Абстрактное наследование реализуется через атрибут abstract подкласса Meta нашей модели, например:

В дальнейшем это может сократить нам количество кода для следующих моделей:

Хорошо. Не надо для каждой модели указывать поля Content, Title и From. На этом плюсы заканчиваются и начинаются ограничения:

  • Абстрактную модель нельзя добавить в ModelAdmin
  • К абстрактной модели нельзя создавать отношения ForeignKey (ну и ManyToMany соответственно)
  • Нельзя использовать константный related_name (для этого есть решение в виде ‘%(class)s’ )
  • Она не имеет менеджера и так далее

Она позволяет на уровне Python разделить общие данные, используя в то же время одну таблицу в базе данных

MultiTable наследование:

Допустим, у вас есть модель «Запись»:

И вы хотите расширить ее, добавив к ней поле Target:

Теперь в таблице будет создано две таблицы main_comment и main_note. Структуру таблиц я попробовал проиллюстрировать на скриншоте:

Такой подход к наследованию кардинально меняет дело, поскольку позволяет применять такой важный параметр ооп, как полиморфизм: снимаются почти все ограничения абстрактного наследования. Вы можете использовать родителей в ForeignKey, ModelAdmin и т д. При чем получая query_set базового класса — вы получаете и все объекты, которые от него унаследованы! То есть Note.objects.all() вернет и Comment-ы так же. Это похоже на полиморфизм в C# и C++. Единственное, чего не хватало мне, так это опции «абстрактный класс». Питон ввел это понятие абстрактных классов (ABC) довольно поздно (2.6), намного позже других строго типизированных языков. Но тем не менее, это стало для него хорошим паттерном.

Что дает понятие «абстрактного класса»? В C++ и C# абстрактный класс не может быть инициализирован, подобно абстрактной модели Джанго. Зачем это нужно? Вернемся к моделям Note, Comment и Article. И введем еще одну модель — Raiting:

Здесь мы хотим определить, что статьи и комментарии можно оценивать. Вто же время Note — это абстрактное понятие, которого не должно существовать в реальном мире. Если в мета-классе прописать abstract=True , то мы получим ошибку поля Target модели Raiting.

Поразмыслив, я пришел к следующему решению, которое решил использовать в своих проектах. Это решение нельзя назвать наиболее оптимальным, но оно работает. Сперва я просто экспериментировал: определил класс, наследованный от Model и переопределим ему save:

В целом он вполне себе отсеил Note. Но не буду же я писать для каждой модели-интерфейса класс-родитель. Чтобы унифицировать этот паттерн, я решил декларировать для проекта следующий протокол: если название модели начинается с заглавной I и вторая буква тоже заглавная (по рекомендациям написания кода в C#), то эта модель будет являться абстрактной. Просто — не правда ли? И мы можем это сделать так же через save:

Чтобы это использовать, достаточно унаследовать свою модель от DjangoInterface, и что в итоге мы получим:

Теперь мы сможем заполнять таблицу Note смело моделями Comment и Article и не бояться, что там будут Note, которых нет.

В принципе, чуть позже были найдены и альтернативные, более эффективные варианты без использования ABC. Их преимущество в том, что они могли бы определить атрибут isinterface для каждой модели на начало запуска программы и тогда не пришлось бы делать проверку при каждой инициализации модели. Но это уже детали реализации…

И да, забыл упомянуть, что модели джанго не умеют работать с ABC. Впрочем, если объединить ABC и ModelBase в одном классе через множественное наследование, может быть, и заработали бы…

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

Но есть некоторые отличия:

  1. При мультитэйбл наследовании дочерняя модель (ANewAbstr в нашем случае) не будет иметь обычной id-колонки в качестве первичного ключа, вместо нее будет колонка имяродительскоймодели_ptr_id , в нашем случае abstr_ptr_id. И вы не сможете обращаться с этой таблицей как с отдельной моделью через джанго-ОРМ.
  2. При OneToOne же — создается отдельная колонка для хранения id связанной модели — Child_id, а значит такая схема с OneToOne будет занимать больше места в БД.

Что еще может пригодиться? При работе с multitable-наследованием вы можете столкнуться с подобной ситуацией, когда надо преобразовать родительский класс в дочерний. Одно из решений выглядит так:

Как видно, это довольно лаконично и эффективно.

Proxy-наследование

Позволяет изменить поведение модели, но не более. Пример использования:

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

Но при этом абстрактное наследование обладает гораздо большим могуществом, поскольку позволяет создавать и наследовать поля моделей. А proxy — может менять только поведение.

Множественное наследование моделей

Множественное наследование можно разделить на множественное наследование с Mixin-классами и полноценное множественное наследование. Это довольно ответственная тема. Поэтому я прошу относиться к ней серьезно. Тем более, что в сети о нет почти никакой информации:

Полноценное множественное наследование Django

В целом оф. документация django не предусматривает множественное наследование моделей. При попытке это сделать, вы получите TypeError: Cannot create a consistent method resolution order (MRO). Для того, чтобы решить эту проблему, необходимо переопределить models.Model и унаследоваться от новой Model. При чем такое переопределение заслуживает отдельной статьи и глубокого знания python, в частности работы Metaclass-а

Множественное наследование модели с Mixin

В сети есть несколько тем по этому вопросу. Mixin изначально подразумевает какой-то класс, расширяющий функционал — что-то наподобие интерфейса с дефолтной реализацией. Например такой:

И в принципе это работает. Но это недокументированный способ. Он у меня работал ровно до того момента, пока я не стал делать миграции: джанго пытался мигрировать MixinModel. Ну а так как MixinModel — это не модель, то… у него ничего не получилось. И даже не пытайтесь обойти это в таком духе:

Получите TypeError: Cannot create a consistent method resolution order (MRO). А проблема решается довольно просто и встретить ее можно только на 2-м питоне. Для решения достаточно унаследовать MixinModel от object, и все проблемы решатся сами собой:

GenericForeignKey

GenericForeignKey — не является вариантом наследования моделей. Это скорее вариант полиморфизма.

Как мы знаем идея полиморфизма в статически типизированных языках тесно связана с наследованием, и без наследования не живет в принципе. В моделях джанго для реализации полиморфизма был придуман атрибут GenericForeignKey — он позволяет назначить полю ForeignKey любой существующей модели.

Как это работает?

Итак, GenericForeignKey — это универсальное поле джанго-orm, которое может указывать на любой объект. Для этого внутри модели создается три поля, которые условно назовем content_type типа ForeignKey, object_id типа PositiveIntegerField и content_object типа GenericForeignKey . content_type ссылается на модель ContentType- встроенную модель в джанго, которая содержит все модели (точнее классы моделей) приложения. object_id будет хранить id объекта в соответствующей таблице ContentType. Ну а GenericForeignKey объединяет их оба, пример:

Несколько громоздко, но работает. Один из минусов такого подхода в том, что нет ограничений на модели, как при классическом полиморфизме, и при недосмотре можно указать неверную модель для content_object.

В сети я нашел такое решение через limit_choices_to:

Оригинально не правда ли? Такой способ может быть альтернативой грамотному мультитэйбл наследованию. Или не может? — на этом у меня все. Свое мнение о том, что вы думаете по этому поводу, можете оставлять в комментариях

Понравилась статья? Поделиться с друзьями:
Все языки программирования для начинающих