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]
  1. Spawnm pisze:

    Ok, fajnie, pokazałeś że w pythonie da się osiągnąć to co w php.
    Tylko teraz pytanie co było szybsze ?

  2. Wojciech Soczyński pisze:

    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 ?

  3. Spawnm pisze:

    Witaj,
    chodziło mi o to który kod szybciej się wykona w przeglądarce.

  4. Wojciech Soczyński pisze:

    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ć…

  5. RobertG pisze:

    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.

  6. Wojciech Soczyński pisze:

    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.

  7. Paweł Krefta pisze:

    I to jest dobre podejscie : ) dobrze robisz ze nie uzywasz odrazu frameworkow : )

  8. Wojciech Soczyński pisze:

    Ja myśle, że to po prostu zdrowe podejście – nie wsiadaj do bolidu f1 póki nie umiesz jeździć maluchem 😉

  1. There are no trackbacks for this post yet.

Leave a Reply

Informuj mnie o odpowiedziach poprzez e-mail. Możesz również subskrybować wpis bez zostawiania komentarza.