Szybki start z Rails, Merb i Pylons

Opublikowane przez Jarosław Zabiełło Sun, 16 Dec 2007 06:31:00 GMT

Celem tego tekstu jest porównanie podstawowych czynności niezbędnych do tego aby uruchomić Rails, Merb oraz Pylons. Zakładam że używany będzie

Wpierw musimy (jeśli jeszcze nie mamy) poinstalować stosowne biblioteki. W wypadku Rails i Merba musimy mieć Rubiego i RubyGems oraz doinstalowane gemy:

gem install -y rails haml merb sequel merb_sequel

W wypadku Pylons musimy mieć zainstalowanego Pythona oraz pakiet setuptools dający możliwość używania skryptu easy_install (taki odpowiednik RubyGems dla Pythona)

easy_install Pylons SQLAlchemy Mako

Tworzenie projektu:

Rails

rails rails_app

Merb

merb merb_app

Pylons

paster create -t pylons pylons_app

Podpięcie do bazy

Rails

Należy w pliku config/database.yml wpisać:

shared: &shared  
  adapter: mysql
  encoding: utf8
  username: login
  socket: /opt/local/var/run/mysql5/mysqld.sock
  password: haslo
development:
  database: rails_app
  <<: *shared
test:
  database: test
  <<: *shared
production:
  database: rails_app
  <<: *shared

Merb

Tu jest tak samo jak w Rails z tą różnicą, że nie ma tego pliku. Należy stworzyć Plik config/database.yml:
---
  shared: &shared  
    :adapter: mysql
    :encoding: utf8
    :username: login
    :socket: /opt/local/var/run/mysql5/mysqld.sock
    :password: haslo
  development:
    :database: merb_app
    <<: *shared
  test:
    :database: test
    <<: *shared
  production:
    :database: merb_app
    <<: *shared

Pylons

Tu, niestety, jest większa zabawa, bo zarówno Pylons jak i SQLAlchemy są bardziej złożone.

W pliku development.ini wstawiamy:

sqlalchemy.url = mysql://user:[email protected]:3306/pylons_app
sqlalchemy.echo = True
sqlalchemy.pool_recycle = 3600  

Warto zwrócić uwagę, że parametr ‘pool_recycle’ zabezpiecza nas przed pewnym problemem w MySQL. Otóż po paru godzinach nieaktywności, potrafi zrywać zestawione wcześniej połączenie. SQLAlchemy zabezpiecza się przed tym stukając do bazy co godzinę. Active Record nie ma przed tym standardowo wbudowanego zabezpieczenia (sic!). Sequel zaś (też – jak SQLAlchemy- pracujący wielowątkowo) wyłapuje zerwane połączenia, czyści je i zestawia z powrotem.

Musimy ustawić sesję dla SQLAlchemy. Działa i czyści się po każdym przeładowaniu przeglądarki internetowej. W pliku pylons_app/lib/base.py wpisujemy:

# reszta pliku...
import pylons_app.model as model

class BaseController(WSGIController):

    def __call__(self, environ, start_response):
        """Invoke the Controller"""
        conn = config['pylons.g'].sa_engine.connect()
        model.Session(bind=conn)
        try:
            return WSGIController.__call__(self, environ, start_response)
        finally:
            model.Session.remove()
            conn.close()
# reszta pliku...

Tworzenie modeli

Rails

W Rails korzystamy z wbudowanych generatorów kodu:

$ script/generate model User
-> app/models/user.rb
...
-> db/migrate/001_create_users.rb

$ script/generate model Post
-> app/models/post.rb
...
-> db/migrate/002_create_posts.rb

Razem z modelami domyślnie są tworzone pliki z migracjami. Wpierw jednak zdefiniujmy relacje między modelami:

Plik app/models/post.rb:

class Post < ActiveRecord::Base
  belongs_to :user
  # Albo to samo za pomocą tych metod:
  # def user
  #   User.find :first,:conditions => ["id=?", id]
  # end
  # def user=(item)  
  #   self.user_id = item.id
  # end
end

Plik app/models/user.rb:

class User < ActiveRecord::Base
  has_many :posts
  # Albo to samo bardziej bezpośrednio:
  # def posts
  #  Post.find :all, :conditions => ["user_id=?", id]
  # end
end

Zdefiniujemy nie tylko struktury tabel ale też wypełnimy je przykładowymi danymi.

Plik db/migrate/001_create_users.rb:

class CreateUsers < ActiveRecord::Migration
  def self.up
    create_table :users, :force => true do |t|
      t.string :name, :null => false
    end
    add_index :users, :name, :unique => true
    User.create! :name => 'Jarek'
  end
  def self.down
    drop_table :users
  end
end

Plik db/migrate/001_create_posts.rb:

class CreatePosts < ActiveRecord::Migration
  def self.up
    create_table :posts, :force => true do |t|
      t.integer :user_id, :null => false
      t.string :title, :null => false
      t.text :body
      t.timestamps
    end
    add_index :posts, :title, :unique => true
    post = Post.new
    post.title = 'Tytuł artykułu'
    post.body = 'Treść artykułu'
    post.user = User.find_by_name('Jarek')
    post.save!    
  end
  def self.down
    drop_table :posts
  end
end

Uruchamiamy migracje:

rake db:migrate

Sprawdźmy interaktywnie (w konsoli, czyli po odpaleniu ruby script/console) czy modele działają prawidłowo.

User.find :all
# => [#<User id: 1, name: "Jarek">]
User.find(:first).posts
# => [#<Post id: 1, user_id: 1, title: "Tytuł artykułu", body: "Treść artykułu", created_at: "2007-12-15 18:16:27", updated_at: "2007-12-15 18:16:27">]
User.find(:first).posts[0].title
# => "Tytuł artykułu"

Merb

Tu wpierw trzeba wybrać jaki ORM chcemy używać (domyślnie Merb nie ma właczonej obsługi żadnego). Operacja jest bardzo prosta. W pliku config/dependencies.rb odkomentujemy linijkę tak aby było:

use_orm :sequel

Modele generujemy identycznymi generatorami jak w Rails.

$ script/generate model User
-> app/models/user.rb
...
-> schema/migrations/001_add_model_users.rb

$ script/generate model Post
-> app/models/post.rb
...
-> schema/migrations/002_add_model_posts.rb

Definicja modeli.

class Post < Sequel::Model(:posts)
  one_to_one :user, :from => :Post
  # To samo co:
  # def user
  #   User.filter(:id=> user_id).first
  # end  
  # def user=(obj)
  #   self.user_id = obj.id
  # end  
  #
  # Merb nie ma automagicznego wypełniania treścią
  # pól o nazwach created_at czy updated_at
  # Ale posiada wywołania zwrotne (też jak Rails)
  after_create do
    set(:created_at => Time.now)
    set(:updated_at => Time.now)
  end
  after_update do
    set(:updated_at => Time.now)
  end
end

class User < Sequel::Model(:users)
  one_to_many :posts, :from => :posts, :key => :user_id
  # To samo co:
  # def posts
  #   Post.filter :user_id => pk
  # end
end

W przeciwieństwie do Active Record, w Sequelu można zdefiniować (metodą set_schema) strukturę tabel w modelu (wtedy nie trzeba np. zaglądać do migracji ani bazy, aby sobie przypomnieć strukturę tabeli). Ale aby oszczędzić sobie pisania, zdałem się tu na automatyczne rozpoznanie struktury tabel przez Rubiego.

Teraz pora na migracje. Sequel też je posiada, choć różnią się troszkę składnią do tych w Active Record.

Plik schema/migrations/001_add_mode_users.rb:

class AddModelUsers < Sequel::Migration
  def up
    create_table! :users do
      primary_key :id
      varchar :name, :size => 255, :unique => true      
    end  
    User.create :name => 'Jarek'
  end
  def down
    execute 'DROP TABLE users'
  end
end

Plik schema/migrations/002_add_mode_posts.rb:

class AddModelPosts < Sequel::Migration
  def up
    create_table! :posts do      
      primary_key :id 
      integer :user_id, :null => false
      varchar :title, :null => false, :unique => true
      text :body
      datetime :created_at 
      datetime :updated_at 
    end    
    post = Post.new
    post.title = 'Tytuł artykułu'
    post.body = 'Treść artykułu'
    post.user = User.filter(:name=>'Jarek').first
    post.save
  end
  def down
    execute 'DROP TABLE posts'
  end
end

Uruchomienie migracji działa w Sequelu tak samo jak w Active Record (nawet parametr VERSION też tu działa).

rake sequel:db:migrate

No to pora na test z interaktywnej konsoli. Odpala się ją przez:

merb -i
User.all
# => [#<User:0x19477e8 @changed_columns=[], @values={:name=>"Jarek", :id=>1}, newfalse]
User.first.posts.class 
# => Sequel::MySQL::Dataset
# User.first.posts.sql
# => "SELECT * FROM posts WHERE (`user_id` = 1)"        
User.first.posts.first.title
# => "Tytu\305\202 artyku\305\202u"      

Sequel nie tylko jest szybszy i używa iteratorów (oszczędzająć przez to pamięć). Jest też wielowątkowy (używa puli wątków) oraz pozwala na krokowy podgląd kodu SQL przed jakimkolwiek przeslaniem kwerendy do bazy danych. Sequel również nie zwraca liste obiektow ORM z bazy (jak Active Record) ale jeden, specjalny obiekt proxy. Pozwala to na interaktywne budowanie kodu w stopniu o wiele lepszym niż pozwala na to Active Record (czy nawet DataMapper). Sequel ma również wbudowany cache modeli współpracujący z szybkim serwerem memcached.

Pylons

Poniższa konfiguracja jest inspirowana tekstem SQLAlchemy 0.4 for people in a hurry. Pylons nie wymaga aby każdy model był w oddzielnym pliku, zatem użyjemy jednego. W pliku pylons_app/model/init.py wpisujemy:

# -*- coding: utf8 -*-
from datetime import datetime

# Zgodnie z filozofią Pythona, importujemy tylko tylko interesujące nas symbole
# aby nie zaśmiecać sobie przestrzeni nazw:

from pylons import config
from sqlalchemy import ForeignKey, Column, MetaData, Table, types
from sqlalchemy.orm import mapper, relation
from sqlalchemy.orm import scoped_session, sessionmaker

Session = scoped_session(sessionmaker(autoflush=True, transactional=True, bind=config['pylons.g'].sa_engine))
metadata = MetaData()

# Tworzymy definicje tabel:

table_users = Table('users', metadata,
  Column('id', types.Integer, primary_key=True),
  Column('name', types.String(255))
  )
table_posts = Table('posts', metadata,
  Column('id', types.Integer, primary_key=True),
  Column('user_id', types.Integer, ForeignKey('users.id')),
  Column('title', types.Unicode(255)),
  Column('body', types.Unicode),
  Column('created_at', types.DateTime, default=datetime.now),
  Column('updated_at', types.DateTime, default=datetime.now)
  )
# Zamiast tego można automatycznie wciągnąć istniejącą strukturę:
#
# table_users = Table('users', metadata, autoload=True)
# table_posts = Table('posts', metadata, autoload=True)

# Tworzymy definicje modeli.

class User(object):
    pass
class Post(object):
    pass

# Mapowanie ORM do klas SQLAlchemy i ustawienie
# relacji jeden-do-wielu
mapper(User, table_users, properties={'posts':relation(Post, backref='user')})
mapper(Post, table_posts)

Wygląda to bardziej skomplikowanie, ale SQLAlchemy ma też większe możliwości. Zasadniczą różnicą w SQLAlchemy jest rozdzielenie obiektów ORM od obiektów modelu biznesowego. Obiekty ORM zajmują się mapowaniem struktur tabel do obiektów Pythona. Są one następnie łączone z klasami SQLAlchemy. Takie rozdzielenie daje dużo większe możliwości, bo można w modelu biznesowym nawet zamapować kilka tabel do jednego modelu. SQLAlchemy ma dużo innych możliwości, o których tu nie będę teraz pisać. W każdym razie odbija się to na złożoności (ale i tak to jest prościej niż np. w javowym Hibernate).

Co z migracjami? Nie ma ich. Trzeba by było je napisać (to w sumie nie jest trudna sprawa). Stworzmy jednak strukture tabel wraz z przykładowymi danymi za pomocą SQLAlchemy. W tym celu końcówka pliku pylons_app/websetup.py powinna wyglądać następująco:

# początek pliku…
def setup_config(command, filename, section, vars):
    """Place any commands to setup pylons_app here"""
    conf = appconfig('config:' + filename)
    load_environment(conf.global_conf, conf.local_conf)

    from pylons_app.model import metadata, Session, User, Post

    log.info("Creating tables")
    metadata.create_all(bind=config['pylons.g'].sa_engine)
    log.info("Successfully setup")

    log.info("Adding some data to table 'users'")
    user = User()
    user.name = 'Jarek'
    Session.save(user)
    Session.commit()

    log.info("Adding some data to table 'posts'")
    user_query = Session.query(User)
    user_id = user_query.filter(User.name == 'Jarek').one().id
    post = Post()
    post.title = 'Tytuł artykułu'
    post.body = 'Treść artykułu'
    post.user_id = user_id
    Session.save(post)
    Session.commit()    
    log.info("Data added.")

W powyższym przykładzie widać też próbkę sposobu w jaki używa się SQLAlchemy. Każda operacja jest zamykana w ramach specjalnej sesji a sesja jest tworzona niezależnie dla każdego wątku. W SQLAlchemy praktycznie w ogóle nie ma potrzeby sięgania do kodu SQL tak jak to nagminnie widać w Active Record. Choć muszę też przyznać, że Sequel także ładnie daje sobie radę ze skomplikowanymi konstrukcjami bez konieczności uciekania się do surowego kodu SQL.

Uruchamiamy tworzenie tabel za pomocą:

paster setup-app development.ini

Interaktywny shell uruchamia się przez

paster shell

Trzeba pamiętać, że fakt uruchomienia konsoli nie oznacza, że mamy automatycznie powciągane moduły wszystkich modeli. W Pythonie nie ma magii, wszystko należy jawnie zaimportować. Także i nasz projekt.

from pylons_app import *
print model.User
# => <class 'pylons_app.model.User'>

Tworzenie kontrolera i prostego widoku.

Rails

script/generate controller home
# => app/controllers/home_controller.rb

Plik kontrolera (app/controller/home_controller.rb):

class HomeController < ApplicationController
  def index
    @user = User.find :first
  end
end

Aby użyć Haml zamiast ERb musimy w katalogu projektu odpalić komendę (nie zapomnij o kropce, która oznacza tu bieżący katalog):

haml --rails .

Plik widok (app/views/home/index.html.haml):

Użytkownik
= @user.name
posiada post(y)
%ul
  - @user.posts.each do |post|
    %li= post.title

Serwer uruchamiamy klasycznie

script/server

Merb

script/generate controller home
# => app/controllers/home.rb
Plik kontrolera (app/controllers/home.r):
class Home < Application
  def index
    @user = User.first
    render
  end
end

Aby uaktywnić Haml należy w pliku config/merb_init.rb dopisać na końcu

Merb::Template::Haml

Warto też doinstalować gemy:

gem install -y merb_helpers merb_has_rails_plugins ruby-debug

i dodać je do config/dependencies.rb

dependency 'merb_helpers'
dependency 'merb_has_rails_plugins'
dependency 'ruby-debug'

Dzięki temu będziemy mieli dostępnych trochę dodatkowych helperów z Rails. Zaś gem ruby-debug umożliwia nam wpisanie w dowolnym miejscu komendy debugger przerywającej pracę przeglądarki i kierującej wykonanie kodu do konsoli. Po jej zakończeniu przeglądarka wznawia obsługę requestu. Działa to tu tak jak w Rails. Można interaktywnie sobie operować na żywym kodzie. Warto też wiedzieć, że w Merb wszystkie pluginy są po prostu gemami. Ułatwia to zarządzanie ich wersjami.

Plik widoku jest identyczny jak w wyżej dla Railsow więc nie będę podawał jeszcze raz jego treści.

Odpalamy serwer za pomocą komendy merb i na stronie http://localhost:4000/home podziwiamy wynik. :) Komenda merb—help wyświetli wszystkie, dostępne opcje.

Pylons

paster controller home
# => pylons_app/controllers/home.py

Plik kontrolera (pylons_app/controllers/home.py):

import logging
from pylons_app.lib.base import *
log = logging.getLogger(__name__)
from pylons_app.model import Session, User

class HomeController(BaseController):
    def index(self):
        user_query = Session.query(User)
        c.user = user_query.filter(User.name == 'Jarek').one()
        return render('/home/index.mako')

Plik widoku (pylons_app/templates/home/index.mako):

Użytkownik ${c.user.name} posiada post(y)
<ul>
  % for post in c.user.posts:
    <li>${post.title}</li>
  % endfor
</ul>

Odpalamy serwer za pomocą

paster serve development.ini --reload

Pylons nie posiada trybów pracy tak jak Rails czy Merb. Wielowątkowy serwer HTTP (paster) musi dostać odpowiedni plik konfiguracyjny. Daje to w sumie więcej możliwości, bo można izolować sobie różne konfiguracje dużo bardziej niż prosty podział na tryb produkcyjny i rozwojowy.

Podsumowanie.

Rails jest bardzo bogato wyposażony w różne helpery, posiada dobrą literaturę, książki itd. Jest też bardzo łatwy do nauki i dosyć intuicyjny. Jeśli chodzi o prototypowanie, to jest prawie nie do pobicia. Niestety, można się przyczepić do jakości implementacji wewnętrznej jak i do niektórych błędów projektowych w Active Record. Myślę, że Railsom wyrósł bardzzo groźny konkurent w postaci Merba. Jest lepiej zorganizowany, spójniejszy i szybszy. Jest też tak samo prosty i intuicyjny. Czy warto się przesiadać? Wg mnie – warto. Jeśli zaś chodzi o ORM, to Sequel jest pod wieloma względami lepszym projektem od Active Record. Tu może tego nie eksponowałem, ale Sequel pozwala na obiektowe budowanie znacznie bardziej skomplikowanych zapytań niż to potrafi Active Record czy DataMapper. Po przykłady kodu Sequela odsyłam do jego strony domowej.

Co z Pylons? Korzysta z potężnego ORM’a i potężnych szablonów. Ma pełną obsługę Unicode, szablony Mako natywnie pracują na obiektach unikodowych (nie mylić ze stringami utf8). Mako mają dziedziczenie, potężny, elastyczny cache i są bardzo “pythonic” w swym podejściu do budowania modułów czy custom tagów. Niestety Pylons w całości wymaga trochę więcej wiedzy i umiejętności aby go ustawić do pracy. Rails i Merb są tu znacznie prostsze. Dzięki zaś Merb przepaść między wydajnością i możliwościami pythonowego Pylonso a frameworkami Rubiego mocno się zmniejszyla. Ci, co czepiali się wcześniej wydajności Rails nie mają teraz specjalnie na co narzekać. Ci co narzekali na ograniczenia Active Record, nie mają wymówki w zetknięciu z Sequelem. No chyba, że ktoś nie chce się uczyć Rubiego, jego sprawa. Ja jednak uważam, że Ruby to piękny język i bardzo przyjemnie się w nim pisze. A prostota Merba w porównaniu z Pylons jest (szczególnie dla osób zaczynających pracę) – powalająca.

Celem tego tekstu było pomóc osobom w początkowej fazie konfiguracji Pylons oraz Merb. Mam nadzieję, że był pomocny i dalej już każdy sobie sam da radę. Jeśli chodzi o dodatkową pomoc, to warto zajrzeć na IRC. Na serwerze freeenode istnieją kanały: #pylons, #merb, #sequel, #datamapper, #rubyonrails oraz nasze polskie #rubyonrails.pl i #python-pl. Są też polskie grupy dyskusyjne w Usenecie: news://pl.comp.lang.python i news://pl.comp.lang.ruby.


Kod źródłowy z powyższymi przykładami można pobrać stąd

Tagi , , , , , , , ,  | 15 comments

Comments

  1. Avatar comboy powiedział about 4 hours later:

    Bardzo fajny artykuł, dzięki :)

  2. Avatar fan powiedział about 5 hours later:

    Jarku artykuł jak zawsze full pro, mógł byś poruszyć jeszcze w którymś z najbliższych artykułów jak użyć w merbie choćby plugina restful_authentication? Myśle, że jeden konkretny artykulik rozwiał by pytanie czy truudno przemigrować swój projekt na merba, a wyraźnie widać , że w sieci o nim coraz głośniej.

  3. Avatar Tomasz Nazar powiedział about 7 hours later:

    Co do SQLAlchemy, to jest potezne. W porownaniu z Hibernate, mam mieszane uczucia. Hibernate, ma mnostwo dokumentacji i jest dobrze zorganizowane (no i ma 2nd level cache za darmo). Natomiast w SQLAlchemy, to czesto jeszcze sie gubie.

    Uwaga co do Mako vs. Utf8 zbedna. Wyjscie mozna ustawic na utf8.

    PS. A moze wiesz jak rozdzielic klasy modelu w Pylons na osobne pliki. Juz mialem kilka podejsc i ciagle koncze na bledzie rekursywnego importu :(

  4. Avatar Jarosław Zabiełło powiedział about 8 hours later:

    Z tym Mako to chciałem podkreślić że pracuje na natywnych obiektach unikodowych Pythona, a nie na stringach takich czy siakich (choćby i utf8). Z tym rodzieleniem modelu i błędem importu to nie za bardzo wiem w czym problem.

    BTW, wrzuciłem na serwer pliki źródłowe z opisywanymi przykładami.

  5. Avatar climbus powiedział about 12 hours later:

    from pylons_app.lib.base import *

    Czy model i Session nie jest wciągany poprzez lib.base? Przynajmniej do 0.9.5 tak było.

    Shell ma defaultowo zaimportowane śrdowisko z lib base + kilka innych zmiennych jak mapper, app.

  6. Avatar climbus powiedział about 12 hours later:

    Co do rekursywnego importu to jest z tym problem ale głownie wychodzi przy skomplikowanych mapowaniach.

    Obecnie robię dość zakręcone mapowanie (już jest 55 tabel) i się naciąłem na problemy z importami. Ogólnie chodzi o wzajemne powiązania. Np użytkownik posiada artykuly, artykuly posiadają komentarze, komentarze należą do użytkownika. U uzytkownicy.py trzeba wciągnąć artykuly.py, w arykuly.py wciągane są komentarze.py, komentarze.py muszą wciagnąć uzytkownicy.py, ale jesteśmy w trakcie budowy modułu użytkownicy.py więc nie może jeszcze jego wciągnąć.

    Nie wiem czy dobrze to opisałem.

    Oczywiście można to rozwiązać łącząc komenetarze.py z uzytkownicy.py ale jeżeli struktura jest skomplikowana i opis użytkownika składa się np. z 10 tabel a komentarze chce się mieć odseparowane i przenośne to już nie jest halo.

    Nie miałem ostatnio czasu się zastanowić nad tym problemem ale zapewne do nie go wrócę.

  7. Avatar Jarosław Zabiełło powiedział about 13 hours later:

    @climbus: nie widzę w konsoli Pylons modeli bez jawnego ich zaimportowania. Co do tych problemów z relacjami, to miałem kiedyś podobny problem w Django ORM. ORM’y Rubiego ładnie to obchodzą. Podczas tworzenia relacji przekazywany jest symbol lub string z nazwą tabeli a nie jej obiekt. Wtedy kolejność ładowania nie ma znaczenia bo i tak ten kod jest odpalany z opóźnieniem.

  8. Avatar Jarosław Zabiełło powiedział about 18 hours later:

    Poprawiłem przykłady i kod do pobrania. W Sequel 0.4.3 poprawili sposób dodawania rekordów w relacji one_to_one. Nie przekazuje się już id, ale cały obiekt Sequel::Dataset, gdzie jego id jest wyłuskiwany.

  9. Avatar Dentharg powiedział 1 day later:

    Ja to bym jeszcze chętnie widział, jak przykładnie te appy odpalić przy pomocy FCGI

  10. Avatar pnowak powiedział 1 day later:

    Dziś wychodzi Sequel 0.4.4, po zmianach o które prosiłem wczoraj. Dojdą nowe metody do migracji (add, rename, drop column), ,tego brakowalo. na ta chwile jeszcze sa bledy z mysql, mam nadzieje ze rozwiazo je szybko, jakis gosc juz nad tym siedzi, pozdr.

  11. Avatar pnowak powiedział 1 day later:

    NO i niestety okazało się, że migracja w sequel rename_column nie działa transparentnie dla wszystkich baz, i poki co w migracji rename_column trzeba tez podac jej typ jesli uzywamy mysql.. coz taka specyfika mysql. mozna to zrobic przez dodatkowe zapytanie, ale team sequel mowi ze najpierw musza zrobic porzadne narzedzie do introspekcji tabel.

  12. Avatar Michal powiedział 22 days later:

    Czy WSGIController.call(self, environ, start_response) nie mozna by zapisac prosciej: self(environ, start_response)?

  13. Avatar Jeffar powiedział about 1 month later:

    Próbowałem wykonać przykład dla Pylonsów, ale okazało się, że jednego z elementów brakuje. Musiałem jeszcze edytować plik config/environment.py, dodać w nim import sqlalchemy as sa oraz config[‘pylons.g’].sa_engine = sa.engine_from_config(config, ‘sqlalchemy.’). Dopiero to pozwoliło przejść przez “paster setup-app development.ini”.

  14. Avatar g00fy- powiedział 9 months later:

    @Jarosław Zabiełło w SQLAlchemy tez mozna przekazywac nazwe tabeli jako relacje. dostepne sa 3 mozliwosci:

    Obiekt (jesli istnieje, i istnieje FK) “Obiekt” (sqla samo znajdzie pole po FK) Obiekt.id (podajemy po jakim polu ma joinowac) Obiekt,obiekt_tabela.c.id “tabela_w_bazie.pole”

    mowie oczywiscie o sqlalchemy 0.5 http://www.sqlalchemy.org/docs/05/documentation.html#docstrings_sqlalchemy.orm_modfunc_relation

  15. Avatar Zagal powiedział over 3 years later:

    Świetny artykuł. Dopiero zaczynam swoją przygodę z Pylonsem i miałem kilka problemów z mapowaniem ORM ( chodzi mi o relacje ) i tu znalazłem rozwiązanie. Dzięki :P

    Póki co pylons z z SQLAlchemy i mako jest cudowny :)

(leave url/email »)

   Pomoc języka formatowania Obejrzyj komentarz