Теория
В данном посте будем работать с базой данных в 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 и создать там класс таблицы постов с полями
from django.db import models from django.shortcuts import reverse # Create your models here. class Post(models.Model): title = models.CharField(max_length=150, db_index = True) slug = models.SlugField(max_length=150, unique = True) body = models.TextField(blank=True, db_index = True) date_pub = models.DateTimeField(auto_now_add = True) def get_absolute_url(self): return reverse('post_detail_url', kwargs={'slug': self.slug}) def __str__(self): return '{}'.format(self.title)
Класс 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 — который несет ответственность за применение и неприменение миграций.
В консоли это выглядит так
python3 manage.py makemigrations python3 manage.py migrate
После миграций можно эксперементировать в консоли с работой с созданной БД. Для входа в консоль shell можно выполнить команду
python3 manage.py shell
После данной команды консоль будет принимать уже команды python через >>> в пределах текущего проекта.
Некоторые команды работы с БД:
- p=Post(title=’New post title 1′, slug = ‘new-post-1’, body = ‘Body text 1 …’) — заполнение нового экземпляра(строки) таблицы;
- p.save() — сохранение экземпляра(строки) таблицы;
- p.id — вывод какой-либо колонки(ID, к примеру);
- print(p) — выведет то, что мы переопределили в методе __str__(self);
- dir(p) — вывод всех свойств и методов, доступных у экземпляра строки;
- p1=Post.objects.create(title=’New post title 2′, slug = ‘new-post-2’, body = ‘Body text 2 …’) — заполнение нового экземпляра(строки) таблицы через метода объекта;
- Post.objects.all() — получение списка всех экземпляров(строк) в таблице;
- Post.objects.get(slug__iexact = ‘new-post-1’) — получение какого-то экземпляра(строки) через регистронезависимую выборку;
- Post.objects.filter(slug__contains = ‘new’) — выборка через фильтр.
Пример работы в консоли
>>> from blog.models import Post >>> p=Post(title='New post title 1', slug = 'new-post-1', body = 'Body text 1 ...') >>> p <Post: New post title 1> >>> p.id >>> p.save() >>> p.id 1 >>> p.id 1 >>> print(p) New post title 1 >>> p <Post: New post title 1> >>> dir(p) ['DoesNotExist', 'MultipleObjectsReturned', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getstate__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__setstate__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', '_check_column_name_clashes', '_check_field_name_clashes', '_check_fields', '_check_id_field', '_check_index_together', '_check_indexes', '_check_local_fields', '_check_long_column_names', '_check_m2m_through_same_relationship', '_check_managers', '_check_model', '_check_model_name_db_lookup_clashes', '_check_ordering', '_check_property_name_related_field_accessor_clashes', '_check_single_primary_key', '_check_swappable', '_check_unique_together', '_do_insert', '_do_update', '_get_FIELD_display', '_get_next_or_previous_by_FIELD', '_get_next_or_previous_in_order', '_get_pk_val', '_get_unique_checks', '_meta', '_perform_date_checks', '_perform_unique_checks', '_save_parents', '_save_table', '_set_pk_val', '_state', 'body', 'check', 'clean', 'clean_fields', 'date_error_message', 'date_pub', 'delete', 'from_db', 'full_clean', 'get_deferred_fields', 'get_next_by_date_pub', 'get_previous_by_date_pub', 'id', 'objects', 'pk', 'prepare_database_save', 'refresh_from_db', 'save', 'save_base', 'serializable_value', 'slug', 'title', 'unique_error_message', 'validate_unique'] >>> p1=Post.objects.create(title='New post title 2', slug = 'new-post-2', body = 'Body text 2 ...') >>> p1 <Post: New post title 2> >>> Post.objects.all() <QuerySet [<Post: New post title 1>, <Post: New post title 2>]> >>> p1=Post.objects.create(title='New post title 3', slug = 'new-post-3', body = 'Body text 3 ...') >>> p1=Post.objects.create(title='New post title 4', slug = 'new-post-4', body = 'Body text 4 ...') >>> p1=Post.objects.create(title='New post title 5', slug = 'new-post-5', body = 'Body text 5 ...') >>> p1=Post.objects.create(title='New post title 6', slug = 'new-post-6', body = 'Body text 6 ...') >>> p1=Post.objects.create(title='New post title 7', slug = 'new-post-7', body = 'Body text 7 ...') >>> Post.objects.get(slug__iexact = 'new-post-1') <Post: New post title 1> >>> Post.objects.filter(slug__contains = 'new') <QuerySet [<Post: New post title 1>, <Post: New post title 2>, <Post: New post title 3>, <Post: New post title 4>, <Post: New post title 5>, <Post: New post title 6>, <Post: New post title 7>]> >>>
Файл views.py
Файл views.py является связующим звеном между моделью и шаблоном. Открываем файл blog/views.py редактируем. Обновления в файле выделены
from django.shortcuts import render from django.http import HttpResponse from .models import Post # Create your views here. def posts_list(request): posts = Post.objects.all() return render(request, 'blog/index.html', context = {'posts' : posts}) #return HttpResponse("<h1>Hello! Hello!</h1>") def post_detail(request, slug): post = Post.objects.get(slug__iexact = slug) return render(request, 'blog/post_detail.html', context = {'post' : post})
До этого мы грузили все посты из списка, теперь грузим из реального БД в виде posts_list(), а видом отдельного активного поста будет заниматься метод post_detail().
Файл urls.py
Открываем blog/urls.py существующему списку URL добавляем новы шаблон, по которому будет открываться отдельный пост. причем, в данном случае, в параметре slug будет хранить URL — поста. Этот параметр указан в угловых скобках, это означает на то, что оно будет переменной и попадется вторым параметром в метод вида, а в виде уже по этому уникальному slug будет сделана выборка из БД, данные которого потом через контекст передачи данных попадет в шаблон открытого поста
from django.urls import path from .views import * urlpatterns = [ path('', posts_list, name='posts_list_url'), path('<str:slug>', post_detail, name='post_detail_url'), ]
Файлы шаблонов
Открываем blog/templates/blog/index.html переписываем
{% extends 'blog/base_blog.html'%} {% block title %} Site Blog {% endblock %} {% block content %} <div> <div class="grid-x"> {% for post in posts %} <div class="cell small-12 medium-3 large-3"> <div class="card"> <img src="https://foundation.zurb.com/sites/docs/assets/img/generic/rectangle-1.jpg"> <div class="card-section"> <h4>{{ post.title }}</h4> <p>{{ post.body | truncatewords:15 }}</p> <!-- <a href="{% url 'post_detail_url' slug=post.slug %}" class="button">Learn More</a> --> <a href="{{ post.get_absolute_url }}" class="button">Learn More</a> <h6>{{ post.date_pub }}</h6> </div> </div> </div> {% endfor %} </div> </div> {% endblock %}
Создаем новый файл blog/templates/blog/post_detail.html и вставляем
{% extends 'blog/base_blog.html'%} {% block title %} Site Blog {% endblock %} {% block content %} <div> <div class="grid-x"> <div class="cell medium-1 large-2"> <div class="cell small-12 medium-5 large-4"> <div class="card"> <img src="https://foundation.zurb.com/sites/docs/assets/img/generic/rectangle-1.jpg"> <div class="card-section"> <h4>{{ post.title }} <b>{{ post.date_pub }}</b></h4> <p>{{ post.body}}</p> </div> </div> </div> <div class="cell medium-1 large-2"> </div> </div> {% endblock %}