Flask & Vue. Друга серія прикладів
Вітаю. Починаю наступну, другу серію прикладів Flask + Vue. У цій серії будуть наступні статті:
- Згадати все (ця стаття)
- Посторінковий вивід даних (пагінація). Як зробити правильно
- Посторінковий вивід даних + сортування + пошук
- Проста Аутентифікація
- Авторизація
- Наводимо красу. Кастомний вигляд компоненту "Product"
- Знову переробляємо backend. SQLAlchemy, Migrate, Marshmallow, JWT
- Використання систем збірки проекту. Webpack
Попередження. Все, що я буду куралесити на бекенді (мається на увазі до прикладу з SQLAlchemy), то так вже ніхто не робить. Цю "дичину" я буду творити тільки щоб показати як працює фронтенд, показати як воно працює у нутрях бекенду, і чому не треба морочити собі...голову, а використовувати вже стандартні готові рішення. Поки що будемо страждати збоченнями на бекенді і виписувати фронтенд. А потім ми перепишемо бекенд на сучасні рівні рейки, а вже в наступній серії переробимо фронтенд щоб він збирався по модньому. І тоді, я так думаю, Ви відчуєте різницю і зрозумієте чому зараз роблять саме так як це буде у наступній серії прикладів. А поки що починаємо другу серію прикладів з неофіційною назвою "збочення з бекендом".
Нагадаю, що попередня серія прикладів на тему Vue + трішечки Flask була присвячена побудові структур додатків. Ми зробили простий додаток: backend - на Flask, frontend - на Vue. У якості бази даних використовували SQLite. Для Flask побудували структуру для простого і швидкого додавання нових об'єктів даних. Для Vue створили комплекс компонент для роботи з даними. А саме: таблицю (standard-table), поля (form-field), форму (standard-form), і стандартну сторінку (standard-page) для перегляду і редагування даних. Компоненту standard-page можна вказати які саме дані використовувати з тих, що описані у const appDataset, і компонент буде будувати таблицю з даними, формую для редагування, додавання, видалення даних. Які поля будуть у таблиці, а які у формі теж описується у const appDataset.
Тобто, ми зробили компонент standard-page для того, щоб кожного разу не виписувати код для редагування наступної таблички у базі, а використовувати готові компоненти. "standard" у назві компоненти натякає на те, що це стандартна компонента і її зовнішній вигляд і функціонал буде дуже схожий для усіх даних, до яких ми будемо її застосовувати. Тобто, зовнішній вигляд усіх таблиць і форм суворо стандартизований. Нехай трохи йому не вистачає різноманітності, але він незмінний. У такому випадку, завдяки стандартній компоненті standard-page просто вказуємо які дані ми хочемо редагувати і все. Ми вже не програмуємо, а збираємо додаток з готових, стандартних блоків. Якщо Вам потрібно відійти від стандартів - це не проблема. Можна написати свою компоненту і використовувати, скажімо, компоненти standard-form, standard-table. Тобто, використовувати трохи дрібніші блоки. Якщо і форма не підходить - робимо свою форму і використовуємо ще більш дрібніші компоненти, наприклад компонент form-field. І так далі. Але це потребує значно більше часу. Тому стандартизація значно прискорює розробку.
У минулій серії прикладів ми зупинилися на простих формах з "простими" даними.
Задача на сьогодні:
- Створити у базі даних нову табличку "COUNTRIES" (довідник країн), додати у таблицю "CLIENT" поле "country_id"
- Додати у web-додаток можливість редагувати довідник країн.
- Додати у форму редагування Клієнта можливість вказувати країну зі списку. Список має містити перелік країн.
Цей приклад - є продовженням попереднього прикладу Vue + Flask. Ви можете завантажити попередній приклад Flask + Vue і виконати всі дії, описані у статті власноруч. Або завантажити готовий приклад Flask + Vue.
Модифікація Бази даних
Запускаємо скрипт modifyDB.py - цей файл містить мінімально необхідні SQL для модифікації бази даних і наповнення нової таблички тестовими даними.
modifyDB.py:
import sqlite3
conn = sqlite3.connect('myApp.db')
cur = conn.cursor()
print ("Opened database successfully")
conn.execute('''CREATE TABLE COUNTRIES
(id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
name TEXT,
code TEXT,
flag_img TEXT
);''')
print ("Table created successfully")
#############################
# Insert Data into table COUNTRIES
#############################
countries = [
{'id':1, 'name': 'Ukraine', 'code':'380', 'flag_img': 'ua.png'},
{'id':2, 'name': 'USA', 'code':'380', 'flag_img': 'us.png'},
{'id':3, 'name': 'France', 'code':'33', 'flag_img': 'fr.png'}
]
for item in countries:
SQL = 'INSERT INTO COUNTRIES (id, name, code, flag_img) VALUES({0}, "{1}", "{2}", "{3}")'.format(item['id'], item['name'], item['code'], item['flag_img'])
cur.execute(SQL)
print ("Table 'COUNTRIES' is done")
conn.execute('''ALTER TABLE CLIENT ADD COLUMN country_id INTEGER;''')
print ("Table CLIENT modified successfully")
conn.execute('''CREATE INDEX IF NOT EXISTS client_country_id ON CLIENT (country_id)''')
print ("Index created successfully")
conn.commit()
print ("Commit is done")
conn.close()
print ("Database is closed")
print ("Good luck!")
Модифікація Backend
Треба додати файл countries.py в директорію model
Файл countries.py містить наступне:
from model import default
class model(default.model):
def __init__(self, connection):
self.connection = connection
self.objName = 'COUNTRIES'
У файлі app_db.py додати "countries" у рядок:
from model import empty, menu, client, seller, product, countries
у цьому ж файлі додати рядок:
self.model['countries'] = product.model(self.connection)
Все! База даних і Backend готовий!
Frontend
Створюємо редактор довідника країн.
В const appDataset описуємо "countries" - посилання на backend, описуємо поля, які потрібно показати у таблиці, і які у формі:
'countries': {
'instance': 'countries',
'url': 'http://localhost:5000/countries/',
'fields': {
'table': [
{name:'name', 'title': 'Name', type:'string', sort: true},
{name:'flag_img', 'title': 'Flag', type:'string', sort: false},
],
'form': [
{name:'name', 'title': 'Name', type:'string', required:true},
{name:'code', 'title': 'Code', type:'string', required:true},
{name:'flag_img', 'title': 'Flag', type:'string' required:false},
]
}
}
в роутах додамо рядок:
{ path: '/countries', component: { template: ' ' } },
в menu.json додамо:
{
"item": "Countries",
"path": "#/countries"
},
Нагадаю: menu віддає backend. Ми його запхали у json винятково для демонстрації гнучкості і універсальності підходу.
Все! Редактор на Frontend готовий! От і все! Ми нічого нового не програмували! Ми використали раніше написаний компонент standard-page і лише вказали нові дані і у якій таблиці і формі їх показувати.
Поїхали далі
Редактор довідника країн готовий. Тепер додамо список (select) у форму редагування клієнта.
Основний підхід такий - основні дані (клієнта) завантажуються, як і раніше. Додаткові дані, (список країн) завантажуються окремо. При чому всі дані завантажує основний компонент. Це важливо! (щось не ясно - дивись попередні статті). Не треба функцію завантаження даних перекладати на компонент списку. Він тупо список - його робота просто показати, і коли щось змінилось повернути event. Що робити з даними - це клопіт основного компонента.
1. Міксин "crud_ext"
Новий функціонал завантаження додаткових даних зробимо у окремому міксині "crud_ext". Цей міксин ми потім будемо додавати при створенні компонентів.
Міксин "crud_ext":
var crud_ext = {
computed: {
form_fields: function () {
let fields = appDataset[this.instance]['fields']['form']
for (field of fields) {
// Prepare @select fields
if (field['type'] == 'select') {
field.items = []
if (field['dataset']) {
if (field['dataset']['src']) {
if (this[field['dataset']['src']]) {
let src = this[field['dataset']['src']]
for (item of src) {
field.items.push({value: item[field['dataset']['value']], caption: item[field['dataset']['caption']]})
}
}
}
}
}
}
return fields
}
},
methods: {
read_ext_data: function(instance) {
let url = appDataset[instance]['url']
let options = {method: 'GET'}
this.fetch_execute(url, options,
(response)=>{
this.$set(this, instance, response)
},
(response)=>{
this.show_error(response.errors)
}
)
}
}
}
В ньому є метод read_ext_data, який і завантажує додаткові дані, та computed поле form_fields яке обробляє і готує дані для форми на основі даних вказаних в описі instance (див. const appDataset) і завантажених додаткових даних. Зараз ми навчили form_fields готувати дані тільки для полів типу "select", але у подальшому його можна розширювати для інших типів. І робити це доведеться у одному місці - тут у міксині crud_ext.
2. Доробили компонент standard-form
Трохи доробили компонент standard-form (довелося додати свою data для забеспечення інтерактивності (щоб списки могли динамічно оновлюватися, коли в цьому виникне потреба)).3. Доробили компонент client-edit
Компонент client-edit відповідає за редагування клієнта (дивись попередні приклади і статті)В опис компонента client-edit додаємо створений міксин "crud_ext":
mixins: [crud, crud_ext],
додаємо дані "countries:
data : function () {
return {
countries: this.countries
}
},
у "mounted" додаємо виклик метода завантаження додаткових даних:
...
this.read_ext_data('countries')
...
4. Опис поля Country
Опис поля select для 'Country' тепер виглядає так:
{name:'country_id', 'title': 'Country', type:'select', dataset: {src: 'countries', value: 'id', caption: 'name'}},
- src - назва instance, звідки завантажувати дані
- value - поле, яке використовується, як значення
- caption - поле, яке використовується, як надпис у списку
Ми щойно розглянути, як доробити свою власну компоненту "client-edit". Ми її робили для того, щоб під час редагування був окремий роут (окремий URL). Але в нас є ще і універсальний компонент "standard-page", який показує таблицю з даними, форму для додавання / редагування даних і т.п. Треба і її навчити працювати з випадаючими списками.
Доробка standard-page
1. Добавили mixin crud_ext:
mixins: [crud, crud_front, crud_ext],
2. Модифікували "data", щоб зберігалася інтерактивність і в завантажених додатково даних
data: function () {
let obj = {
current_row: this.current_row,
form_data: this.form_data
}
if (this.datasets) {
for (ext_data of this.datasets) {
obj[ext_data] = this[ext_data]
}
}
return obj
},
3. У "mounted" для завантаження додаткових даних додали наступний код
Перелік (це масив) необхідних додаткових даних передається параметром "datasets":
if (this.datasets) {
this.datasets_array = []
for (ext_data of this.datasets) {
console.log(ext_data)
this.read_ext_data(ext_data)
this.datasets_array.push(ext_data)
}
}
4. В роутах опис стандартної сторінки виглядає так
{ path: '/client_old', component: { template: ' ' } },
Ми додали параметр datasets і вказали в ньому які додаткові дані треба завантажити.
Висновок
Раніше побудований CRUD - підхід на бекенді і на фронтенді дозволив швидко зробити новий об'єкт даних і створити форму для його редагування практично не програмуючи нічого нового. Вся пророблена сьогодні робота стосується тільки нового функціоналу. Звісно, це стосується досить простих, стандартних даних. Зі складними все цікавіше і доводиться дещо робити руками. Але саме для того, щоб позбутися рутини у своїй роботі і займатися виключно новими і цікавими речами і використовують сучасні практики.
P.S. У наступній статті: пагінація - взаємодія з сервером; що не так з компонентою standard-table?; як зробити правильно.
Як запустити приклад?
- Завантажити архів з прикладом, розпакувати
- Запустити приклад командою:
абоpython ./my_app.py
python3 ./my_app.py
- У браузері відкрити посилання: http://localhost:5000/
Дивись також:
- Flask & Vue. Приклад# 2.1
- Flask & Vue. Приклад# 2.2 Frontend - Зручна навігація
- Flask & Vue. Приклад# 2.3 Серверна пагінація, пошук, сортування даних
- Flask & Vue. Приклад# 2.4 Проста аутентифікація
- Flask & Vue. Приклад# 2.5 Авторизація
- Flask & Vue. Приклад# 2.6 Кастомні компоненти
- Flask & Vue. Приклад# 2.7 SQLAlchemy, Migrate, Marshmallow, JWT
- Flask & Vue. Використання систем збірки проекту. Webpack
- Flask & Vue. OAuth. Facebook / Google Account
- Flask & Vue. Завантаження файлів
Недавні записи
- LCD Display ST7567S (IIC)
- Розпізнавання мови (Speech recognition)
- Selenium
- Комп'ютерний зір (Computer Vision)
- Деякі думки про точність вимірювань в електроприводі
- Датчики Холла 120/60 градусів
- Модуль драйверів напівмосту IGBT транзисторів
- Драйвер IGBT транзисторів на A316J
- AS5600. Варіант встановлення на BLDC мотор
- DC-DC для IGBT драйверів ізольований 2 W +15 -8 вольт
Tags
battery soldering java-script ngnix adc rs-232 flask esp8266 watchdog web exti solar mongodb rtc sms pwm usart books ethernet smd git websocket meteo css python dc-dc displays led ssd1306 eeprom gpio barometer max1674 mpx4115a brushless motor mpu-6050 timer sensors remap servo bldc atmega pmsm 3d-printer flash encoder examples dma raspberry-pi tim ssd1331 piezo mpu-9250 rfid eb-500 foc bme280 gps nvic dht11 bluetooth hih-4000 stm32 st-link docker uart avr html wifi bmp280 bkp nodemcu options usb lcd programmator i2c capture
Архіви