Frontend:
Dane z backendu pobieram przy wybraniu z opcji menu komponentu Navbar frameworka Bulma. Aby dane były wczytane podczas randerowania komponentu używam hook useEffect. Tworzone jest wówczas zapytanie do backendu, a jako parametr przekazywany jest token, ustawiany podczas logowania użytkownika. Z backendu przesyłana jest odpowiedź w formie obiektu Promise, z którego jeśli zapytanie zostało zakończone sukcesem jest ustawiana zmienna cursor (zmiana stanu za pomocą setCursor). Cursor i funkcja setCursor przekazywane są do komponentu z komponentu nadrzędnego App.js jako parametry (props) tzn.
import LoginForm from '../Forms/LoginForm/LoginForm'
import { useEffect } from "react";
function Contracts({token, putToken, cursor, setCursor}) {
useEffect(
() => {
const token = sessionStorage.getItem('token')
fetch('/api/contract/' + token)
.then(res => res.json()).then(data => setCursor(data)).catch((err) => console.log(err))
}, [token, setCursor],
);
if (!token) {
return <LoginForm putToken={putToken} />
};
Aby komponent odświeżał dane po poprawnym zalogowaniu się w dodatkowym parametrze metody useEffect w tablicy umieściłem zmienną token, która zostaje ustawiona w komponencie LoginForm.js
Aby komponent odświeżał dane po zmianie zawartości cursora np. poprzez dodanie nowego kontraktu jako dodatkowy parametr metody useEffect umieściłem funkcję setCursor.
Poszczególne wiersze danych są umieszczane w tabeli tzn.
<tbody>
{cursor && Object.keys(cursor).map((keyName, keyIndex) =>
<tr key={keyIndex}>
<td>{keyIndex+1}</td>
<td>{(cursor[keyName].status)}</td>
<td>{(cursor[keyName].contract_number)}</td>
<td>{(cursor[keyName].contractor)}</td>
<td>{(cursor[keyName].customer)}</td>
<td>{(cursor[keyName].date_of_order)}</td>
<td>{(cursor[keyName].date_of_delivery)}</td>
<td>{(cursor[keyName].pallets_position)}</td>
<td>{(cursor[keyName].pallets_planned)}</td>
<td>{(cursor[keyName].pallets_actual)}</td>
<td>{(cursor[keyName].warehouse)}</td>
</tr>)}
</tbody>
Backend we Flask-restful:
W pliku __init__.py dodaję kolejną klasę zasobów:
from api.resources.contracts import AllContracts
api.add_resource(AllContracts, '/api/contract/<token>', endpoint='all_contracts')
W pliku contracts.py definiuję klasę zasobów:
from datetime import datetime
from flask import request
from flask_restful import Resource
from .. import db
from ..common.models import User, Contract
class AllContracts(Resource):
def get(self, token):
token = token.strip('"')
user = User.query.filter_by(token=token).first()
contracts = Contract.query.filter_by(customer=user.username).order_by(Contract.id.desc()).all()
cursor = {}
for i, contract in enumerate(contracts):
cursor[i] = contract.serialize()
return cursor
W pliku models.py definiuję m.in. jak klasa Contract dziedzicząca po klasie Model z SQLAlchemy ma być serializowana:
from .. import db
from datetime import datetime
class Contract(db.Model):
'''Model of contract between a contractor and a customer'''
id = db.Column(db.Integer, primary_key=True)
status = db.Column(db.String(10), nullable=False, default='open')
contract_number = db.Column(db.String(20), nullable=False)
contractor = db.Column(db.String(20), db.ForeignKey('user.username'), nullable=False)
customer = db.Column(db.String(20), db.ForeignKey('user.username'), nullable=False)
date_of_order = db.Column(db.Date, nullable=False, default=datetime.utcnow)
date_of_delivery = db.Column(db.Date, nullable=False)
pallets_position = db.Column(db.Integer)
pallets_planned = db.Column(db.Integer, nullable=False)
pallets_actual = db.Column(db.Integer)
warehouse = db.Column(db.String(10), nullable=False)
def serialize(self):
return {'id': self.id,
'status': self.status,
'contract_number': self.contract_number,
'contractor': self.contractor,
'customer': self.customer,
'date_of_order': datetime.strftime(self.date_of_order, '%Y-%m-%d'),
'date_of_delivery': datetime.strftime(self.date_of_delivery, '%Y-%m-%d'),
'pallets_position': self.pallets_position,
'pallets_planned': self.pallets_planned,
'pallets_actual': self.pallets_actual,
'warehouse': self.warehouse
}