Cominciamo a definire il database definendo gli oggetti che rappresenteranno le entità
in relazione tra loro.
Nel file fantamanager/app/models.py cominciamo con l’importare ciò di cui abbiamo bisogno:
from . import db
from datetime import datetime
con la prima linea di codice torniamo indietro di una posizione (app) e trovando un
file __init__.py dove abbiamo definito db, lo importiamo
Ora definiamo degli oggetti molto scarni, con solo un id rappresentativo e la relazione
che lega una entità, all’altra.
class User(db.Model):
__tablename__ = 'users'
id = db.Column(db.Integer, primary_key=True)
teams = db.relationship('Team', backref='user')
# M2M secondary tables
leagues_teams = db.Table('leagues_teams',
db.Column('league_id', db.Integer,
db.ForeignKey('leagues.id')),
db.Column('team_id', db.Integer,
db.ForeignKey('teams.id')))
# M2M secondary Association object
class LineupsPlayers(db.Model):
__tablename__ = 'lineups_players'
player_id = db.Column(db.Integer, db.ForeignKey('players.id'),
primary_key=True)
lineup_id = db.Column(db.Integer, db.ForeignKey('lineups.id'),
primary_key=True)
player = db.relationship('Player',
backref=db.backref('form_ass', lazy='dynamic'))
position = db.Column(db.Integer)
class League(db.Model):
__tablename__ = 'leagues'
id = db.Column(db.Integer, primary_key=True)
matches = db.relationship('Match', backref='league', lazy='dynamic')
teams = db.relationship('Team', secondary=leagues_teams,
backref=db.backref('all_leagues', lazy='dynamic'),
lazy='dynamic')
class Team(db.Model):
__tablename__ = 'teams'
id = db.Column(db.Integer, primary_key=True)
leagues = db.relationship('League', secondary=leagues_teams,
backref=db.backref('all_teams', lazy='dynamic'),
lazy='dynamic')
players = db.relationship('Player', backref='team', lazy='dynamic')
lineups = db.relationship('Lineup', backref='team', lazy='dynamic')
trades = db.relationship('Trade', backref='team', lazy='dynamic')
user_id = db.Column(db.Integer, db.ForeignKey('users.id'))
class Player(db.Model):
__tablename__ = 'players'
id = db.Column(db.Integer, primary_key=True)
team_id = db.Column(db.Integer, db.ForeignKey('teams.id'),)
evaluations = db.relationship('Evaluation', backref='player',
lazy='dynamic')
class Match (db.Model):
__tablename__ = 'matches'
id = db.Column(db.Integer, primary_key=True)
league_id = db.Column(db.Integer, db.ForeignKey('leagues.id'))
home_id = db.Column(db.Integer, db.ForeignKey('teams.id'),)
visit_id = db.Column(db.Integer, db.ForeignKey('teams.id'),)
home = db.relationship('Team', foreign_keys='Match.home_id',
primaryjoin="Match.home_id==Team.id")
visit = db.relationship('Team', foreign_keys='Match.visit_id',
primaryjoin="Match.visit_id==Team.id")
class Lineup (db.Model):
__tablename__ = 'lineups'
id = db.Column(db.Integer, primary_key=True)
team_id = db.Column(db.Integer, db.ForeignKey('teams.id'))
players = db.relationship('Player', secondary='lineups_players',
backref=db.backref('lineups', lazy='dynamic'))
def get_players_by_position(self):
lu_players = LineupsPlayers.query.filter_by(lineup_id=self.id).order_by(
LineupsPlayers.position).all()
if lu_players:
return [rec.player for rec in lu_players]
class Evaluation(db.Model):
__tablename__ = 'evaluations'
id = db.Column(db.Integer, primary_key=True)
player_id = db.Column(db.Integer, db.ForeignKey('players.id'))
class Trade(db.Model):
__tablename__ = 'trades'
id = db.Column(db.Integer, primary_key=True)
team_id = db.Column(db.Integer, db.ForeignKey('teams.id'),)
player_id = db.Column(db.Integer, db.ForeignKey('players.id'),)
player = db.relationship('Player', foreign_keys='Trade.player_id',
primaryjoin="Trade.player_id==Player.id")
Sommariamente:
User è in relazione “uno a molti” con Team poichè come utente posso
partecipare con più squadre. mentre una squadra non può essere di più utenti.
League è in relazione “molti a molti” con Team, poichè si potrebbe, parallelamente
alla League, partecipare anche ad una coppa con la stessa squadra.
Team è in relazione “uno a molti” con Player, poichè un giocatore non può
far parte di più squadre. Qui si acuista un giocatore tramite asta.
E così via per le altre entità.
Cominciamo a creare il database poi, per gradi, aggiungeremo i campi che
ci servono grazie alle migrations.
Portiamoci al livello di fantamanager/manage.py e se abbiamo ancora la dir migrations,
rappresentativa della struttura di un progetto complesso, rimuoviamola.
Ora lanciamo il comando di inizializzazione del database:
python manage.py db init
(su ubuntu si può omettere il comando python, avendo utilizzato il shebang)
>python manage.py db init
Creating directory ...\fantamanager\migrations ... done
Creating directory ...\fantamanager\migrations\versions ... done
...
il db non è ancora stato creato, ma possiamo notare che la dir migration è stata creata.
Procediamo con il comando che genererà il primo script di migration:
python manage.py db migrate -m "first migration"
e il comando che rende la modifica permanente:
python manage.py db upgrade
>python manage.py db upgrade
INFO [alembic.migration] Context impl SQLiteImpl.
INFO [alembic.migration] Will assume non-transactional DDL.
Ora apriamo il file manage.py e rendiamo disponibili per la shell tutti gli
oggetti, in modo da non reimportarli ogni volta.
La funzione make_shell_context sarà:
def make_shell_context():
return dict(app=app, db=db,
User=User, LineupsPlayers=LineupsPlayers,
League=League, Team=Team, Player=Player,
Match=Match, Lineup=Lineup,
Evaluation=Evaluation, Trade=Trade)
Ora sistemiamo i nostri oggetti nel file models.py inquanto non
abbiamo inserito nessun campo, ma solo le relazioni tra gli oggetti stessi
es di User, League e Team migliorati:
#...
class User(db.Model):
__tablename__ = 'users'
id = db.Column(db.Integer, primary_key=True)
teams = db.relationship('Team', backref='user')
email = db.Column(db.String(64), unique=True, index=True)
username = db.Column(db.String(64), unique=True, index=True)
#...
class League(db.Model):
__tablename__ = 'leagues'
id = db.Column(db.Integer, primary_key=True)
matches = db.relationship('Match', backref='league', lazy='dynamic')
teams = db.relationship('Team', secondary=leagues_teams,
backref=db.backref('all_leagues', lazy='dynamic'),
lazy='dynamic')
name = db.Column(db.String(40), unique=True)
budget = db.Column(db.Integer)
max_trades = db.Column(db.Integer)
max_goalkeepers = db.Column(db.Integer)
max_defenders = db.Column(db.Integer)
max_midfielders = db.Column(db.Integer)
max_forwards = db.Column(db.Integer)
rounds = db.Column(db.Integer)
offset = db.Column(db.Integer)
#...
class Team(db.Model):
__tablename__ = 'teams'
id = db.Column(db.Integer, primary_key=True)
leagues = db.relationship('League', secondary=leagues_teams,
backref=db.backref('all_teams', lazy='dynamic'),
lazy='dynamic')
players = db.relationship('Player', backref='team', lazy='dynamic')
lineups = db.relationship('Lineup', backref='team', lazy='dynamic')
trades = db.relationship('Trade', backref='team', lazy='dynamic')
user_id = db.Column(db.Integer, db.ForeignKey('users.id'))
name = db.Column(db.String(50), unique=True)
budget = db.Column(db.Integer())
max_trades = db.Column(db.Integer())
#...
creiamo la seconda migration con il comando:
python manage.py db migrate -m "user, team, league fields added"
>python manage.py db migrate -m "user, team, league fields added"
INFO [alembic.migration] Context impl SQLiteImpl.
INFO [alembic.migration] Will assume non-transactional DDL.
INFO [alembic.autogenerate.compare] Detected added table 'users'
INFO [alembic.autogenerate.compare] Detected added index 'ix_users_email' on '['email']'
INFO [alembic.autogenerate.compare] Detected added index 'ix_users_username' on '['username']'
INFO [alembic.autogenerate.compare] Detected added table 'leagues'
INFO [alembic.autogenerate.compare] Detected added table 'matches'
INFO [alembic.autogenerate.compare] Detected added table 'teams'
INFO [alembic.autogenerate.compare] Detected added table 'trades'
INFO [alembic.autogenerate.compare] Detected added table 'leagues_teams'
INFO [alembic.autogenerate.compare] Detected added table 'players'
INFO [alembic.autogenerate.compare] Detected added table 'lineups'
INFO [alembic.autogenerate.compare] Detected added table 'lineups_players'
INFO [alembic.autogenerate.compare] Detected added table 'evaluations'
Generating C:\fantamanager\migrations\versions\3ebec3a59744_user_team_league_fields_added.py ... done
lo script di migration è generato, per renderlo effettivo facciamo l’upgrade:
python manage.py db upgrade
Nel caso si volesse tornare indietro, basterà al posto di “upgrade” utilizzare “downgrade”
>python manage.py db upgrade
INFO [alembic.migration] Context impl SQLiteImpl.
INFO [alembic.migration] Will assume non-transactional DDL.
INFO [alembic.migration] Running upgrade -> 3ebec3a59744, user, team, league fields added
Ora possiamo entrare nella shell furba con il comando:
python manage.py shell
Creiamo un paio di Users senza salvarli su db:
>>> u1 = User(username="user_1", email="[email protected]")
>>> u2 = User(username="user_2", email="[email protected]")
i dati senza il comando “add” e “commit” non sono salvati, infatti:
>>> User.query.all()
[]
se richiamiamo le istanze di User create, ci accorgiamo che non
sono proprio riconoscibili infatti:
>>> u1
<app.models.User object at 0x039A9E30>
>>> u2
<app.models.User object at 0x039A9270>
Per renderle più amichevoli, dobbiamo utilizzare il metodo __repr__
>>> def myrepr(self):
... return "<User %r>" % self.username
...
>>> User.__repr__ = myrepr
>>> u1
<User 'user_1'>
>>> u2
<User 'user_2'>
Ok, visto che ci piace, rendiamo effettiva questa modifica nel file models.py
aggiungendo il metodo __repr__ in ogni classe, come più ci piace.
Ora che abbiamo visto come lavorare sul database, modificandolo, focalizziamoci
sulle singole classi per introdurre anche le view e le template.
articolo successivo:
Flask (parte 4): views e templates
articoli precedenti:
Flask (parte 1): virtualenv
Flask (parte 2): struttura progetto complesso
link utili:
“Flask Web Development” di Miguel Grinberg, fondamentale per calarsi nel mondo di Flask.
Altri link fondamentali:
Flask sito ufficiale
il blog dell’autore (Flask mega tutorial)
Commenti recenti