Upload
others
View
3
Download
0
Embed Size (px)
Citation preview
Översikt
18-01-20TDDD80 Mobila och sociala applikationer
• Relationsdatabaser (tabeller)• SQL (Structured Query Language)• Relationsmodeller– Många-till-många relationer
• Alchemy• Enhetstestning• Modulär kod – designmönstret MVC
Olika typer av databaser
18-01-21TDDD80 Mobila och sociala applikationer
• Relationsdatabaser (SQL-databaser)– Fokus på entiteter och relationer mellan dessa– Tabeller
• Unika rader• NoSQL databaser– Dokument-orienterade databaser (t.ex. MongoDB)– JSON-databaser (t.ex. Firebase)– Key-value databaser (Redis)
SQLite
18-01-20TDDD80 Mobila och sociala applikationer
• Liten SQL-databas som kan lagras på en fil– Oftast lokalt på egen dator– Används med fördel för utveckling och testing
TDDD80 Mobila och sociala applikationer
Entity-relationship diagram (ER)
18-01-21
mångaen
noll el. många
Relationsdatabaser har tabeller
18-01-21TDDD80 Mobila och sociala applikationer
ISBN titel författare ……
Nycklar
18-01-21TDDD80 Mobila och sociala applikationer
• Varje tabell har primär nyckel (ID)– Alltid unik för varje rad
• Dvs. varje relationstupel kan hittas entydigt via primär nyckeln
• Foreign key– Referens till annan tabells primär nyckel– T.ex. länka bok till bok-exemplar, till lån, etc.
Tabeller (exempel)
18-01-20TDDD80 Mobila och sociala applikationer
data saknas (motsv. ingen som har lästdetta meddelande)
Foreign key (kopplar till annan
tabell)
primär nyckel (fördenna tabell)
primär nyckel (fördenna tabell)
Schema (“tabellhuvud”)
18-01-20TDDD80 Mobila och sociala applikationer
• Mall, bestämmer vilka kolumner som ingår i tabellen– Namn– Typ av tillåtna värden
• Integer• String
– Constraints• Unik• Non-null
Data (tabellrader)
18-01-20TDDD80 Mobila och sociala applikationer
• Läggs till t.ex. när app körs• Krux– Singulära värden i tabellen, dvs. ej listor, etc.
• Ställer krav på hur data organiseras i tabeller– Relationsmodellering
Modell = data + relationer mellan dessa
18-01-20TDDD80 Mobila och sociala applikationer
• One-to-one relation– Kan läggas i samma tabell– Men, kanske konceptuellt tydligare i två tabeller
• One-to-many– Två tabeller, där den ena länkar till den andra via
foreign key
Structured Query Language (SQL)• Microsoft Access
• SELECT [Firstname], [Lastname] FROM [Employees] WHERE [Lastname] = "Davolio";
Från denna tabell Detta kriterium
Vill ha ut dessa två kolumner
18-01-20TDDD80 Mobila och sociala applikationer
Ibland måste tabeller kombineras• Ge mig alla avdelningar och dess chef, i hela företaget– Två tabeller behöver kombineras!
SELECT Employees.[Department], Managers.[Manager] FROM Employees INNER JOIN Managers WHERE Employees.[Department] = Managers.[Department];
18-01-21TDDD80 Mobila och sociala applikationer
tabell kolumn
Inner join
A INNER JOIN B behåll bara matchande rader
B 1
D 2
F 3
ABCDE
B 1D 2
18-01-20TDDD80 Mobila och sociala applikationer
Left join (outer join)
B 1
D 2
F 3
ABCDE
AB 1CD 2E
A LEFT JOIN B behåll alla rader från B
18-01-20TDDD80 Mobila och sociala applikationer
Right join (outer join)
B 1
D 2
F 3
A xB zC gD hE p
B 1 zD 2 hF 3
A RIGHT JOIN B alla rader från A
18-01-20TDDD80 Mobila och sociala applikationer
Join
18-01-20TDDD80 Mobila och sociala applikationer
• Läs mer på:– https://community.qlik.com/thread/39177
SQLAlchemy
18-01-20TDDD80 Mobila och sociala applikationer
• Object-Relational Mapper (ORM)• Du kan definiera klasser och metoder som vanligt• SQLAlchemy översätter– Klass till tabell– Variabler till kolumner– Metoder till queries
Sätta upp en databas
18-01-20TDDD80 Mobila och sociala applikationer
from flask_sqlalchemy import SQLAlchemy
db_path = os.path.join(os.path.dirname(__file__), 'app.db')db_uri = 'sqlite:///{}'.format(db_path)app.config['SQLALCHEMY_DATABASE_URI'] = config.db_uriapp.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = True
db = SQLAlchemy(app)
Flask-SQLAlchemy
18-01-20TDDD80 Mobila och sociala applikationer
• Configurerar Alchemy, ger db-objekt
Tabell:class User(db.Model):
__tablename__ = 'users'id = db.Column(db.Integer, primary_key=True)name = db.Column(db.String(5))
…
Exempel (Flask-SQLAlchemy)
18-01-20TDDD80 Mobila och sociala applikationer
class User(Base):__tablename__ = 'users' id = Column(Integer, Sequence('user_id_seq'), primary_key=True) name = Column(String(50)) fullname = Column(String(50)) password = Column(String(12)) def __repr__(self): return "<User(name='%s', fullname='%s',
password='%s')>" % ( self.name, self.fullname, self.password)
Flask-SQLAlchemy queries inom klassdef
18-01-20TDDD80 Mobila och sociala applikationer
class User(db.Model): #...
def follow(self, user): if not self.is_following(user):
self.followed.append(user) return self
def unfollow(self, user): if self.is_following(user):
self.followed.remove(user) return self
def is_following(self, user): return self.followed.filter(followers.c.followed_id == user.id).count() > 0
https://blog.miguelgrinberg.com/post/the-flask-mega-tutorial-part-viii-followers-contacts-and-friends
SQLAlchemy query (forts)
18-01-20TDDD80 Mobila och sociala applikationer
• Tillåter vanlig “python”-syntax– Dina egna data-repr i form av klasser och objekt
• T.ex. message.users.append(user) • Bakom ligger databas-tabeller och SQL-queries
Tre lager av query-syntax
18-01-20TDDD80 Mobila och sociala applikationer
• Flask-SQLAlchemy– Konfigurerar Alchemy åt oss, förenklad syntax
(query_by, etc.)• Alchemy– Kan använda Klasser och vanliga metoder istf
“lågnivå” SQL-queries• SQL– Allt översätts till SQL som loggas om man är i
debug-mode
Exempel1• db.session.query(Employees.lastname).
filter_by(lastname=‘Davalio’).all()
• Employees.query.filter(last_name=‘Davalio’).all()
• SELECT [lastname] FROM [Employees] WHERE [lastname] = "Davolio";
18-01-22TDDD80 Mobila och sociala applikationer
Exempel2
18-01-22TDDD80 Mobila och sociala applikationer
db.session.query(User).filter(User.name.like('%ed')).order_by(User.id)
SELECT users.id AS users_id, users.name AS users_name, users.fullname AS users_fullname, users.password AS users_password
FROM users WHERE users.name LIKE ? ORDER BY users.id('%ed',)
Join i Alchemy (exempel)
18-01-22TDDD80 Mobila och sociala applikationer
db.session.query(Message).join(User).filter(User.name == ‘Kalle’).all()
• Skapar en sammanslagen tmp-tabell, med kolumner frånbåda tabellerna och de rader som matchar (jfr. outerjoin)
• Se vidare: http://docs.sqlalchemy.org/en/latest/orm/query.html
Dynamic queries
18-01-22TDDD80 Mobila och sociala applikationer
• Om man anger lazy=‘dynamic’ i sin tabell-def:– Query exekveras inte utan kan byggas på– Kan lägga på ytterligare filter på en query
• Exekveras vid trigger, t.ex. ‘all()’
• Ändringar i databasen måste commit:as
Alchemy session
18-01-22TDDD80 Mobila och sociala applikationer
• Ändringar i databasen måste commit:as– Tex. skapa nytt objekt från User– db.session.add(user1)
• Session.dirty flaggar nu True– db.session.commit()
• Lägg in ändringar i databasen
Many-to-many relationer
18-01-20TDDD80 Mobila och sociala applikationer
survey_questions = db.Table('survey_questions',db.Column('survey_id', db.Integer, db.ForeignKey('surveys.id')), db.Column('question_id', db.Integer, db.ForeignKey('questions.id')))
class Survey(db.Model): __tablename__ = 'surveys' id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(50), unique=True) ending = db.Column(db.DateTime)
class Question(db.Model): __tablename__ = 'questions' id = db.Column(db.Integer, primary_key=True) text = db.Column(db.Text) help = db.Column(db.Text)
Ytterligare exempel
18-01-22TDDD80 Mobila och sociala applikationer
read_by = db.Table('read_by',
db.Column('user_id', db.Integer,db.ForeignKey('users.id')),
db.Column('message_id', db.Integer, db.ForeignKey('messages.id’)))
class User(db.Message):__tablename__ = ’messages’
Exempel på vanlig tabell
18-01-20TDDD80 Mobila och sociala applikationer
class Message(db.Model):__tablename__ = 'messages'id = db.Column(db.Integer, primary_key=True)mess_id = db.Column(db.Integer, unique=True)message = db.Column(db.String(140))
users = db.relationship('User',secondary=read_by,primaryjoin=(read_by.c.message_id == id),# back_populates='messages',backref=db.backref('messages', lazy='dynamic'),lazy="dynamic")
Many-to-many relationships
18-01-20TDDD80 Mobila och sociala applikationer
• Läs vidare på:• http://www.ergo.io/blog/sqlalchemy-relationships-
from-beginner-to-advanced/
• http://docs.sqlalchemy.org/en/latest/orm/tutorial.html– Klicka på navigationsfältet:
• Building a Many to Many Relationship
Statiska klasser – dynamiskt skapade objekt
18-01-20TDDD80 Mobila och sociala applikationer
• Java– Klasser statiska (finns i koden, filerna)– Objekt skapas när ett javaprogram körs igång
• Skilda världar– Kan t.ex. inte komma åt vanliga variabler förrän ett
objekt har skapats som är “värd” för variabeln• Detta eftersom olika objekt kan ha olika värden på
samma variabel– myTriangle = new Triangle().setColor(“Red”)– hisTriangle = new Triangle().setColor(“Blue”)
Modell – databas
18-01-20TDDD80 Mobila och sociala applikationer
• Modell (databas-schema) statisk– Anger vilken typ av data som ska repr
• Databas dynamisk– Innehåller data från en speciell körning– Data för företag X vs. data för företag Y– Data under utveckling och testning != data för
deployment (sjösatt system)
Kod som ska sjösättas
18-01-20TDDD80 Mobila och sociala applikationer
• All kod– Utom log-filer, etc.
• Datamodell– Ej själva databasen– Databasen skapas dynamiskt på plats (på den
server som ska hantera de riktiga kunderna)
• Databasen ska inte versionshanteras– Om man inte vill ha “hårdlödd” databas
Skickar “fejk” requests
18-01-20TDDD80 Mobila och sociala applikationer
• Sätter upp test-databas med start-data• Kör ett antal testfall• Varje testfall: sekvens av anrop som bygger upp
“scenen”• T.ex. “Steg för steg, lägg i 2 meddelanden i
databasen, med första meddelandet läst av‘Kalle’
– Slutligt anrop som utvärderas i scenen somskapades
Exempel
18-01-20TDDD80 Mobila och sociala applikationer
def test_mark_and_retrieve_message(self):payload = {'message': 'hej3'}rv = self.app.post('/messages', data=json.dumps(payload),
content_type='application/json')message_id1 = rv.dataassert rv.status_code == 200assert len(message_id1) == 8…rv = self.app.post('/messages/' + message_id1 + '/flag/' + user_name1)assert ……rv = self.app.get('/messages/unread/' + user_name2)assert b'hej3' in rv.dataassert b'hejhej3' in rv.data
Sätta upp/ta ner tmp-databas
18-01-20TDDD80 Mobila och sociala applikationer
def setUp(self):self.db_fd, app.config['DATABASE'] = tempfile.mkstemp()app.config['TESTING'] = Trueself.app = app.test_client()with app.app_context():
data.initialize_db()
def tearDown(self):os.close(self.db_fd)os.unlink(app.config['DATABASE'])
Anrop till egendatabas-
hanteringsmodul
Modulär kod
18-01-20TDDD80 Mobila och sociala applikationer
• Bra om kod kan återanvändas– T.ex. db-anrop direkt från unit_test
• Bra om db och views kan bytas oberoende avvarandra, dvs. “plug-n-play”– Lägg @app.route – metoderna i server.py eller
views.py– Lägg databas-hantering i egen modul (egen mapp
eller fil)
Cirkulära referenser
18-01-22TDDD80 Mobila och sociala applikationer
• Problem att vyn är beroende av data och vice versa (för initiering av db)
• Läs mer på:– http://flask.pocoo.org/docs/0.12/patterns/packa
ges/
Modulär kodstruktur
18-01-22TDDD80 Mobila och sociala applikationer
• app.py
from lab2 import app
if __name__=='__main__':app.run(host='0.0.0.0’,
port=8080)
Modulär kodstruktur
18-01-22TDDD80 Mobila och sociala applikationer
• __init__.py
from lab2 import config
app = Flask(__name__)
app.config['DEBUG'] = config.debug_flag
app.config['SQLAL…'] = config.db_uri
import lab2.server
Modulär kodstruktur
18-01-22TDDD80 Mobila och sociala applikationer
• server.py
from lab2 import appfrom lab2 import data
data.initialize_db()
Modulär kodstruktur
18-01-22TDDD80 Mobila och sociala applikationer
• data.py
from lab2 import appdb = SQLAlchemy(app)…