Everything you wanted to know
about the Django framework

Angular и Django: создание приложения микро-блога

Интересует тема, как вызывать функции API Angular 6 и HttpClient? В этом учебном пособии будут показаны некоторые методы построения приложения для микро-блогов, использующего Angular 6 и Django Rest Framework (DRF). В процессе мы узнаем следующее:

  • Как сделать бэкэнд приложение с помощью Django и API Django Rest Framework
  • Создание простого одностраничного приложения Angular 6, которое может запрашивать API
  • Аутентификация пользователей с помощью JSON Web Tokens (JWT)

Готовы? Давайте начнем!

Обзор

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

База данных SQLite → Модель Django → Сериализатор Django → Django ViewSet → Сервис Angular → Компонент Angular

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

Приложение Django

Модели Django

Наше приложение микро-блога содержит две модели данных: User и BlogPost. Модель User предоставлена самим фреймворком Django, поэтому нам нужно создать только класс модели для BlogPost. Это довольно просто и определяет несколько полей, а также "волшебный" метод Python «to-string», называемый __str __().

microblog/models.py:

from django.db import models
from django.contrib.auth.models import User
from django.utils import timezone
 
class BlogPost(models.Model):
    user = models.ForeignKey(User, on_delete=models.CASCADE)
    date = models.DateTimeField(default=timezone.now)
    body = models.CharField(default='', max_length=200)
 
    def __str__(self):
        return self.body

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

Сериализатор

Django Rest Framework работает в концепции модели, сериализаторы и ViewSets. Сериализатор описывает, как преобразовать объекты модели в JSON и обратно. Django ORM использует классы моделей, а конечные точки API используют JSON. Сериализатор — это то, что преобразовывает одно в другое.

microblog/serializers.py:

from django.contrib.auth.models import User
from rest_framework import serializers
from .models import BlogPost
 
class UserSerializer(serializers.ModelSerializer):
 
    class Meta:
        model = User
        fields = ('id', 'username', 'first_name', 'last_name')
 
class BlogPostSerializer(serializers.ModelSerializer):
    user = serializers.StringRelatedField(many=False)
 
    class Meta:
        model = BlogPost
        fields = ('id', 'user', 'date', 'body')

В отличие от наших вышеприведенных классов моделей, нам необходимо предоставить сериализатор для модели User. Django Rest Framework не делает это за нас, даже если сама модель User находится непосредственно в Django.

Кроме того, мы создаем класс BlogPostSerializer, который определяет, какие поля в модели BlogPost должны быть преобразованы в JSON. Обратите внимание, что мы также определяем связь с классом User. Это означает, что при сериализации объекта BlogPost полученный JSON будет содержать вложенный объект, который является сериализованной моделью пользователя. Представление JSON BlogPost может выглядеть так:

{
    "id": 1
    "user": {
        "id": 10,
        "username": "alice123",
        "first_name": "Alice",
        "last_name": "Smith"
    },
    "date": 1528071221,
    "body": "The quick brown fox jumped over the lazy dog"
}

ViewSet

Следующее, что нам нужно для Django Rest Framework - это ViewSet. Это коллекция Django Views (иными словами, контроллеров), которые содержатся в классе.

microblog/views.py:

from django.shortcuts import render
from django.contrib.auth.models import User
from rest_framework import viewsets, permissions
from .models import BlogPost
from . import serializers
from .permissions import ReadOnly
 
def index(request, path=''):
    return render(request, 'index.html')
 
class UserViewSet(viewsets.ModelViewSet):
    """
    Provides basic CRUD functions for the User model
    """
    queryset = User.objects.all()
    serializer_class = serializers.UserSerializer
    permission_classes = (ReadOnly, )
 
class BlogPostViewSet(viewsets.ModelViewSet):
    """
    Provides basic CRUD functions for the Blog Post model
    """
    queryset = BlogPost.objects.all()
    serializer_class = serializers.BlogPostSerializer
    permission_classes = (permissions.IsAuthenticatedOrReadOnly, )
 
    def perform_create(self, serializer):
        serializer.save(user=self.request.user)

Также как и в сериализаторе, мы создаем ViewSet дял обеих моделей: User и BlogPost.

В этом примере приложение не позволяет редактировать пользовательские модели с помощью API, поэтому UserViewSet настроен с permission_classes = (ReadOnly, ), не позволяя API писать в БД. Как же происходит редактирование профилей пользователей, спросите вы? Это выходит за рамки этого примера, но встроенное приложение-админка Django предоставляет способ сделать это, если нужно. При создании своего собственного приложения вы можете создать компонент Angular, чтобы пользователи могли редактировать свои профили и изменять разрешение ReadOnly на другое разрешение, позволяя пользователям редактировать только свои собственные профили.

Класс BlogPostViewSet также содержит класс разрешений IsAuthenticatedOrReadOnly. Это позволяет посетителям просматривать любые записи в нашем приложении, но только пользователи, которые вошли в систему, могут создавать записи.

Конфигурация Django URL

Нам также необходимо добавить адреса ViewSet'ов в URL нашего приложения.

Во-первых, мы добавляем URL-адреса ViewSet в наше приложение для микроблогов:

microblog/urls.py

from django.urls import path, include
from rest_framework import routers
 
from . import views
 
router = routers.DefaultRouter(trailing_slash=False)
router.register(r'posts', views.BlogPostViewSet)
router.register(r'users', views.UserViewSet)
 
urlpatterns = [
    path(r'api/', include(router.urls)),
    path(r'', views.index, name='index')
]

Во-вторых, добавим URL приложения в URL проекта:

angular_django_example/urls.py

...
urlpatterns = [
    path('admin/', admin.site.urls),
    path(r'', include('microblog.urls')),   # add this
    path(r'api-token-auth/', obtain_jwt_token),
    path(r'api-token-refresh/', refresh_jwt_token),
]

Проверим наше API

Django Rest Framework поставляется с встроенным средством просмотра Swagger API. Чтобы узнать, что доступно в API, все, что вам нужно сделать, — запустить python manage.py runserver и посетить localhost:8000/api, чтобы увидеть конечные точки API в действии.

Вызов API из Angular

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

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

Компонент и шаблон

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

microblog/front-end/app.component.ts

import {Component, OnInit} from '@angular/core';
import {BlogPostService} from './blog_post.service';   # add this
import {UserService} from './user.service';
import {throwError} from 'rxjs';
 
@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
 
  /**
   * An array of all the BlogPost objects from the API
   */
  public posts;
 
  /**
   * An object representing the data in the "add" form
   */
  public new_post: any;
 
  constructor(private _blogPostService: BlogPostService, private _userService: UserService) { }
 
  ngOnInit() {
    this.getPosts();
    this.new_post = {};
    this.user = {
      username: '',
      password: ''
    };
  }
 
  ... user login/logout methods are unchanged from part 1. omitted for brevity ...
 
  getPosts() {
    this._blogPostService.list().subscribe(
      // the first argument is a function which runs on success
      data => {
        this.posts = data;
        // convert the dates to a nice format
        for (let post of this.posts) {
          post.date = new Date(post.date);
        }
      },
      // the second argument is a function which runs on error
      err => console.error(err),
      // the third argument is a function which runs on completion
      () => console.log('done loading posts')
    );
  }
 
  createPost() {
    this._blogPostService.create(this.new_post, this.user.token).subscribe(
       data => {
         // refresh the list
         this.getPosts();
         return true;
       },
       error => {
         console.error('Error saving!');
         return throwError(error);
       }
    );
  }
 
}

Наш компонент содержит два новых свойства: 'posts' представляет собой массив записей блога, который мы получили от API. Свойство 'new_post' содержит простой объект JavaScript, который используеся для создания формы новой записи блога.

Кроме того, есть еще два метода: getBlogPosts() будет запрашивать API через BlogPostService и заполнять объект 'posts', а createPost() берет данные из 'new_post' и отправляет их в функцию API.

В нашем файле шаблона Angular мы добавляем список pfgbctq в блоге и форму для создания нового сообщения.

microblog/front-end/app.component.html

...
<h2 class="mt-3">Micro Blog Posts</h2>
<div *ngFor="let post of posts">
  <div class="row mb-3">
    <label class="col-md-2">By:</label>
    <div class="col-md-2 mb-1">{{ post.user }}</div>
    <label class="col-md-2">Date:</label>
    <div class="col-md-6">{{ post.date }}</div>
    <div class="col-md-12">{{ post.body }}</div>
  </div>
</div>
 
<h3>Create a new post:</h3>
 
<div class="row mb-1">
  <label class="col-md-3">Enter your post:</label>
  <div class="col-md-9 mb-1"><input type="text" name="body" [(ngModel)]="new_post.body"></div>
  <div class="col-md-2 offset-3">
    <button (click)="createPost()" class="btn btn-primary">Save</button>
  </div>
</div>

Сервис Angular для одного сообщения блога

В 1-ой части мы создали класс UserService для Angular для обработки вызовов API, связанных с пользовательсокй авторизацией и работы с токеном. Теперь мы создадим вторую службу: BlogPostService, которая обрабатывает соединения с конечными точками API BlogPost, которые мы создали выше.

Вот заключительная часть нашей головоломки, сервис для сообщения блога:

microblog/front-end/blog_post.service.ts

import {Injectable} from '@angular/core';
import {HttpClient, HttpHeaders} from '@angular/common/http';
import {UserService} from './user.service';
 
@Injectable()
export class BlogPostService {
 
  constructor(private http: HttpClient, private _userService: UserService) { }
 
  // Uses http.get() to load data from a single API endpoint
  list() {
    return this.http.get('/api/posts');
  }
 
  // send a POST request to the API to create a new data object
  create(post, token) {
    httpOptions = {
      headers: new HttpHeaders({
        'Content-Type': 'application/json',
        'Authorization': 'JWT ' + this._userService.token   // this is our token from the UserService (see Part 1)
      })
    };
    return this.http.post('/api/posts', JSON.stringify(post), httpOptions);
  }
}

Обратите внимание на методы list() и create() в нашем сервисе. Они создают Observable, используя HttpClient Angular и возвращают его вызывающему. Вот так компонент получает свои данные.

Использование токена CSRF

Помните, что в вышеприведенном коде ViewSet, конечные точки Blog Post имели разрешения только для проверки подлинности или чтения? Когда мы отправляем данные запросом POST в API, нам также нужен способ сообщить, что пользователь аутентифицирован.

Итак, давайте рассмотрим первую часть, где вызывали конечную точку API: аутентификацию, чтобы получить JSON Web Token. Мы можем использовать этот токен, чтобы указать API, что пользователь вошел в систему, а также идентификатор пользователя. Все, что нам нужно сделать, это добавить заголовок «Авторизация» в заголовки HTTP, которые мы отправляем с запросом API. При использовании JWT значение должно быть «JWT», за которым следует пробел, а затем весь токен.

Вот полный пример заголовка авторизации:

Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImFkbWluIiwib3JpZ19pYXQiOjE1MjgwNjg4NDEsInVzZXJfaWQiOjEsImVtYWlsIjoidGVzdEBleGFtcGxlLmNvbSIsImV4cCI6MTUyODA3MjQ0MX0.oxqzt-d2l5Bl473KwObsUetlKS2uOMXn7vqcHcSX5Gg

Обратите внимание, что в этом примере нам нужно отправить заголовок при записи в API. Поскольку анонимные пользователи могут видеть сообщения в блоге, и у них не будет токена JWT, мы не отправляем заголовок авторизации с запросами GET, создаваемыми методом list(). Однако в другом приложении, где контент не отображается анонимным пользователям, нам также необходимо отправить заголовок авторизации в запрос GET.

Перевод https://www.metaltoad.com/blog/angular-api-calls-django-part-2-building-micro-blog-app

Поделитесь с другими:

Представления-классы
(Class-Based Views)

Детальное описание и структура классов Django.

Выпуск Django 3.0

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

Выпущены релизы безопасности Django: 2.2.8 и 2.1.15

В соответствии с политикой безопасности, команда Django выпускает Django 2.2.8 и Django 2.1.15. Этот выпуск решает проблему безопасности, подробно описанную ниже. Мы призываем всех пользователей Django обновиться как можно скорее.

Путь от request до response в Джанго

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

Выпущен релиз-кандидат Django 3.0

Кандидат 1 релиза Django 3.0 - это последняя возможность для вас испытать множество новых функций перед выпуском Django 3.0.

Django DetailView - основы использования

Django позволяет создавать приложения очень легко. Если приложение должно быть выпущено быстро и является относительно общим, то эта среда Python идеально подходит для этого. В течение нескольких лет я профессионально работал в этой среде и часто рылся внутри, поэтому знаю почти всё, и сегодня я представлю вам все, что нужно знать, чтобы эффективно использовать универсальный DetailView в Django.

Выпущены исправления Django: 2.2.7, 2.1.14 и 1.11.26

Сегодня команда разработчиков Django выпустила версии 2.2.7, 2.1.14 и 1.11.26 с исправлениями ошибок. Пакет и контрольные суммы доступны на странице загрузок, а также из индекса пакетов Python. Идентификатор ключа PGP, использованный в этом выпуске: Mariusz Felisiak: 2EF56372BA48CD1B.

Как заставить request.is_ajax() работать с JS fetch()

Объект запроса Django request имеет изящный небольшой метод is_ajax(). Он позволяет определить, поступил ли запрос от JS-фреймворка (он же ajax старой школы). Хотя он отлично работает с некоторыми библиотеками JS, включая почтенный jQuery, он не будет работать с современным встроенным в JS fetch().

Практика программирования на Python 3, лекция №5

Лекции о Python 3 от Тимофея Хирьянова при поддержке Московского физико-технического института. Лекция №5.

Практика программирования на Python 3, лекция №4

Лекции о Python 3 от Тимофея Хирьянова при поддержке Московского физико-технического института. Лекция №4.

Практика программирования на Python 3, лекция №3

Лекции о Python 3 от Тимофея Хирьянова при поддержке Московского физико-технического института. Лекция №3.