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

👁 10 просмотров
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 ObjecDetailMixin(View):
    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 ObjecDetailMixin

# 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(ObjecDetailMixin, 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(ObjecDetailMixin, 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

👁 13 просмотров
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

👁 18 просмотров
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

👁 14 просмотров
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')],
        ...
...

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

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

Маршрутизация и генерация ответа пользователю в Django

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

Теория

В данном посте рассмотрим, как Django работает с URL — адресами и как подключаются приложения к маршрутизации для обработки входящих запросов их выдача.

В проекте Django нужно акцентировать внимание на нескольких файлах:

  • urls.py — файл маршрутизации;
  • views.py — файл контроллера и выдачи рендера страница;
  • models.py — файл работы с базой данных на основе ORM.

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

Концептуальный принцип MVC реализован из таких частей:

  • M — доступ к данным, обрабатывается слоем работы с базой данных.
  • V — эта часть, которая определяет какие данные получать и как их отображать, обрабатывается представлениями и шаблонами.
  • C — эта часть, которая выбирает представление в зависимости от пользовательского ввода, обрабатывается самой средой разработки, следуя созданной вами схемой URL, и вызывает соответствующую функцию Python для указанного URL.

Но в Django это работает чуть иначе. Так как «C» обрабатывается средой разработки и всё интересное в Django происходит в моделях, шаблонах и представлениях, на Django ссылаются как на MTV-ориентированную среду разработки. В MTV-подходе к разработке:

  • M — определено для «Модели» (Model), слоя доступа к данным. Этот слой знает всё о данных: как получить к ним доступ, как проверить их, как с ними работать и как данные связаны между собой.
  • T — определено для «Шаблона» (Template), слоя представления данных. Этот слой принимает решения относительно представления данных: как и что должно отображаться на странице или в другом типе документа.
  • V — определено для «Представления» (View), слоя бизнес-логики. Этот слой содержит логику, как получать доступ к моделям и применять соответствующий шаблон. Вы можете рассматривать его как мост между моделями и шаблонами.

В общем, ниже представлена схема того, как Django представляет для себя MVC в виде MTV

Шаблон VMC в понимании Django в виде MTV
Шаблон VMC в понимании Django в виде MTV

 

Практика

В прошлом посте мы обсудили тему создания проекта Django и приложения к проекту. Теперь давайте сделаем следующее:

  • интегрируем приложение с проектом;
  • сделаем маршрутизация на созданное приложение;
  • добавим вид.

На данный момент у нас есть такая структура

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

По умолчанию в проекте создается главное приложение с тем же именем, что и у проекта. Данное приложение  содержит файл конфигурации settings.py и файл маршрутизации urls.py, которые нам будут необходимы в данном разборе.

Открываем файл settings.py и добавляем приложение blog к проекту, дописав значение для INSTALLED_APPS

...
INSTALLED_APPS = [
    ...,
    'blog',
]
...

Далее открываем файл маршрутизации urls.py и добавляем шаблон URL для нашего приложения, добавиви параметр для переменной urlpatterns

...
from django.path import include

...
urlpatterns = [
    path('admin/', admin.site.urls),
    path('blog/', include('blog.urls')),
]
...

Строка path(‘blog/’, include(‘blog.urls’)) означает, что мы задаем для нашего приложения новый маршрут, по которому оно будет доступно и записываем его в виде шаблона URL. Это означает, что если после домена мы введем www.mysite.com/blog/, то запустится наше приложение и путь запуска указано вторым параметром в функции path(url_pattern, function_or_app_path). Путь к приложению указывается через функцию include() из стандартного модуля django.path, который тоже нужно включить.

Значение ‘blog.urls’ говорит, что перенаправлять нужно на маршрутизатор приложения, т.е. замысел в том, чтобы из маршрутизатора главного приложения мы перешли в маршрутизатор того приложения, которое соответствует шаблону адреса URL, которую мы набираем в строке браузера после домена.

Примечание. В функции path(…) кода выше представлены только 2 обязательные значения, но у нее есть еще 2 необязательные(в общем 4), которыми мы воспользуемся чуть позже.

Теперь идем в папку нашего приложения blog и создаем файл urls.py(по умолчанию Django его не добавляет). Пишем в нем такой код и сохраняем

from django.path import include
from .views import posts_list

urlpatterns = [
    path('', posts_list),
]

Теперь открываем файл views.py нашего проекта и добавляем туда новую функцию posts_list

from django.shortcuts import render
from django.http import HttpResponse
# Create your views here.
def posts_list(request):
    
    return HttpResponse("<h1>Hello! Hello!</h1>")

Что мы сделали выше? Во первых, если в предыдущем myproject/urls.py в функции path(…) вторым параметром мы указывали на приложение blog, то в blog/urls.py мы вторым параметром этой функции указываем функцию, которую грузим из вида этого приложения blog/views.py и если теперь перезапустить сервер и посмотреть по адресу 127.0.0.1:5000/blog, то мы увидим

Результат вывода приложения blog
Результат вывода приложения blog

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

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

Создаем новую виртуальную среду

virtualenv venv

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

cd venv
source bin/activate

Устанавливаем Django

pip3 install django

Создаем новый проект Django

django-admin startproject myblog

Выполняем миграцию баз данных в SQLite(он по умолчанию в Django)

cd myblog
python3 manage.py migrate

Запуск созданного проекта

python3 manage.py runserver 5000

Переходим по адресу 127.0.0.1:5000 и удостоверяемся, что проект запускается удачно

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

python3 manage.py startapp blog

Данное приложение пока нигде не задействовано. Работа с ним будет произведена в следующих шагах.

Резюме

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

Примечание 1. По умолчанию проект Django содержит одно единственное приложение с тем же названием, что и название проекта.

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

Примечание 3. Связь между приложениями и обработка данных выполняются по принципу шаблона проектирования MVC/MTV.

Примечание 4. По умолчанию Django мигрирует данные в БД SQLite, поэтому, если есть необходимость в других видах БД, к примеру в PostgreSQL, то можно посмотреть этот пост.

Примечание 5. По умолчанию, команда

python3 manage.py runserver 5000

запустит сервер WSGI на хосте 12.0.0.1 и на порту 5000. Если Django запускается на VPS/VDS, то чтобы был доступ через внешний IP нужно явно указать IP, как 0:5000 или 0.0.0.0:5000. Установку домена и прочие настройки можно узнать из этого поста.

 

 

Создание простого приложения на Django 2

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

Цикл минималистичных статей для запоминания и быстрой разработки простого блога на Django 2.x / Python 3.x.

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

  1. Установка Django и создание первого приложения
  2. Маршрутизация и генерация ответа пользователю
  3. Шаблоны и наследование шаблонов в Django
  4. Создание модели ORM, вывод из базы данных в шаблоны Django
  5. Облако тегов через связь ManyToMany и принцип DRY в Django
  6. Виды в виде классов(Class Based Views) и использование миксинов

Введение в Django Channels

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

Добро пожаловать в Django Channels! Каналы меняют Django, чтобы сплести под ним асинхронный код через синхронное ядро, позволяя проектам на Django обрабатывать не только HTTP, но и протоколы, которые требуют подключения соединения на длительное время — WebSockets, MQTT, чатботы, пользовательское радио и многое другое.

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

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

Черепахи всегда идут вниз

Channels оперируют по принципу «первый вошел — первым вышел». У нас есть единая идея или картина того, что есть приложения в виде Channels и даже самый простой Consumer(эквивалент Views  в Django) является полными работоспособными приложениями ASGI, которые вы можете запускать самостоятельно.

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

Мы рассматриваем HTTP и существующие Django Views, как части большого целого. Традиционно Django Views все также существуют с Channels и все также используемы — мы оборачиваем их в ASGI приложение, называемое channels.http.AsgiHandler . Теперь вы можете написать свой long-polling HTTP — обработчик или приемники WebSocket и бок о бок с существующим кодом Django. URL — маршрутизация и промежуточные модули — это все простые приложения ASGI.

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

Области и события

Channels и ASGI разделяют входящее соединение на 2 компонента: scope(областью) и серия events(событий).

Scope — это набор подробностей о одном входящем соединении — например, путь, по которому веб-запрос был сделан или исходящий IP-адрес WebSocket, или пользовательский обмен сообщениями с чатом — и это сохраняется в течение всего соединения.

Для HTTP протокола scope — это просто один произошедший запрос. Для WebSocket он является жизненным циклом сокета (но изменяется, если сокет закрывается и повторно соединяется).

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

В течение всего срока действия этой Scope(области) происходит ряд Events(событий). Они представляют пользовательское взаимодействие — создание HTTP — запроса, WebSocket — фрейм и т.д.

Наши каналы или приложения ASGI будут создаваться один раз для каждой Scope(области), а затем будут передаваться поток Events(событий), происходящих в этой области, чтобы решить, что делать.

Пример с HTTP:

  • пользователь создает запрос HTTP;
  • мы открываем новую Scope(область) типа http с деталями пути запроса, метода, заголовков, и т.д.
  • мы отправляем событие  http.request с контентом тела HTTP;
  • приложения Channels или ASG обрабатывают это и генерируют событие http.response для обратного ответа браузеру и закрывает соединение;
  • HTTP — request(запрос)/response(ответ) заканчиваются и Scope(область) уничтожается.

Пример с чатом:

  • пользователь отправляет первое сообщение чату;
  • открывается Scope(область), которая содержит пользовательский ник, выбранного имени и пользовательский ID;
  • программа получает Events(событие) chat.received_message с текстом события. Оно не имеет ответ, должен быть один, два или больше в качестве Events(события) chat.send_message если есть в этом потребность;
  • чем больше пользователь отправляет в чат сообщений тем больше генерируются Events(события) <span chat.received_message
  • после таймаута или когда процесс программы возобновляется Scope(область) закрывается.

В течение срока действия Scope(области) — будь то чат, HTTP-запрос, сокет-соединение или что-то еще — у вас будет один экземпляр приложения, обрабатывающий все события из него, и вы также сможете перенести вещи на экземпляр приложения. Вы можете выбрать, если хотите, сырое приложение ASGI, но Channels(каналы) дают вам удобную абстракцию над ними, называемую Consumers(потребителями).

Что такое Consumer?

Consumer — это потребитель событий и является базовой единицей в коде Channels. Мы называем его Consumer(потребитель), так как он потребляет события, но вы можете подумать об этом, как об ихнем маленьком приложении. Когда приходит запрос или новый сокет, Chanells(каналы) будут следовать их таблице маршрутизации — мы рассмотрим немного этот момент — находят там правильного/нужного Consumer(потребителя) для этих входящих соединений и запустит на выполнение его копию.

Это значит, что в отличии от Views(видов) Django, Consumers(потребители) рассчитаны на запуск в долгое время работы. Они также могут быть запущены на короткое время — ведь, HTTP запросы могут быть обработаны и Consumers(потребителями) — но они построены вокруг идеи жизни в короткий промежуток( они живут в течении Scope(области), которых мы описали выше).

Ниже представлен базовый Consumer(потребитель):

class ChatConsumer(WebsocketConsumer):

    def connect(self):
        self.username = "Anonymous"
        self.accept()
        self.send(text_data="[Welcome %s!]" % self.username)

    def receive(self, *, text_data):
        if text_data.startswith("/name"):
            self.username = text_data[5:].strip()
            self.send(text_data="[set your username to %s]" % self.username)
        else:
            self.send(text_data=self.username + ": " + text_data)

    def disconnect(self, message):
        pass<span id="mce_marker" data-mce-type="bookmark" data-mce-fragment="1">​</span>

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

Внизу, Channels(каналы) запущены в полном асинхронном событийном цикле и если вы пишете код, похожий выше, то он будет вызван в синхронном потоке. Это означает, что вы безопасно выполнять блокирующие операции, такие как вызов Django ORM:

class LogConsumer(WebsocketConsumer):

    def connect(self, message):
        Log.objects.create(
            type="connected",
            client=self.scope["client"],
        )<span id="mce_marker" data-mce-type="bookmark" data-mce-fragment="1">​</span>

Однако, если вы хотите больше контроля, и вы готовы работать только в асинхронных функциях, вы можете написать полностью асинхронных Consumers(потребителей):

class PingConsumer(AsyncConsumer):
    async def websocket_connect(self, message):
        await self.send({
            "type": "websocket.accept",
        })

    async def websocket_receive(self, message):
        await asyncio.sleep(1)
        await self.send({
            "type": "websocket.send",
            "text": "pong",
        })<span id="mce_marker" data-mce-type="bookmark" data-mce-fragment="1">​</span>

Вы можете прочесть больше про Customers(потребителей) по ссылке на официальный сайт.

Маршрутизация и мультипротокольность

Вы можете комбинировать несколько Consumers(потребителей), которые являются, если вспомнить, их собственнымиASGI приложениями, в одно большое приложение, которое будет представляет ваш проект с маршрутизацией:

application = URLRouter([
    url(r"^chat/admin/$", AdminChatConsumer),
    url(r"^chat/$", PublicChatConsumer),
])

Channels(каналы) не только построены вокруг мира HTTP и WebSockets, но также позволяют нам построить любой протокол внутри Django среды, создавая сервер, который отображает эти протоколы в один и тот же набор событий. К примеру, вы можете построить бот чата с стиле:

class ChattyBotConsumer(SyncConsumer):
    def telegram_message(self, message):
        """
        Simple echo handler for telegram messages in any chat.
        """
        self.send({
            "type": "telegram.message",
            "text": "You said: %s" % message["text"],
        })

И затем используем другой маршрутизатор, чтобы один проект мог обслуживать как WebSockets, так и чат-запросы:

application = ProtocolTypeRouter({

    "websocket": URLRouter([
        url(r"^chat/admin/$", AdminChatConsumer),
        url(r"^chat/$", PublicChatConsumer),
    ]),

    "telegram": ChattyBotConsumer,
})

Цель Channels(каналов)- это позволить вам построить ваши собственные Django проекты для работы через любой протокол или транспорт, с которыми вы можете столкнуться в современном WEB, позволяя вам работать со знакомыми компонентами и стилем кодирования, к которому вы привыкли.

Больше информации про протоколы и маршрутизацию можно узнать по ссылке.

Кросспроцессная коммуникация

Как стандартный WSGI-сервер, ваш код приложения, обрабатывающий события протокола, выполняется внутри самого процесса сервера — например, код обработки WebSocket выполняется внутри вашего сервера WebSocket.

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

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

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

Канальный слой — это дополнение к Channels(каналам) и может быть запрещен, если он не нужен. для этого нужно установить для CHANNEL_LAYERS пустое значение.

Вы также можете отправлять сообщения в выделенный процесс, который прослушивает самостоятельно, фиксированное имя канала:

# In a consumer
self.channel_layer.send(
    "myproject.thumbnail_notifications",
    {
        "type": "thumbnail.generate",
        "id": 90902949,
    },
)

Вы можете почитать еще больше про канальный слой на офф-сайте Channel Layers.

Интеграция с Django

Channels(каналы) поставляются с простой поддержкой обычных функций Django, таких как сеансы и аутентификация. Вы можете объединить аутентификацию с Views(представлениями) WebSocket, просто добавив к ним правильное middleware:

application = ProtocolTypeRouter({
    "websocket": AuthMiddlewareStack(
        URLRouter([
            url(r"^front(end)/$", consumers.AsyncChatConsumer),
        ])
    ),
})

Для дополнительной информации можно почитать Sessions и Authentication.

Установка и настройка Cygwin с Netbeans

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

NetBeans позволяет работать с проектами на C++. Это отличное кроссплатформенное решение или хотя бы пойдет для написания мелкой игрушки.

Исходные данные

  • Установленный NetBeans 8.2;
  • Windows 10
  • Стандартный плагин для NetBeans C/C++;
  • Cygwin — UNIX подобное окружение для Windows с установленным в нем GCC, G++, Make, GDB.

Плагин NetBeans C/C++

Плагин ставится очень легче в меню «Tools > Plugins» / «Сервис > Подключаемые модули»

На скриншоте он у меня уже установлен

 

Скачивание и установка Cygwin и его компонентов

Для скачивания идем на официальный сайт в раздел и выбираем либо setup-x86_64.exe либо setup-x86.exe.

Это будет не установщик, а загрузчик компонентов

Основной смысл загрузчика - указать нужные компоненты и загрузить, указав папку Cygwin

На 6-м шаге нужно искать компоненты, которые соответствуют GCC, G++, Make, GDB. После установки нужно добавить пусть C:/Cygwin/bin в переменную PATH Windows, после чего открываем командную строку Windows и проверяем на то, что все компоненты глобально вызываются через консоль:

cygcheck -c cygwin
gcc --version
g++ --version
make --version
gdb --version<br>

 

Конфигурация Cygwin с NetBeans

Для этого открываем диалог параметров «Сервис > Параметры» , кликаем на вкладку «С/C++»

Окно параметров C/C++ в NetBeans
Окно параметров C/C++ в NetBeans

Создаем новый набор средств и задаем базовую папку, как C:/Cygwin/bin и все нужные инструменты автоматически установятся в полях с права, а если не установились, то задаем вручную или проверяем на то, что мы установили правильные компоненты.

Создание проекта С/C++ в NetBeans

В общем, ничего нового, стандартно создаем проект и указываем, что нужно создать:

  • приложение;
  • статическую библиотеку;
  • динамическую библиотеку;
  • и т.п.
Создание нового проекта C/C++ в NetBeans
Создание нового проекта C/C++ в NetBeans

Мы создадим приложение, чтобы потом указать на еще один ньюанс с настройкой построения и запуска проекта. Самая распространенная ошибка после интеграции — неправильно указанная консоль запуска. По умолчанию этот параметр установлен так, что используется стандартная консоль Cygwin,  а не Windows, из-за чего проект иногда может не запускаться из среды NetBeans, ссылаясь на то, что не может найти путь с параметром пути /cygdrive/путь_до_папки_проекта. Решается это легко заменой стандартной консоли Cygwin на консоль Windows или на консоль вывода NetBeans

Окно свойств проекта. Указание терминала запуска
Окно свойств проекта. Указание терминала запуска