У цьому уроці ми створимо блог за допомогою Flask і AngularJS.
Це перша частина курсу. В ній ми зосередимося на створенні REST API, який буде використовуватися додатком AngularJS.
Цілі
Цілі застосунку прості:
- Реєстрація користувачів;
- Їх авторизація;
- Кожен авторизований користувач повинен мати можливість створити пост;
- Відображення постів на домашній сторінці;
- Функція "Вихід".
Вимоги
Це керівництво припускає, що ви добре знайомі з Python, налаштуванням віртуального середовища та роботою з терміналом.
Структура Каталогів
Структуру каталогів, що ми будемо використовувати повинна виглядати так:
Встановлення необхідних пакунків Python
Ми використовуватимемо наступні пакети:
- Flask-RESTful
- Flask-SQLAlchemy
- Flask-Bcrypt
- Flask-HTTPAuth
- Flask-WTF
- WTForms-Alchemy
- marshmallow
Щоб спростити задачу, створіть файл з ім'ям requirements.txt у blog/server/ каталог і додайте наступний текст:
Flask==0.10.1
Flask-Bcrypt==0.6.0
Flask-HTTPAuth==2.2.1
Flask-RESTful==0.2.12
Flask-SQLAlchemy==1.0
Flask-WTF==0.10.0
Jinja2==2.7.3
MarkupSafe==0.23
SQLAlchemy==0.9.7
SQLAlchemy-Utils==0.26.9
WTForms==2.0.1
WTForms-Alchemy==0.12.8
WTForms-Components==0.9.5
Werkzeug==0.9.6
aniso8601==0.83
decorator==3.4.0
infinity==1.3
intervals==0.3.1
itsdangerous==0.24
marshmallow==0.7.0
py-bcrypt==0.4
pytz==2014.4
six==1.7.3
validators==0.6.0
wsgiref==0.1.2
Перейдіть у директорію blog/server/ і виконайте команду pip install -r requirements.txt
, щоб встановити необхідні пакети.
Початкові налаштування
Створіть новий файл у blog/server/app і назвіть його config.py, додайте до нього наступне:
DEBUG = True
WTF_CSRF_ENABLED = False
Потім, додайте файл server.py у blog/server/app. Скопіюйте і вставте в нього код:
import os
from flask import Flask
from flask.ext import restful
from flask.ext.restful import reqparse, Api
from flask.ext.sqlalchemy import SQLAlchemy
from flask.ext.bcrypt import Bcrypt
from flask.ext.httpauth import HTTPBasicAuth
basedir = os.path.join(os.path.abspath(os.path.dirname(__file__)), '../')
app = Flask(__name__)
app.config.from_object('app.config')
# flask-sqlalchemy
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///' + os.path.join(basedir, 'app.sqlite')
db = SQLAlchemy(app)
# flask-restful
api = restful.Api(app)
# flask-bcrypt
flask_bcrypt = Bcrypt(app)
# flask-httpauth
auth = HTTPBasicAuth()
@app.after_request
def after_request(response):
response.headers.add('Access-Control-Allow-Origin', '*')
response.headers.add('Access-Control-Allow-Headers', 'Content-Type,Authorization')
response.headers.add('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE')
return response
import views
В цьому файлі ми ініціалізуємо Flask, завантажуємо змінні налаштувань з конфігураційного файлу, створюємо flask-sqlalchemy, об'єкти flask-restful і т.д... Ми також додаємо деякі заголовки-відповіді у функцію after_request, яка дозволить CORS. Це надасть можливість розміщати сервер (REST API) і клієнт (AngularJS додаток) на різних доменах та піддоменах (приклад: api.example.com і example.com). Під час розробки, це дозволить запускати їх через різні порти (приклад: localhost:8000 і localhost:5000).
Моделі
Тепер, коли ми закінчили з початковими налаштуваннями, давайте визначимося з моделями. Створіть новий файл у blog/server/app каталог і назвіть його models.py, скопіюйте і вставте код:
from flask import g
from wtforms.validators import Email
from server import db, flask_bcrypt
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
email = db.Column(db.String(120), unique=True, nullable=False, info={'validators': Email()})
password = db.Column(db.String(80), nullable=False)
posts = db.relationship('Post', backref='user', lazy='dynamic')
def __init__(self, email, password):
self.email = email
self.password = flask_bcrypt.generate_password_hash(password)
def __repr__(self):
return '<user %r="">' % self.email
class Post(db.Model):
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(120), nullable=False)
body = db.Column(db.Text, nullable=False)
user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
created_at = db.Column(db.DateTime, default=db.func.now())
def __init__(self, title, body):
self.title = title
self.body = body
self.user_id = g.user.id
def __repr__(self):
return '<post %r="">' % self.title
У коді вище ми визначили User і Post моделі. User модель має id
в якості первинного ключа, який визначається як ціле число, і поля email
та password
, які визначені як рядки. Вона також має зв'язок моделлю Post через поле posts
. Модель Post також має id
в якості первинного ключа, який також визначається як ціле число, і поля title
та body
, які визначені як рядки. Вона також має поле user_id
, яке визначається як ціле число і як зовнішній ключ для поля id
моделі User. Вона також має поле created_at
, що визначається типом DateTime.
Форми
Тепер, коли ми закінчили визначення моделей, визначимо форми, які ми будемо використовувати для перевірки даних, що вводяться користувачем. Для перевірки форми, ми будемо використовувати розширення для Flask під назвою WTForms і доповнення WTForms-Alchemy - для більш швидкого та простого визначення нашої форми. Створіть новий файл під blog/server/app і назвіть його forms.py, а потім додайте наступний код:
from flask.ext.wtf import Form
from wtforms_alchemy import model_form_factory
from wtforms import StringField
from wtforms.validators import DataRequired
from app.server import db
from models import User, Post
BaseModelForm = model_form_factory(Form)
class ModelForm(BaseModelForm):
@classmethod
def get_session(self):
return db.session
class UserCreateForm(ModelForm):
class Meta:
model = User
class SessionCreateForm(Form):
email = StringField('email', validators=[DataRequired()])
password = StringField('password', validators=[DataRequired()])
class PostCreateForm(ModelForm):
class Meta:
model = Post
Для того, щоб WTForms-Alchemy працювало з Flask-WTF, ми визначили клас з ім'ям ModelForm, який наслідується від BaseModelForm, наданий WTForms-Alchemy. Ви можете знайти більше інформації тут.
Якщо ви не розумієте, що відбувається у наведеному вище коді, я рекомендую вам ознайомитись з документацією WTForms і WTForms-Alchemy.
Серіалізатори
Щоб рендерити моделі даних у JSON форматі в наших відповідях, спочатку необхідно перетворити їх у нативні типи даних мови Python. Flask-RESTful може зробити це, використовуючи модуль полів і декоратор marshal_with() (докладніше - тут). Я не знав, що Flask-RESTful їх підтримує, коли почав будувати REST API, так що я використовував Marshmallow. Створіть новий файл у каталозі blog/server/app і назвіть його serializers.py, скопіюйте і вставте туди наступний код:
from marshmallow import Serializer, fields
class UserSerializer(Serializer):
class Meta:
fields = ("id", "email")
class PostSerializer(Serializer):
user = fields.Nested(UserSerializer)
class Meta:
fields = ("id", "title", "body", "user", "created_at")
Views
Тепер, коли ми закінчили визначення потрібних нам моделей, форм і серіалізаторів, визначимося з view і їх використанням. Створіть новий файл у blog/server/app каталог і назвіть його views.py, скопіюйте і вставте код:
from flask import g
from flask.ext import restful
from server import api, db, flask_bcrypt, auth
from models import User, Post
from forms import UserCreateForm, SessionCreateForm, PostCreateForm
from serializers import UserSerializer, PostSerializer
@auth.verify_password
def verify_password(email, password):
user = User.query.filter_by(email=email).first()
if not user:
return False
g.user = user
return flask_bcrypt.check_password_hash(user.password, password)
class UserView(restful.Resource):
def post(self):
form = UserCreateForm()
if not form.validate_on_submit():
return form.errors, 422
user = User(form.email.data, form.password.data)
db.session.add(user)
db.session.commit()
return UserSerializer(user).data
class SessionView(restful.Resource):
def post(self):
form = SessionCreateForm()
if not form.validate_on_submit():
return form.errors, 422
user = User.query.filter_by(email=form.email.data).first()
if user and flask_bcrypt.check_password_hash(user.password, form.password.data):
return UserSerializer(user).data, 201
return '', 401
class PostListView(restful.Resource):
def get(self):
posts = Post.query.all()
return PostSerializer(posts, many=True).data
@auth.login_required
def post(self):
form = PostCreateForm()
if not form.validate_on_submit():
return form.errors, 422
post = Post(form.title.data, form.body.data)
db.session.add(post)
db.session.commit()
return PostSerializer(post).data, 201
class PostView(restful.Resource):
def get(self, id):
posts = Post.query.filter_by(id=id).first()
return PostSerializer(posts).data
api.add_resource(UserView, '/api/v1/users')
api.add_resource(SessionView, '/api/v1/sessions')
api.add_resource(PostListView, '/api/v1/posts')
api.add_resource(PostView, '/api/v1/posts/<int:id>')
Функція verify_password, яка декорована auth.verify_password, буде використовуватися Flask-HTTPAuth для автентифікації користувачів. Обирається користувач по електронній пошті і перевіряється, чи збігається пароль з паролем, що зберігаються в базі даних. UserView клас буде обробляти запити на реєстрацію користувача, SessionView клас буде обробляти запити на вхід, PostListView клас буде обробляти вибірки списків постів і запити на їх створення. І нарешті, PostView клас буде обробляти отримання окремого посту. У нижній частині файлу, ми просто налаштовуємо маршрутизацію ресурсу для API.
Створення бази даних
Ми також повинні створити файл з назвою db_create.py. Ми будемо запускати скрипт для ініціалізації бази даних. Тепер перейдіть у blog/server/і запустіть скрипт використовуючи python db_create.py
from app.server import db
db.create_all()
REST API сервер
Ще один файл, який ми повинні створити, це run.py. Через цей скрипт ми будемо запускати REST API сервер.
from app.server import app
app.run()
Якщо ви виконали описані вище дії правильно, ви повинні мати можливість запустити REST API сервер, зайшовши у каталог blog/server і запустивши python run.py
Останні кроки
Реєстрація користувача
Для реєстрації користувача необхідно відправити POST-запит на localhost:5000/API/В1/users. Обов'язкові поля: email
і password
. Ось приклад: curl --dump-header - -H "Content-Type: application/json" -X POST -d '{"email": "johndoe@gmail.com","password": "admin"}' http://localhost:5000/api/v1/users
Авторизація користувача
Для входу користувача в систему, вам необхідно відправити реквізити доступу користувача до localhost:5000/API/В1/сесій за допомогою запиту Post. Приклад: curl --dump-header - -H "Content-Type: application/json" -X POST -d '{"email": "johndoe@gmail.com","password": "admin"}' http://localhost:5000/api/v1/sessions
Створення посту
Щоб створити пост, вам потрібно відправити Post-запит на localhost:5000/API/В1/posts. Обов'язкові поля: title і body. Оскільки користувач зобов'язаний реєструватися при створенні посту, зверніть увагу, що вам також потрібно надіслати авторизаційний заголовок, що містить base64-закодовані облікові дані користувача, розділені двокрапкою (":"). Приклад: curl --dump-header - -H "Content-Type: application/json" -H "Authorization: Basic am9obmRvZUBnbWFpbC5jb206YWRtaW4=" -X POST -d '{"title": "Example post","body": "Lorem ipsum"}' http://localhost:5000/api/v1/posts
Отримання постів
Для вибірки всіх збережених постів, вам потрібно відправити запит GET на http://localhost:5000/api/v1/posts. Приклад: curl -i http://localhost:5000/api/v1/posts
Крім Curl, ви також можете використовувати Advanced REST Client - розширення для Chrome для перевірки API.
Що далі?
Перша частина уроку на цьому закінчується. У другій частині ми будемо фокусуватися на створенні angularjs веб-додатку, який буде використовувати API, який ми тільки що створили.
Ще немає коментарів