Aplikacje webowe w Pythonie dla programistów PHP pt.2
Witam w drugiej części tutoriala. Tematem na dziś będzie łączenie się z bazą MySql i wykonywanie prostych zapytań typu insert czy select.
Pierwszą rzeczą jaką trzeba wykonać by móc podłączyć się do bazy MySql jest ściągnięcie sterownika. Paczki dla *nixów są na stronie projektu.Dostępne są też instalki dla Windowsa.
Po zainstalowaniu drivera tworzymy dwa pliki. Pierwszy będzie się nazywał common_db
, będziemy go includować za każdym razem gdy będziemy coś robić z bazą, w pliku tym jest funkcja connect, która utworzy nam połączenie z odpowiednimi parametrami:
import MySQLdb
def connect():
oDb = MySQLdb.connect (host="localhost", user="root", db="test")
return oDb
Dla porównania plik php-owy, który robi dokładnie to samo:
$oDb = new PDO('mysql:dbname=test;host=127.0.0.1', 'root');
Natomiast drugi plik nazywa się test_insert.py
, w którym mamy insertowanie danych do bazy:
import common_db
oDb = common_db.connect()
content = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.'
def test(path):
global oDb
global content
oDb.begin()
cursor = oDb.cursor()
result = ''
for i in range(0,1000):
cursor.execute ("INSERT INTO test1(content) values ('"+content+"')")
result += "inserted row number"+str(i)+"
"
oDb.commit()
cursor.close()
oDb.close ()
return result
Ekwiwalent php:
require 'lib/common_db.php';
$sContent = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. ';
set_time_limit(0);
for ($i=0; $i < 1000; $i++){
$sQuery = "INSERT INTO test1(content) values ('$sContent')";
$oDb->exec($sQuery);
echo "inserted row number $i
";
}
Są to skrypty do testów obciążeniowych więc nie dziwcie się, że zapytania tam idą w pętli, oczywiście globale też są złe ;). Driver Pythonowy nie ma defaultowo ustawionego auto-commita więc wszystkie zapytania typu insert, update, delete trzeba przeprowadzać w transakcji.
Pobieranie z bazy – test_select.py
:
import common_db
import random
oDb = common_db.connect()
def getRandomId(table):
global oDb
cursor = oDb.cursor()
sQuery = 'select max(id) as max, min(id) as min from '+table;
cursor.execute(sQuery)
iMax, iMin = cursor.fetchone()
return random.randint(int(iMin),int(iMax))
def test(path):
global oDb
cursor = oDb.cursor()
cursor.execute('select * from test1 where id = %s',getRandomId('test1'))
return cursor.fetchall()
Ekwiwalent php-owy
require 'lib/common_db.php';
function getRandomId($table) {
global $oDb;
$sCountStatement = 'select max(id) as max, min(id) as min from '.$table;
$oCountStatement = $oDb->query($sCountStatement);
list( $iMin,$iMax ) = $oCountStatement->fetch(PDO::FETCH_NUM);
return rand($iMin,$iMax);
}
$sStatement = 'select * from test1 where id = ?';
$oQuery = $oDb->prepare( $sStatement );
$iId = getRandomId('test1');
$oQuery->execute(array($iId));
$aResult = $oQuery->fetch(PDO::FETCH_ASSOC);
echo($aResult['content']);
Aby przetestować powyższy kod, należy utworzyć w katalogu z projektem z pierwszej części tutorialu ww. pliki, uruchomienie jak poprzednio – http://localhost/py/nazwa_pliku_z_przykladem.py.
Jeszcze na koniec taka mała dygresja, w pierwszej części opisałem jak obsługiwać błędy, niestety informacje, które dostawaliśmy z wyjątku były dość skąpe i rzadko pozwalały zidentyfikować problem.
Pogrzebałem trochę w internecie i znalazłem rozwiązanie. Dzięki niemu, dostaniemy na ekran taki stack trace jaki zwykle dostajemy w PHP przy wypisywaniu wyjątku. Kod poniżej
import imp
from string import join
import os
import os.path
import sys
import traceback
def application(environ, start_response):
try:
# tutaj stary kod
except Exception as e:
# funkcja pobiera informacje o ostatnim wyjatku i zwraca w formie listy
exc_type, exc_value, exc_traceback = sys.exc_info()
# funkcja formatuje informacje o wyjatku i zwraca je w postaci czytelnego trace'a
ex = traceback.format_exception(exc_type, exc_value, exc_traceback)
output = '\n' +join(ex, '\n')
status = '200 OK'
response_headers = [('Content-type', 'text/plain'),
('Content-Length', str(len(output)))]
start_response(status, response_headers)
return [output]
Ok, fajnie, pokazałeś że w pythonie da się osiągnąć to co w php.
Tylko teraz pytanie co było szybsze ?
No to jest z definicji tutorial i pokazuje on jak coś zrobić w Pythonie. Co do szybkości to czy mógłbyś sprecyzować czy chodzi Ci o szybkość wykonania skryptu, czy o szybkość stworzenia kodu ?
Witaj,
chodziło mi o to który kod szybciej się wykona w przeglądarce.
Mierzyłem ilość requestów/sekunde apache benchmarkiem i wychodziło, że przy SELECT szybszy jest PHP. Natomiast w pewnych warunkach Pythonowy skrypt się wysypywał i dlatego też jak usunę ten błąd to zrobię ponowne pomiary i dopiero wtedy będzie można coś wyrokować…
Wg. mnie to porównanie jest na złym poziomie abstrakcji, aplikacje w Pythonie (prawie) zawsze powstają w oparciu o framework, więc osoby piszące je użyją wbudowanego ORMa, raportowania błędów do logów, zamiast pisać żywcem zapytania, swoją obsługę wyświetlania błędów. Podobnie będzie z innymi rzeczami (np. nacjonalizacja, autoryzacja).
Dla mnie największą różnicą między PHP a Pythonem czy Ruby jest podejście do pisania rzeczy webowych w gołym języku.
tll;dr proponuję porównać, jak problemy z notki rozwiązuje się korzystając z frameworków.
To jest tutorial a nie porównanie ! Proszę, czytajcie ze zrozumieniem, jest to tutorial dla programistów php, którzy chcą zaznajomić się z Pythonem. Dlatego są fragmenty kodu w obu językach, żeby łatwiej było zrozumieć kod Pythonowy. Dlatego również nie stosuje tutaj frameworków tylko proste skrypty, takie jakie pewnie pisał każdy początkujący php-owiec.
I to jest dobre podejscie : ) dobrze robisz ze nie uzywasz odrazu frameworkow : )
Ja myśle, że to po prostu zdrowe podejście – nie wsiadaj do bolidu f1 póki nie umiesz jeździć maluchem 😉