Работа с формами Form, ModelForm и валидация данных в Django

👁 28 просмотров
1 Звезда2 Звезды3 Звезды4 Звезды5 Звезд (Пока оценок нет)
Загрузка...

В данном посте разберем работу с формами и то, как с ними работать в Django.

Первым делом нам нужно добавить новый модуль forms в наше приложение блога в папку blog/forms.py

from django import forms
class TagForm(forms.Form):
    title = forms.CharField(max_length = 50)
    slug = forms.CharField(max_length = 50)

В данном модуле мы объявляем новую форму через класс TagForm, который добавляет и редактирует теги блога. Объявление формы соответствует, в большинстве, объявлению модели, так как поля и их типы данных с формы должны соответствовать полям и их типам базы данных. Говоря абстрактно, форма — это некоторый портал, из которого данные поступаю в ячейки базы данных.

Анализ

Проверка строки JSON на ошибки в PHP

👁 29 просмотров
1 Звезда2 Звезды3 Звезды4 Звезды5 Звезд (Пока оценок нет)
Загрузка...

<?php
// Верная json-строка
$json[] = '{"Organization": "PHP Documentation Team"}';

// Неверная json-строка, которая вызовет синтаксическую ошибку,
// здесь в качестве кавычек мы используем ' вместо "
$json[] = "{'Organization': 'PHP Documentation Team'}";


foreach ($json as $string) {
    echo 'Декодируем: ' . $string;
    json_decode($string);

    switch (json_last_error()) {
        case JSON_ERROR_NONE:
            echo ' - Ошибок нет';
        break;
        case JSON_ERROR_DEPTH:
            echo ' - Достигнута максимальная глубина стека';
        break;
        case JSON_ERROR_STATE_MISMATCH:
            echo ' - Некорректные разряды или несоответствие режимов';
        break;
        case JSON_ERROR_CTRL_CHAR:
            echo ' - Некорректный управляющий символ';
        break;
        case JSON_ERROR_SYNTAX:
            echo ' - Синтаксическая ошибка, некорректный JSON';
        break;
        case JSON_ERROR_UTF8:
            echo ' - Некорректные символы UTF-8, возможно неверно закодирован';
        break;
        default:
            echo ' - Неизвестная ошибка';
        break;
    }

    echo PHP_EOL;
}

How to get specific menu items from Joomla 3.x via PHP

👁 30 просмотров
1 Звезда2 Звезды3 Звезды4 Звезды5 Звезд (Пока оценок нет)
Загрузка...

There are 2 ways you can do it:

Option 1:

Joomla loads menus every time page is loads. You can access the menus by calling the following methods.

// Get default menu - JMenu object, look at JMenu api docs
$menu = JFactory::getApplication()->getMenu();

// Get menu items - array with menu items
$items = $menu->getMenu();

// Look through the menu structure, once you understand it
// do a loop and find the link that you need.
var_dump($items);

This method is faster because you don’t need to query database. Simple operation in memory. 

Option 2:

Get it from the database. Either get menu link from jos_menu based on alias or something, or get article # from jos_content by article alias, then create the link

$db = JFactory::getDBO();

//  Load by menu alias
$query = "SELECT link FROM #__menu WHERE alias = 'privacy-policy'";
$db->setQuery($query);
$url = $db->loadResult();
$url = JRoute::_($url);


//  Load by article alias
$query = "SELECT id FROM #__content WHERE alias = 'privacy-policy'";
$db->setQuery($query);
$articleId = (int) $db->loadResult();
$url = JRoute::_("index.php?option=com_content&view=article&id=$articleId");

Работа в базой PostgreSQL в Linux

👁 30 просмотров
1 Звезда2 Звезды3 Звезды4 Звезды5 Звезд (Пока оценок нет)
Загрузка...

Вход в терминал psql

Psql — это интерактивный терминал для работы с Postgres. Имеется обилие флагов, доступных для использования при работе с psql, но сосредоточимся на некоторых из наиболее важных, а затем, как подключиться

  • -h флаг указания хоста;
  • -U флаг указания пользователя;
  • -p флаг указания порта (по умолчанию он равен 5432).

Для входа в терминал можно использовать следующую строку, которая запросит пароль

psql -h localhost -U username databasename

Другой вариант — использовать полную строку и позволить psql анализировать ее

psql "dbname=dbhere host=hosthere user=userhere password=pwhere port=5432 sslmode=require"

Если вы в терминале, где уже установлена рабочая версия PostgreSQL, то вы можете войти под пользователя posgres, который по умолчанию является суперадминистратором СУБД PostgreSQL

Еще один способ входа, но уже без пароля

sudo -U postgres psql

Как только вы подключитесь, вы можете начать уже делать запросы. В дополнение к основным запросам вы также можете использовать определенные команды. Команда \? предоставит вам список всех доступных команд.

Все по порядку про CRUD

Одна из хорошей вещей в PGSQL заключается в том, что он поставляется с некоторыми двоичными файлами, такими как createuser и createdb. Поэтому мы будем использовать их.

По умолчанию в Postgres сконфигурирован с единственным пользователем postgres, у которого есть все права на манипуляции в СУБД и является суперадминистратором.

Вход в консоль psql

sudo -u postgres psql

Вышеупомянутая команда получает интерфейс командной строки psql в режиме полного администрирования.

Работа с пользователями

Создание пользователя посредством бинарника createuser

sudo -u postgres createuser <username>

Задание пароля для пользователя

$ sudo -u postgres psql
psql=# ALTER USER <username> WITH ENCRYPTED PASSWORD '<password>';

Создание пользователя посредством терминала psql

psql=# CREATE USER youruser WITH ENCRYPTED PASSWORD 'yourpass';

Работа с базой данных

Создание базы данных посредством бинарника createdb

sudo -u postgres createdb <dbname>

Задание привилегий для пользователя на действия с базой данных

psql=# GRANT ALL PRIVILEGES ON DATABASE <dbname> TO <username>;

Создание базы данных посредством терминала psql

psql=# CREATE DATABASE <dbname>;

 

Установка прав пользователям

Установка пользователю роль администратора

psql=# GRANT admins TO <username>;

Чтобы дать все права пользователю на операции с базой данных можно воспользоваться командой psql

psql=# GRANT ALL PRIVILEGES ON DATABASE <dbname> TO <username>;

Тоже самое по другому

psql=# GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA <nameschema> TO <username>;

Разрешение на соединение

psql=# GRANT CONNECT ON DATABASE <namedb> TO <username>;

Можно комбинировать несколько привилегий

psql=# GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA <namescheme> TO <username>;

Если мы хотим дать привилегии на действия всем пользователям, то пишем так

psql=# GRANT ALL PRIVILEGES ON DATABASE <dbname> TO PUBLIC;

Если мы хотим дать привилегии на таблицы, то делаем так

psql=# ALTER DEFAULT PRIVILEGES IN SCHEMA <schemaname> GRANT ALL PRIVILEGES ON TABLES TO <username>;

 

Примечание. Ключевое слово SCHEMA означает тоже самое, что и имя базы данных.

Разрешение входа для пользователя postgres через phpPgAdmin

PostgresSQL по умолчанию имеет суперпользователя с логином postgres и вход через этот логин из вне запрещен. Поэтому, чтобы управлять базами через phpPgAdmin нам нужно войти, разрешив внешний вход. Делается через файл конфигурации phpPgAdmin, который называется config.inc.php.

В составе Vesta, данный файл конфигурации в Ubuntu находится по пути /etc/phppgadmin/config.inc.php.

В данном файле нужно поменять параметр $conf[‘extra_login_security’] поменять с true на false

    // If extra login security is true, then logins via phpPgAdmin with no
    // password or certain usernames (pgsql, postgres, root, administrator)
    // will be denied. Only set this false once you have read the FAQ and
    // understand how to change PostgreSQL's pg_hba.conf to enable
    // passworded local connections.
    $conf['extra_login_security'] = false;

Далее, если вы не знаете пароль супер администратора postgres, то есть возможность ее сменить через терминал

$ sudo -u postgres psql
$ postgres=# \password postgres

После данной команды сервер запросит ввести пароль и подтвердить еще одним вводом и дальше установится новый пароль, которым мы сможем войти через phpPgAdmin

Виды в виде классов(Class Based Views) и использование миксинов

👁 58 просмотров
1 Звезда2 Звезды3 Звезды4 Звезды5 Звезд (Пока оценок нет)
Загрузка...

Теория

В данном посте рассмотрим возможность обернуть методы вида в классы, иначе говоря в Class Based Views и воспользуемся миксинами, чтобы осуществить принцип DRY на уровне кода Python для Django.

В данном посте не будем добавлять в приложение новых фитч, а будем усовершенстововать код, который используется в файле blog/views.py.

Это усовершенствование преследует 2 цели:

  • расширить возможности функций-обработчиков видов заменив их классами;
  • уменьшить повторяемость.

Для расширения возможности видов в Django есть специальный класс Views в модуле django.views.generic, а уменьшение повторяемости можно достичь используя миксин в виде отдельного универсального класса для однотипных задач, которые имеют всего лишь разные значения.

Примечание. Mixin — это особый вид наследования в Python (и других объектно-ориентированных языках), и он начинает получать большую популярность в разработке Django / Web Application. Вы можете использовать Mixin, чтобы позволить классам на Python делиться методами между любым другим классом, который наследует от этого Mixin.

Использование наследуемых видов виде классов в дальнейшем нам потребуется, чтобы выполнять операции добавления и сохранения через формы в базу данных через POST — метод, учитывая, что за чтение данных из БД отвечает GET -метод. Без CBW мы использовали просто функции с методом GET.

В дополнении ко всему рассмотрим, как на уровне вида выдать ошибку 404, вместо стандартного Traceback Django.

Практика

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

Этап 1. Переход к CBV

На этом этапе заменим функции post_detail() и tag_detail() в blog/view.py на аналогичные по функциональности классы и поправим код в blog/urls.py.

Открываем файл blog/view.py и правим код. Сохраним все предыдущее состояние кода,для наглядности того, что поменяли

from django.shortcuts import render
from django.http import HttpResponse
from .models import Post, Tag
from django.views.generic import View
# 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>")
    
class PostDetail(View):
    def get(self, request, slug):
        post = Post.objects.get(slug__iexact=slug)
        return render(request, 'blog/post_detail.html', context={"post" : post})
    
# def post_detail(request, slug):
#     post = Post.objects.get(slug__iexact = slug)
#     return render(request, 'blog/post_detail.html', context = {'post' : post})

def tags_list(request):
    tags = Tag.objects.all()
    return render(request, 'blog/tags_list.html', context={'tags': tags})

class TagDetail(View):
      def get(self, request, slug):
          tag = Tag.objects.get(slug__iexact = slug)
          return render(request, 'blog/tag_detail.html', context={'tag': tag})
          
# def tag_detail(request, slug):
#     tag = Tag.objects.get(slug__iexact = slug)
#     return render(request, 'blog/tag_detail.html', context={'tag': tag})

Что делает данный код? Мы подключаем модуль django.views.generic и импортируем оттуда класс View, далее создаем 2 класса, которых наследуем от класса View, для переопределения функции get() в них. Этот метод соответствует методу запроса GET, поэтому он должен возвращать рендер шаблона вида с значениям постов и тегов, чтобы показать в браузере пользователя, отправившего этот запрос.

Теперь открываем файл blog/urls.py и правим метод передачи обработки по шаблону запроса маршрутизации с post_detail и tag_detail на PostDetail.as_view() и TagDetail.as_view() соответственно

from django.urls import path
from .views import *

urlpatterns = [
    path('', posts_list, name='posts_list_url'),
    #path('post/<str:slug>', post_detail, name='post_detail_url'),
    path('post/<str:slug>', PostDetail.as_view(), name='post_detail_url'),
    path('tags/', tags_list, name='tags_list_url'),
    #path('tag/<str:slug>', tag_detail, name='tag_detail_url'),
    path('tag/<str:slug>', TagDetail.as_view(), name='tag_detail_url'),
]

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

Этап 2. Генерация страницы 404

Открываем файл blog/views.py и меняем содержимое на такое

from django.shortcuts import render, get_object_or_404
from django.http import HttpResponse
from .models import Post, Tag
from django.views.generic import View
# 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>")
    
class PostDetail(View):
    def get(self, request, slug):
        #post = Post.objects.get(slug__iexact=slug)
        post = get_object_or_404(Post, slug__iexact=slug)
        #return render(request, 'blog/post_detail.html', context={"post" : post})
        return render(request, 'blog/post_detail.html', context={Post.__name__.lower(): post})
    
# def post_detail(request, slug):
#     post = Post.objects.get(slug__iexact = slug)
#     return render(request, 'blog/post_detail.html', context = {'post' : post})

def tags_list(request):
    tags = Tag.objects.all()
    return render(request, 'blog/tags_list.html', context={'tags': tags})

class TagDetail(View):
      def get(self, request, slug):
          #tag = Tag.objects.get(slug__iexact = slug)
          tag = get_object_or_404(Tag, slug__iexact=slug)
          #return render(request, 'blog/tag_detail.html', context={'tag': tag})
          return render(request, 'blog/tag_detail.html', context={Tag.__name__.lower(): tag})
      
# def tag_detail(request, slug):
#     tag = Tag.objects.get(slug__iexact = slug)
#     return render(request, 'blog/tag_detail.html', context={'tag': tag})

В общем, в данном файле мы все запросы к БД выполняем через функцию get_object-or_404(…), который внутри себя реализует запрос к БД и если ничего не найдено, то выдает страницу 404 вместо Traceback, который не понятен бывалому пользователю сайта

Генерация ошибки 404 для несуществующей страницы Django
Генерация ошибки 404 для несуществующей страницы Django

Конструкция Tag.__name__.lower() возвращает название класса(в данном случае название модели) с заглавной буквы, но нам нужны только строчные буквы и поэтому используем функцию lower() и далее передаем его в виде названием параметра словаря контекста. Это нам пригодится, что на следующем этапе определить для этих классов общий миксин.

Этап 3. Создание Миксина

Теперь создадим еще один файл blog/utils.py, в котором определим наш миксин в виде универсального класса для уменьшения кода, который будет унаследован классами с переопределенными свойствами

from django.shortcuts import render, get_object_or_404
from django.views.generic import View
from .models import *

class ObjectDetailMixin:
    model = None
    template = None
    
    def get(self, request, slug):
        obj = get_object_or_404(self.model, slug__iexact=slug)
        return render(request, self.template, context={self.model.__name__.lower(): obj})
        

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

  • model — класс модели;
  • template — шаблон модели.

Как мы видим, данный класс-миксин не содержит в себе каких-то индивидуальных параметров, которые бы его ограничивали в использовании. Он универсален для вывода и поста и для тега и это для нас может разыграть отличное условие, чтобы соответствовать принципу DRY в коде Python/Django.

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

from django.shortcuts import render, get_object_or_404
from django.http import HttpResponse
from .models import Post, Tag
from django.views.generic import View
from .utils import ObjectDetailMixin

# 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>")

class PostDetail(ObjectDetailMixin, View):
        model = Post
        template = 'blog/post_detail.html'
'''   
class PostDetail(View):
    def get(self, request, slug):
        #post = Post.objects.get(slug__iexact=slug)
        post = get_object_or_404(Post, slug__iexact=slug)
        #return render(request, 'blog/post_detail.html', context={"post" : post})
        return render(request, 'blog/post_detail.html', context={Post.__name__.lower(): post})
''' 
      
# def post_detail(request, slug):
#     post = Post.objects.get(slug__iexact = slug)
#     return render(request, 'blog/post_detail.html', context = {'post' : post})

def tags_list(request):
    tags = Tag.objects.all()
    return render(request, 'blog/tags_list.html', context={'tags': tags})

class TagDetail(ObjectDetailMixin, View):
        model = Tag
        template = 'blog/tag_detail.html'
        
'''
class TagDetail(View):
      def get(self, request, slug):
          #tag = Tag.objects.get(slug__iexact = slug)
          tag = get_object_or_404(Tag, slug__iexact=slug)
          #return render(request, 'blog/tag_detail.html', context={'tag': tag})
          return render(request, 'blog/tag_detail.html', context={Tag.__name__.lower(): tag})
'''
         
# def tag_detail(request, slug):
#     tag = Tag.objects.get(slug__iexact = slug)
#     return render(request, 'blog/tag_detail.html', context={'tag': tag})

Перезапускаем сервер и убеждаемся, что все работает также, как и прежде.

Облако тегов через связь ManyToMany и принцип DRY в Django

👁 56 просмотров
1 Звезда2 Звезды3 Звезды4 Звезды5 Звезд (Пока оценок нет)
Загрузка...

Теория

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

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

Практика

Файл models.py

Откроем blog/models.py и добавим новую таблицу Tag . Выделенные строки соответствуют местам изменения кода

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)
    tags = models.ManyToManyField('Tag', blank=True, related_name = 'posts')
    
    def get_absolute_url(self):
        return reverse('post_detail_url', kwargs={'slug': self.slug})
    
    def __str__(self):
        return '{}'.format(self.title)

class Tag(models.Model):
    title = models.CharField(max_length=50)
    slug = models.SlugField(max_length=50, unique = True)
    
    def get_absolute_url(self):
        return reverse('tag_detail_url', kwargs = {'slug': self.slug})
    def __str__(self):
        return '{}'.format(self.title)
        

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

python3 manage.py makemigrations
python3 manage.py migrate

 

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

python3 manage.py shell

И потренеруемся с добавлением и связкой тегов к постам

>>> from blog.models import *
>>> t = Tag.objects.create(title='Django', slug='django')
>>> t
<Tag: Django>
>>> Post.objects.values()
<QuerySet [{'id': 1, 'title': 'New post title 1', 'slug': 'new-post-1', 'body': 'Body text 1 ...', 'date_pub': datetime.datetime(2018, 11, 11, 18, 56, 18, 127958, tzinfo=<UTC>)}, {'id': 2, 'title': 'New post title 2', 'slug': 'new-post-2', 'body': 'Body text 2 ...', 'date_pub': datetime.datetime(2018, 11, 11, 19, 8, 32, 411534, tzinfo=<UTC>)}, {'id': 3, 'title': 'New post title 3', 'slug': 'new-post-3', 'body': 'Body text 3 ...', 'date_pub': datetime.datetime(2018, 11, 11, 19, 24, 47, 409418, tzinfo=<UTC>)}, {'id': 4, 'title': 'New post title 4', 'slug': 'new-post-4', 'body': 'Body text 4 ...', 'date_pub': datetime.datetime(2018, 11, 11, 19, 25, 15, 533403, tzinfo=<UTC>)}, {'id': 5, 'title': 'New post title 5', 'slug': 'new-post-5', 'body': 'Body text 5 ...', 'date_pub': datetime.datetime(2018, 11, 11, 19, 25, 30, 460405, tzinfo=<UTC>)}, {'id': 6, 'title': 'New post title 6', 'slug': 'new-post-6', 'body': 'Body text 6 ...', 'date_pub': datetime.datetime(2018, 11, 11, 19, 25, 47, 309093, tzinfo=<UTC>)}, {'id': 7, 'title': 'New post title 7', 'slug': 'new-post-7', 'body': 'Body text 7 ...', 'date_pub': datetime.datetime(2018, 11, 11, 19, 26, 1, 493016, tzinfo=<UTC>)}]>
>>> p = Post.objects.get(slug='new-post-1')
>>> p.title
'New post title 1'
>>> p.title = 'Post width tag'
>>> p.save()
>>> p.tags.add(t)
>>> p.tags.all()
<QuerySet [<Tag: Django>]>

Опишем некоторые команды

  • from blog.models import * — импортируем все модели;
  • t = Tag.objects.create(title=’Django’, slug=’django’) — создаем новый эксземпляр(для таблицы это будет строка) сущности
  • Post.objects.values() — выводим все значения в модели Post;
  • p = Post.objects.get(slug=’new-post-1′) — ищем посты по соответствию;
  • p.title — выводим заголовок поста;
  • p.title = ‘Post width tag’ — задаем новый заголовок для поста;
  • p.save() — сохраняем экземпляр с новым изменениями и перезаписываем в БД, как строку;
  • p.tags.add(t) — добавляем новый экземпляр тега к посту;
  • p.tags.all() — выводим все теги экземпляра поста.

 

Файл urls.py

Открываем файл blog/urls.py и задаем 2 дополнительных маршрутизации для вывода постов по тегу и списка всех тегов и плюс к этому добавим к ним обработчики для видов

from django.urls import path
from .views import *

urlpatterns = [
    path('', posts_list, name='posts_list_url'),
    path('post/<str:slug>', post_detail, name='post_detail_url'),
    path('tags/', tags_list, name='tags_list_url'),
    path('tag/<str:slug>', tag_detail, name='tag_detail_url'),
]

В данном случае, мы добавили 2 шаблона URL

  • tags — выведет все существующие теги в базе;
  • tag/<str:slug> —  выведет все посты, прикрепленные к этому тегу.

 

Файл views.py

Открываем файл blog/views.py и пишем обработчики для шаблонов ссылок маршрутизации, которые выведут в шаблоны вида список тегов и список связанных постов

from django.shortcuts import render
from django.http import HttpResponse
from .models import Post, Tag
# 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})

def tags_list(request):
    tags = Tag.objects.all()
    return render(request, 'blog/tags_list.html', context={'tags': tags})

def tag_detail(request, slug):
    tag = Tag.objects.get(slug__iexact = slug)
    return render(request, 'blog/tag_detail.html', context={'tag': tag})
    

Как мы видим, нам потребуется создать еще 2 шаблона вида

  • tags_list.html — шаблон страницы списка всех тегов;
  • tag_detail.html — шаблон страницы всех постов с тем или иным конкретным тегом.

 

Принцип DRY в шаблонах Django

Как было в предыдущей части упомянуто, нам нужно создать еще 2 шаблона вида: tags_list.html и tag_detail.html.

Тут важно учитывать, что вывод постов по ссылке mysite.com/blog и по ссылке mysite.com/blog/tag/tagname идентичны и нам нужно для этого создать карточку для поста, чтобы не копипастить один и тот же код и для этого в шаблонах Django есть возможность подключать куски кода html друг в друга и это соответствует принципу DRY.

Don’t repeat yourselfDRY (рус. не повторяйся) — это принцип разработки программного обеспечения, нацеленный на снижение повторения информации различного рода, особенно в системах со множеством слоёв абстрагирования. Принцип DRY формулируется как: «Каждая часть знания должна иметь единственное, непротиворечивое и авторитетное представление в рамках системы».

Т.е., помимо шаблонов tags_list.html и tag_detail.html мы еще создадим папку blog/templates/blog/includes, в которой будет карточка поста post_card.html, который будет подключен к файлу blog/templates/blog/index.html и к файлу blog/templates/blog/tag_detail.html

Файл 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 %}
				{% include 'blog/includes/post_card.html'%}
			{% endfor %}
		</div>
	</div>
{% endblock %}

Файл blog/templates/blog/tags_list.html

{% extends 'blog/base_blog.html'%}
{% block title %}
	Site Blog - Tags list
{% endblock %}
{% block content %}
	<div>
		<div class="grid-x">
		    {% for tag in tags %}
			    <div class="cell small-12 medium-3 large-3">
				      <div class="card">
					        <div class="card-section">
					        	<h4>
						        	<a href="{{ tag.get_absolute_url }}" class="button">
						        		{{ tag.title }}
						        	</a>
					        	</h4>
					        </div>
				      </div>
			    </div>
			{% endfor %}
		</div>
	</div>
{% endblock %}

Файл blog/templates/blog/tag_detail.html

{% extends 'blog/base_blog.html'%}
{% block title %}
	Site Blog - Tag detail
{% endblock %}
{% block content %}
	<div>
		<h3>Posts with tag <b>{{tag.title|title}}</b>:</h3>
		<br/>
		<div class="grid-x">
		    {% for post in tag.posts.all %}
				{% include 'blog/includes/post_card.html'%}
			{% endfor %}
		</div>
	</div>
{% endblock %}

Файл blog/templates/blog/includes/post_card.html

 <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>
           Tags: 
	    {% for tag in post.tags.all %}
        	<a href="{{ tag.get_absolute_url }}">
        		{{ tag.title }}
        	</a>
        {% empty %}
            none
		{% endfor %}
       </div>
    </div>
 </div>

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

В итоге карточки постов должны получиться, как на скриншотах внизу

Карточки постов с тегом и без тега
Карточки постов с тегом и без тега

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

Страница списка тегов. Пока один тег
Страница списка тегов (пока в базе один тег)

Список постов по определенному тегу

Список постов по определенному тегу
Список постов по определенному тегу

 

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

👁 139 просмотров
1 Звезда2 Звезды3 Звезды4 Звезды5 Звезд (Пока оценок нет)
Загрузка...

Теория

В данном посте будем работать с базой данных в 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 %}

Шаблоны и наследование шаблонов в Django

👁 79 просмотров
1 Звезда2 Звезды3 Звезды4 Звезды5 Звезд (Пока оценок нет)
Загрузка...

Теория

Templates — шаблоны, один из основных составляющих в идеологии MTV, на котором построена обработка и выдача данных в Django.

Шаблоны(Tеmplates) от видов в Django отличаются тем, что первые представляют из себя куски страниц HTML, которые соединяют методом наследования в одно целое, а процесс их выдачи и обработки происходит в Видах(Views).

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

Практика

Шаблоны хранятся в папке templates внутри каждого проекта, это стандарт Django и Django будет всегда, первым делом, искать шаблоны в этой папке. Есть папка templates в пределах приложения и есть корневая папка templates, которая содержит одинаковый для всех приложений код HTML и выглядит эта иерархия, как внизу

[myproject]/
├── [myproject]/
│   ├── __init__.py
│   ├── settings.py
│   ├── urls.py
│   └── wsgi.py
├── [blog]/
│   ├── __init__.py
│   ├── admin.py
│   ├── apps.py
│   ├── moddels.py 
│   ├── views.py 
│   ├── urls.py 
│   ├── templates/
│   │   └──blog/ 
│   │      ├──base_blog.html 
│   │      └──index.html 
│   └── tests.py 
├── templates/
│   └──base.html 
└── manage.py

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

Содержимое папки templates/:

  • blog/ — одноименная папка для интимных шаблонов приложения;
  • index.html — индексная страница вывода обработки данных приложения;
  • base_blog.html — базовый шаблон в пределах приложения, который наследует base.html и от которого наследуются остальные страницы в пределах приложения.

Содержимое папки blog/templates/:

  • base.html — базовый шаблон для всех страницы в пределах проекта.

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

Теперь, давайте, все по порядку будем редактировать и разъяснять куски кода.

Открываем файл blog/views.py и через функцию рендера подключаем шаблон index.html

from django.shortcuts import render
#from django.http import HttpResponse
# Create your views here.
def posts_list(request):
    posts = ['post 1', 'post 2', 'post 3', 'post 4', 'post 5',]
    return render(request, 'blog/index.html', context = {'posts' : posts})
    #return HttpResponse("<h1>Hello! Hello!</h1>")

Функция render(…) выводит шаблон вторым параметром, первым параметром она принимает данные запроса, а третьим — контекст с данными из вида в шаблон. Третий параметр этой функции является связующим звеном между Видом(View) и Шаблоном(Template), точнее сказать, отправляет в шаблон данные на вывод и в данном случае, для примера, передадим из вида в шаблон список постов в виде массива.

Открываем файл корневого шаблона templates/base.html и ставим такой код

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
    <title>
		{% block title %}
			Page title
		{% endblock %}
    </title>
	<script src="https://code.jquery.com/jquery-3.3.1.js" integrity="sha256-2Kok7MbOyxpgUVvAk/HJ2jigOSYS2auK4Pfzbm7uH60=" crossorigin="anonymous"></script>
	<!-- Compressed CSS -->
	<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/foundation-sites@6.5.0/dist/css/foundation.min.css" integrity="sha256-VEEaOnBKVRoYPn4AID/tY/XKVxKEqXstoo/xZ6nemak= sha384-D46t32f421/hB30qwnim2pIcisNN5GU9+6m2Mfnd3dKpTSFidZLa08/1StEiCFId sha512-WkgzH8VKemDfwrp18r+wgbx+oHXOkfd2kJs7ocAXdGDgonXDXh88E90IRtRZRXtO0IHprxYHYlY14h+wyTsUDA==" crossorigin="anonymous">
	<!-- Compressed JavaScript -->
	<script src="https://cdn.jsdelivr.net/npm/foundation-sites@6.5.0/dist/js/foundation.min.js" integrity="sha256-GZq6aeugpWo25iH//1eKmeK6FHCf+6KXTfoUpkCqPCA= sha384-vjxUQtbGw5FJMigaaFpXYyxoHHLb7LbvRywnMZOiPJeh5j9sl2rnmQ3iucuegRm8 sha512-h7tIMIX/opZXfWkcTDbkO+nT0LePyAAwDacfYhWtgGUidV+Kkh3eesW52fPSxKEsw3rgywKhQvghNLT4eDuUyw==" crossorigin="anonymous"></script>
</head>
<body>
	<nav class="main-navigation">
		<div class="title-bar" data-responsive-toggle="example-menu" data-hide-for="medium">
		  <button class="menu-icon" type="button" data-toggle="example-menu"></button>
		  <div class="title-bar-title">Menu</div>
		</div>
		
		<div class="top-bar" id="example-menu">
		  <div class="top-bar-left">
		    <ul class="dropdown menu" data-dropdown-menu>
		      <li class="menu-text">Site Title</li>
		      <li>
		        <a href="#">One</a>
		        <ul class="menu vertical">
		          <li><a href="#">One</a></li>
		          <li><a href="#">Two</a></li>
		          <li><a href="#">Three</a></li>
		        </ul>
		      </li>
		      <li><a href="#">Two</a></li>
		      <li><a href="#">Three</a></li>
		    </ul>
		  </div>
		  <div class="top-bar-right">
		    <ul class="menu">
		      <li><input type="search" placeholder="Search"></li>
		      <li><button type="button" class="button">Search</button></li>
		    </ul>
		  </div>
		</div>
	</nav>
	{% block content %}
		Page content
	{% endblock %}
    <script>
      $(document).foundation();
    </script>
</body>
</html>

В данном файле есть несколько моментов, которые следует упомянуть:

  • блоки для шаблонов кода, которые начинаются с ключевого слова block и заканчиваются ключевым словом endblock. Данные блоки играют роль вешалки на странице, в последующем, в частных страницах приложений эти места можно заменять индивидуальным содержимым, а вот ключевое слово extends указывает файл шаблона, от которого будет происходить наследование;
  • мы подключили фронтенд фреймворк Zurb Foundation, который сделает элементы на странице чуть более привлекательным, а сетку более понятной и структурированной. Последние ссылки на CDN можно найти тут. К теме разработки на Django это не относится, мы могли бы вместо него включить и Bootstrap, но я предпочел первое.

Открываем частный шаблон blog/templates/blog_base.html и вставляем такой код и сохраняем

{% extends 'base.html' %}

Открываем частный шаблон blog/templates/blog/index.html и вставляем такой код и сохраняем

{% extends 'blog/base_blog.html'%}
{% block title %}
	Blog page
{% 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 }}</h4>
					          	<p>This row of cards is embedded in.</p>
					        </div>
				      </div>
			    </div>
			{% endfor %}
		</div>
	</div>
{% endblock %}

Как вы помните,файл blog/templates/blog/index.html мы подключали к нашему виду blog/views.py из которого передавали через контекст массив постов posts. Данные массив выводится при помощи цикла в этом шаблоне. Особ следует отметить, что мы наследуем данный шаблон от базового шаблона blog/templates/blog/base_blog.html, являющийся корневым в пределах текущего приложения и который, в свою очередь, наследуется от корневого templates/blog/base.html в пределах всего проекта.

Примечание. В шаблонах используются 2 вида скобок шаблонизации:

  • {% [выражение pyhton] %} — в таких скобках вычисляются выражения;
  • {{ [переменная на вывод] }} — в таких скобках значение переменной выводят на показ в шаблон.

После всего выше перечисленого остался один момент, связанный с нахождением корневого шаблона проекта, его надо явно указать в файле settings.py, иначе файл blog/templates/blog/blog_base.html не сможет найти и унаследовать его. Для этого нужно открыть файл settings.py и добавить абсолютный путь к папке корневого шаблона проекта

...
TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [os.path.join(BASE_DIR, 'templates')],
        ...
...

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

Результат работы из ткущей статьи
Результат работы из ткущей статьи