Archivio

Archivio per la categoria ‘pylons’

Pylons: Fantamanager parte 4 – update giocatori

26 Ottobre 2011 Commenti chiusi

<part_3
Vorrei inserire nella pagina “Squadre”, la possibilità di fare l’update del database,
in modo da avere il giocatori e le squadre di appartenenza, sempre aggiornati.

Per prima cosa creo il controller che si occuperà di leggere il file di ingresso per
poi passarlo all’action che aggiornerà il database:

paster controller upload

Inserisco un form nella template ..FantaManagerfantamanagertemplatesderivedseriea.mako:

&lt;%inherit file=&quot;/base/base.mako&quot; /&gt;

&lt;%def name=&quot;title()&quot;&gt;Teams&lt;/%def&gt;
&lt;%def name=&quot;heading()&quot;&gt;&lt;/%def&gt;
&lt;%def name=&quot;head_tags()&quot;&gt;&lt;/%def&gt;

&lt;p&gt;Squadre Serie A&lt;/p&gt;

&lt;ul&gt;
% for squadra in c.squadre:
	&lt;li&gt;&lt;a href=&quot;/team/${squadra}&quot;&gt;${squadra}&lt;/a&gt;&lt;/li&gt;
% endfor
&lt;/ul&gt;

&lt;%def name=&quot;footer()&quot;&gt;
	&lt;h1&gt;Upgrade Players&lt;/h1&gt;

	${h.form(h.url(controller='upload', action='upload'), multipart=True)}
			MCC file: ${h.file('mccfile')} &lt;br /&gt;
	${h.submit('submit', 'Upgrade')}
	${h.end_form()}

&lt;/%def&gt;

la parte che mi interessa è:

${h.form(h.url(controller='upload', action='upload'), multipart=True)}
			MCC file: ${h.file('mccfile')} &lt;br /&gt;
	${h.submit('submit', 'Upgrade')}
	${h.end_form()}

e sopratutto il context_object h.file(‘mccfile’), che richiamerò nel controller al
momento del “submit”.
Utilizzando parecchi helpers (h.) all’interno del template, è necessario fare le
dovute importazioni, nel modulo FantaManagerfantamanagerlibhelpers:

from webhelpers.html.tags import file

from webhelpers.html.tags import stylesheet_link
from webhelpers.html.tags import link_to
from webhelpers.html import escape, HTML, literal, url_escape
from webhelpers.html.tags import *
from pylons import url

Ora occupiamoci del controller FantaManagerfantamanagercontrollersupload.py

import logging

import fantamanager.lib.helpers as h


from pylons import request, response, session, tmpl_context as c, url
from pylons.controllers.util import abort, redirect
from fantamanager.model.models import Giocatore
from fantamanager.lib.base import BaseController, render
from fantamanager.extra.FileBrowser import FileBrowser
import fantamanager.model.meta as meta

from repoze.what.predicates import not_anonymous, has_permission
from repoze.what.plugins.pylonshq import ActionProtector


log = logging.getLogger(__name__)

class UploadController(BaseController):

    @ActionProtector(has_permission('admin'))
    def upload(self):
        def get_flag_role(idg):
            &quot;&quot;&quot;get the role, by idgaz&quot;&quot;&quot;
            if idg &lt; 200:
                role = 'portiere'
            elif idg &gt; 200 and idg &lt; 500:
                role = 'difensore'
            elif idg &gt; 500 and idg &lt; 800:
                role = 'ccampista'
            else:
                role = 'attaccante'
            return role
        mccfile = request.POST['mccfile']
        file_in = mccfile.file
        if file_in != None:
            file_in.seek(0)
            for line in file_in.readlines():
                val = line.split('|')
                try:
                    idr = int(val[0])
                except ValueError:
                    idr = int(val[0].replace('xefxbbxbf', ''))
                
                role = get_flag_role(idr)
                nomev = val[2].decode('utf-8').replace('&quot;', '')
                squadrav = val[3].replace('&quot;', '')
                play_in = meta.Session.query(Giocatore).filter(
                                                Giocatore.idgaz == idr).first()
                if play_in == None:
                    log.info(&quot; &lt;&lt;&lt;&lt; assente giocatore con idgaz = %s&quot; % idr)
                    valore = int(val[27].rstrip())
                    player = Giocatore()
                    player.idgaz = idr
                    player.nome = nomev
                    player.squadra = squadrav
                    player.valore = valore
                    player.ruolo = role
                    meta.Session.add(player)
                else:
                    log.info(&quot; ++++ aggiorno giocatore con idgaz = %s&quot; % idr)
                    player = meta.Session.query(Giocatore).filter(
                                                Giocatore.idgaz == idr).first()
                    player.idgaz = idr
                    player.nome = nomev
                    player.squadra = squadrav
                    player.valore = int(val[27])
                    player.ruolo = role
            meta.Session.commit()
            file_in.close()
            log.info(&quot;core&gt; dati inseriti con successo&quot;)
            session['flash'] = 'Players successfully updated.'
            session.save()
        else:
            log.info(&quot;core&gt; Nessun dato da salvare&quot;)
        redirect(url(controller = 'seriea', action = 'squadre'))

Il context_object (catturata dalla pagina con il codice della template) lo utilizziamo qui:

        mccfile = request.POST['mccfile']

poco prima del redirect, abbiamo salvato nella session (un dizionario), alla chiave ‘flash’, il messaggio
che desideriamo far apparire ad update avvenuto (come descritto nel pylons book).
Poi abbiamo salvato la session con il metodo save().

            session['flash'] = 'Players successfully updated.'
            session.save()

Perchè il flash-message funzioni, bisogna innanzitutto importare la session all’interno del controller:

from pylons import session

Poi editiamo la template base, dal quale ereditano tutte le altre template, e modifichiamo il codice:

## -*- coding: utf-8 -*-
&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.01//EN&quot;
&quot;http://www.w3.org/TR/html4/strict.dtd&quot;&gt;

&lt;html&gt;
	&lt;head&gt;
	  &lt;title&gt;${self.title()}&lt;/title&gt;
	  ${self.head()}
	&lt;/head&gt;
&lt;h1&gt;Fantacalcio Manager&lt;/h1&gt;
	&lt;body&gt;
		${self.header()}
		${self.tabs()}
		${self.menu()}
		${self.heading()}
		${self.breadcrumbs()}
		${self.flash()}
		${next.body()}
		${self.footer()}
	&lt;/body&gt;
&lt;/html&gt;

&lt;%def name=&quot;title()&quot;&gt;Fantacalcio Manager&lt;/%def&gt;
&lt;%def name=&quot;head()&quot;&gt;${h.stylesheet_link(h.url('/css/main.css'))}&lt;/%def&gt;

&lt;%def name=&quot;header()&quot;&gt;&lt;a name=&quot;top&quot;&gt;&lt;/a&gt;&lt;/%def&gt;
&lt;%def name=&quot;tabs()&quot;&gt;&lt;/%def&gt;
&lt;%def name=&quot;menu()&quot;&gt;&lt;/%def&gt;
&lt;%def name=&quot;heading()&quot;&gt;&lt;h1&gt;${c.heading or 'No Title'}&lt;/h1&gt;&lt;/%def&gt;
&lt;%def name=&quot;breadcrumbs()&quot;&gt;&lt;/%def&gt;
&lt;%def name=&quot;footer()&quot;&gt;&lt;p&gt;&lt;a href=&quot;#top&quot;&gt;Top ^&lt;/a&gt;&lt;/p&gt;&lt;/%def&gt;

&lt;%def name=&quot;flash()&quot;&gt;
	% if session.has_key('flash'):
	&lt;div id=&quot;flash&quot;&gt;&lt;p&gt;${session.get('flash')}&lt;/p&gt;&lt;/div&gt;
	&lt;%
	del session['flash']
	session.save()
	%&gt;
	% endif
&lt;/%def&gt;

definiamo la funzione flash all’interno della template

		${self.flash()}

, subito prima del body, con il seguente codice:

&lt;%def name=&quot;flash()&quot;&gt;
	% if session.has_key('flash'):
	&lt;div id=&quot;flash&quot;&gt;&lt;p&gt;${session.get('flash')}&lt;/p&gt;&lt;/div&gt;
	&lt;%
	del session['flash']
	session.save()
	%&gt;
	% endif
&lt;/%def&gt;

e inseriamo nel file FantaManagerfantamanagerpubliccssmain.css, lo
stile desiderato per l’evidenziazione del nostro flash-message:

#flash {
background: #ffc;
padding: 5px;
border: 1px dotted #000;
margin-bottom: 20px;
}
#flash p { margin: 0px; padding: 0px; }

Ora, quando la action upload (del controller upload), viene chiamata (premendo il pulsante upgrade),
dopo tutte le operazioni effettuate sul file, compreso l’aggiornamento del database,
il messaggio ‘Players successfully updated.’ viene salvato nella session e avviene il redirect alla
template squadre.mako

redirect(url(controller = 'seriea', action = 'squadre'))

Siccome la template squadre.mako eredita da base.mako, prima del viene eseguita la funzione flash.
Tale funzione flash, controlla che nella session sia presente la chiave flash (sì!), ne memorizza il
valore associato alla chiave (in nostro messaggio), cancella la chiave ‘flash’ e salva la
session (per pulirla).
Il messaggio viene quindi visualizzato dalla template figlia (squadre.mako).
Se effettuiamo inoltre un refresh della pagina, il flash-message sparisce, poichè la session
viene ripulita come ultima operazione.

Categorie:Fantacalcio, pylons, python Tag:

Pylons: Paginate parte 2

20 Ottobre 2011 Commenti chiusi

Paginate: AttributeError: ‘thread._local’ object has no attribute ‘mapper’

Nell’articolo precedente abbiamo sistemato il paginate.pager, ora
sistemiamo anche l’action del controller Page e la template, per allinearci
al capitolo 8 (SimpleSite) del Pylons Book.

...
from routes import Mapper
...

class PageController(BaseController):
    ...
    def list(self, id = None):
        mapper = Mapper()
        mapper.connect(':page')
        records = meta.Session.query(model.Page).all()
        if id is None:
            c.num = 1
        else:
            c.num = id
        c.paginator = paginate.Page(records, page = int(request.params.get('page', c.num)),
                                    items_per_page = 1,)
        return render('/derived/page/list.html')

il template list.html

&lt;%inherit file=&quot;/base/index.html&quot; /&gt;

&lt;%def name=&quot;heading()&quot;&gt;&lt;h1&gt;Page List&lt;/h1&gt;&lt;/%def&gt;

&lt;%def name=&quot;buildrow(page, odd=True)&quot;&gt;
	%if odd:
		&lt;tr class=&quot;odd&quot;&gt;
	%else:
		&lt;tr class=&quot;even&quot;&gt;
	% endif

	&lt;td valign=&quot;top&quot;&gt;
		${h.link_to(page.id,
				h.url(controller=u'page',
					action='view',
					id=unicode(page.id)))}
	&lt;/td&gt;

	&lt;td valign=&quot;top&quot;&gt;
		${page.title}
	&lt;/td&gt;
	&lt;td valign=&quot;top&quot;&gt;${page.posted.strftime('%c')}&lt;/td&gt;

	&lt;/tr&gt;
&lt;/%def&gt;

% if len(c.paginator):
	&lt;p&gt;Page : ${c.paginator.pager('$link_first $link_previous $first_item to $last_item of $item_count $link_next $link_last') }&lt;/p&gt;

	&lt;table class=&quot;paginator&quot;&gt;&lt;tr&gt;&lt;th&gt;Page ID&lt;/th&gt;&lt;th&gt;Page Title&lt;/th&gt;&lt;th&gt;Posted&lt;/th&gt;&lt;/tr&gt;
	&lt;% counter=0 %&gt;

	% for item in c.paginator:
		${buildrow(item, counter%2)}
		&lt;% counter += 1 %&gt;
	% endfor
	&lt;/table&gt;
	&lt;p&gt;${ c.paginator.pager('~2~') }&lt;/p&gt;
% else:
	&lt;p&gt;
	No pages have yet been created.
	&lt;a href=&quot;${h.url(controller='page', action='new')}&quot;&gt;Add one&lt;/a&gt;.
	&lt;/p&gt;
% endif

Visitando http://localhost:5000/page/list, funziona tutto.

Categorie:pylons, python Tag:

Pylons: Paginate parte 2

20 Ottobre 2011 Commenti chiusi

Paginate: AttributeError: ‘thread._local’ object has no attribute ‘mapper’

Nell’articolo precedente abbiamo sistemato il paginate.pager, ora
sistemiamo anche l’action del controller Page e la template, per allinearci
al capitolo 8 (SimpleSite) del Pylons Book.

...
from routes import Mapper
...

class PageController(BaseController):
    ...
    def list(self, id = None):
        mapper = Mapper()
        mapper.connect(':page')
        records = meta.Session.query(model.Page).all()
        if id is None:
            c.num = 1
        else:
            c.num = id
        c.paginator = paginate.Page(records, page = int(request.params.get('page', c.num)),
                                    items_per_page = 1,)
        return render('/derived/page/list.html')

il template list.html

<%inherit file="/base/index.html" />

<%def name="heading()"><h1>Page List</h1></%def>

<%def name="buildrow(page, odd=True)">
	%if odd:
		<tr class="odd">
	%else:
		<tr class="even">
	% endif

	<td valign="top">
		${h.link_to(page.id,
				h.url(controller=u'page',
					action='view',
					id=unicode(page.id)))}
	</td>

	<td valign="top">
		${page.title}
	</td>
	<td valign="top">${page.posted.strftime('%c')}</td>

	</tr>
</%def>

% if len(c.paginator):
	<p>Page : ${c.paginator.pager('$link_first $link_previous $first_item to $last_item of $item_count $link_next $link_last') }</p>

	<table class="paginator"><tr><th>Page ID</th><th>Page Title</th><th>Posted</th></tr>
	<% counter=0 %>

	% for item in c.paginator:
		${buildrow(item, counter%2)}
		<% counter += 1 %>
	% endfor
	</table>
	<p>${ c.paginator.pager('~2~') }</p>
% else:
	<p>
	No pages have yet been created.
	<a href="${h.url(controller='page', action='new')}">Add one</a>.
	</p>
% endif

Visitando http://localhost:5000/page/list, funziona tutto.

Categorie:pylons, python Tag:

Paginate: AttributeError: ‘thread._local’ object has no attribute ‘mapper’

19 Ottobre 2011 Commenti chiusi

Questo errore continuava ad apparire durante l’esercizio del pylons book:
(capitolo 8 – simplesite – pagination)

In sostanza l’eccezione si scatena al momento dell’utilizzo del pager().

ricapitoliamo in un secondo dove avviene l’errore.

Importiamo webhelpers per l’utilizzo di paginate:

&gt;&gt;&gt; import webhelpers.paginate as p
&gt;&gt;&gt; collection = range(1000)
&gt;&gt;&gt; page = p.Page(collection, page=3)
&gt;&gt;&gt; page
Page:
Collection type:  &lt;type 'list'&gt;
(Current) page:   3
First item:       41
Last item:        60
First page:       1
Last page:        50
Previous page:    2
Next page:        4
Items per page:   20
Number of items:  1000
Number of pages:  50

risalgo agli items della pagina:

&gt;&gt;&gt; for item in page.items: print item

40
41
...
59

se volessi usare il metodo pager() come da guida, senza argomenti, otterrei un errore:

&gt;&gt;&gt; page.pager()

Traceback (most recent call last):
...
  File &quot;c:programmipython26libsite-packagesRoutes-1.12.3-py2.6.eggroutes__init__.py&quot;, line 14, in __getattr__
    return getattr(self.__shared_state, name)
AttributeError: 'thread._local' object has no attribute 'mapper'
&gt;&gt;&gt;

La soluzione (suggerita qui), è di importare anche mapper da routes, creare un oggetto mapper e
connetterlo al controller:

&gt;&gt;&gt; from routes import Mapper
&gt;&gt;&gt; mapper = Mapper()
&gt;&gt;&gt; mapper.connect(':controller')

dopodichè chiamiamo pager passando come argomenti controller ed action:

&gt;&gt;&gt; page.pager(controller='ctrl', action='act')
literal(u'&lt;a class=&quot;pager_link&quot; href=&quot;/ctrl?page=1&quot;&gt;1&lt;/a&gt; &lt;a class=&quot;pager_link&quot; href=&quot;/ctrl?page=2&quot;&gt;2&lt;/a&gt; &lt;span class=&quot;pager_curpage&quot;&gt;3&lt;/span&gt; &lt;a class=&quot;pager_link&quot; href=&quot;/ctrl?page=4&quot;&gt;4&lt;/a&gt; &lt;a class=&quot;pager_link&quot; href=&quot;/ctrl?page=5&quot;&gt;5&lt;/a&gt; &lt;span class=&quot;pager_dotdot&quot;&gt;..&lt;/span&gt; &lt;a class=&quot;pager_link&quot; href=&quot;/ctrl?page=50&quot;&gt;50&lt;/a&gt;')

ovviamente al posto di ‘ctrl’ verrà messo il nome del controller che contiene l’azione che scatena l’eccezione.

Ora riporto la cosa, in pylons per bypassare l’errore durante il render della lista pagine.

Categorie:pylons, python Tag:

Paginate: AttributeError: ‘thread._local’ object has no attribute ‘mapper’

19 Ottobre 2011 Commenti chiusi

Questo errore continuava ad apparire durante l’esercizio del pylons book:
(capitolo 8 – simplesite – pagination)

In sostanza l’eccezione si scatena al momento dell’utilizzo del pager().

ricapitoliamo in un secondo dove avviene l’errore.

Importiamo webhelpers per l’utilizzo di paginate:

>>> import webhelpers.paginate as p
>>> collection = range(1000)
>>> page = p.Page(collection, page=3)
>>> page
Page:
Collection type:  <type 'list'>
(Current) page:   3
First item:       41
Last item:        60
First page:       1
Last page:        50
Previous page:    2
Next page:        4
Items per page:   20
Number of items:  1000
Number of pages:  50

risalgo agli items della pagina:

>>> for item in page.items: print item

40
41
...
59

se volessi usare il metodo pager() come da guida, senza argomenti, otterrei un errore:

>>> page.pager()

Traceback (most recent call last):
...
  File "c:\programmi\python26\lib\site-packages\Routes-1.12.3-py2.6.egg\routes\__init__.py", line 14, in __getattr__
    return getattr(self.__shared_state, name)
AttributeError: 'thread._local' object has no attribute 'mapper'
>>>

La soluzione (suggerita qui), è di importare anche mapper da routes, creare un oggetto mapper e
connetterlo al controller:

>>> from routes import Mapper
>>> mapper = Mapper()
>>> mapper.connect(':controller')

dopodichè chiamiamo pager passando come argomenti controller ed action:

>>> page.pager(controller='ctrl', action='act')
literal(u'<a class="pager_link" href="/ctrl?page=1">1</a> <a class="pager_link" href="/ctrl?page=2">2</a> <span class="pager_curpage">3</span> <a class="pager_link" href="/ctrl?page=4">4</a> <a class="pager_link" href="/ctrl?page=5">5</a> <span class="pager_dotdot">..</span> <a class="pager_link" href="/ctrl?page=50">50</a>')

ovviamente al posto di ‘ctrl’ verrà messo il nome del controller che contiene l’azione che scatena l’eccezione.

Ora riporto la cosa, in pylons per bypassare l’errore durante il render della lista pagine.

Categorie:pylons, python Tag:

Pylons: Fantamanager parte 3 – authorization e authentication con repoze.what

14 Ottobre 2011 Commenti chiusi

<part_1<part_2
Le prime basi di pylons mi sono piaciute parecchio.
Ho testato i vari semplici esempi disponibili sulle guide e sulla documentazione
reperibile on-line, poi mi sono imbattuto sul problema dell’autenticazione e
delle autorizzazioni.
I metodi sono parecchi, ma ho scelto di appoggiarmi per questo compito
a Repoze.what (come suggerito dalla guida ufficiale).
Tale guida è mutuata da questa e, vista la scarsa documentazioni disponibile
in Italiano ho pensato di riassumere un po’ quello che ho provato e testato.
Per i programmatori più esperti, forse non ci sono stati problemi, per me, aspirante tale, invece sì.

Anticipo subito che i problemi più fastidiosi li ho avuti codificando i models di
Sqlalchemy, avvalendomi del declarative_base.
Non ho avuto modo di vedere repoze funzionante appieno, a causa di un errore riguardante il mapper di SQLA.

‘EagerLoader’ object has no attribute ‘mapper’
Per questa ragione ho dovuto ricodificare tutte le classi della mia applicazione:
Giocatore, Squadra ecc.

Comunque…

I componenti utili alla causa sono:

-Pylons v1.0
-repoze.what v1.0.9
-repoze.what-pylons v1.0
-repoze.what-quickstart v1.0.8

perchè tutto funzioni, sono necessarie le seguenti dipendenze:

-repoze.who
-repoze.who-friendlyform
-repoze.what
-repoze.what-pylons
-repoze.what-quickstart
-repoze.what-plugins-sql
-repoze.who.plugins.sa

Dovrebbero comunque installarsi tutte automaticamente via easy_install

easy_install repoze.what-pylons

easy_install repoze.what-quickstart

Ora riprendendo a mano il mio progetto di esempio, creo il file:
..FantaManagerfantamanagermodelauth.py
dove definisco i modelli utilizzati da repoze.what

from sqlalchemy import Table, ForeignKey, Column
from sqlalchemy.types import Unicode, UnicodeText, Integer, Date, CHAR
from sqlalchemy import orm
from fantamanager.model.meta import metadata
import os
from hashlib import sha1

group_table = Table('group', metadata,
    Column('id', Integer(), primary_key=True),
    Column('name', Unicode(255), unique=True, nullable=False),
)

permission_table = Table('permission', metadata,
    Column('id', Integer(), primary_key=True),
    Column('name', Unicode(255), unique=True, nullable=False),
)

user_table = Table('user', metadata,
    Column('id', Integer(), primary_key=True),
    Column('username', Unicode(255), unique=True, nullable=False),
    Column('email', Unicode(255), unique=True, nullable=False),
    Column('password', Unicode(80), nullable=False),
    Column('fullname', Unicode(255), nullable=False),
)

# many-to-many relationship between groups and permissions.
group_permission_table = Table('group_permission', metadata,
    Column('group_id', Integer, ForeignKey('group.id')),
    Column('permission_id', Integer, ForeignKey('permission.id')),
)

# many-to-many relationship between groups and users
user_group_table = Table('user_group', metadata,
    Column('user_id', Integer, ForeignKey('user.id')),
    Column('group_id', Integer, ForeignKey('group.id')),
)

class Group(object):
    pass

class Permission(object):
    pass

class User(object):

    def _set_password(self, password):
        &quot;&quot;&quot;Hash password on the fly.&quot;&quot;&quot;
        hashed_password = password

        if isinstance(password, unicode):
            password_8bit = password.encode('UTF-8')
        else:
            password_8bit = password

        salt = sha1()
        salt.update(os.urandom(60))
        hash = sha1()
        hash.update(password_8bit + salt.hexdigest())
        hashed_password = salt.hexdigest() + hash.hexdigest()

        # Make sure the hased password is an UTF-8 object at the end of the
        # process because SQLAlchemy _wants_ a unicode object for Unicode
        # fields
        if not isinstance(hashed_password, unicode):
            hashed_password = hashed_password.decode('UTF-8')

        self.password = hashed_password

    def _get_password(self):
        &quot;&quot;&quot;Return the password hashed&quot;&quot;&quot;
        return self.password

    def validate_password(self, password):
        &quot;&quot;&quot;
        Check the password against existing credentials.

        :param password: the password that was provided by the user to
            try and authenticate. This is the clear text version that we will
            need to match against the hashed one in the database.
        :type password: unicode object.
        :return: Whether the password is valid.
        :rtype: bool

        &quot;&quot;&quot;
        hashed_pass = sha1()
        hashed_pass.update(password + self.password[:40])
        return self.password[40:] == hashed_pass.hexdigest()

# Map SQLAlchemy table definitions to python classes
orm.mapper(Group, group_table, properties={
    'permissions':orm.relation(Permission, secondary=group_permission_table),
    'users':orm.relation(User, secondary=user_group_table),
})
orm.mapper(Permission, permission_table, properties={
    'groups':orm.relation(Group, secondary=group_permission_table),
})
orm.mapper(User, user_table, properties={
    'groups':orm.relation(Group, secondary=user_group_table),
})

il file ..FantaManagerfantamanagermodelmeta.py è così composto:

&quot;&quot;&quot;SQLAlchemy Metadata and Session object&quot;&quot;&quot;
from sqlalchemy.orm import scoped_session, sessionmaker
from sqlalchemy import MetaData

__all__ = ['Base', 'Session']

Session = scoped_session(sessionmaker())

metadata = MetaData()

Ora importiamo Group, User, Permission nel file ..FantaManagerfantamanagermodel__init__.py

&quot;&quot;&quot;The application's model objects&quot;&quot;&quot;
import sqlalchemy as sa
from sqlalchemy import orm
from fantamanager.model import meta
from fantamanager.model.auth import User, Group, Permission

def init_model(engine):
    &quot;&quot;&quot;Call me before using any of the tables or classes in the model&quot;&quot;&quot;
    sm = orm.sessionmaker(autoflush=True, autocommit=False, bind=engine)
    meta.engine = engine
    meta.Session = orm.scoped_session(sm)

Una volta definite le class/table per le autorizzazioni/autenticazioni, dobbiamo settare
repoze.what in modo che le utilizzi. Per fare questo si utilizza la funzione
setup_sql_auth() all’interno della funzione add_auth(), nel file
..FantaManagerfantamanagerlibauth.py

from repoze.what.plugins.quickstart import setup_sql_auth
from fantamanager.model import meta
from fantamanager.model.auth import User, Group, Permission

def add_auth(app, config):
    &quot;&quot;&quot;
    Add authentication and authorization middleware to the ``app``.

    We're going to define post-login and post-logout pages
    to do some cool things.

    &quot;&quot;&quot;
    # we need to provide repoze.what with translations as described here:
    # http://what.repoze.org/docs/plugins/quickstart/
    return setup_sql_auth(app, User, Group, Permission, meta.Session,
                login_url='/account/login',
                post_login_url='/account/login',
                post_logout_url='/',
                login_handler='/account/login_handler',
                logout_handler='/account/logout',
                cookie_secret=config.get('cookie_secret'),
                translations={
                    'user_name': 'username',
                    'group_name': 'name',
                    'permission_name': 'name',
                })

Editiamo il file ..FantaManagerdevelopment.ini ed inseriamo il cookie_secret

[app:main]
use = egg:FantaManager
full_stack = true
static_files = true
sqlalchemy.url = sqlite:///%(here)s/FantaManager.sqlite
cache_dir = %(here)s/data
beaker.session.key = fantamanager
beaker.session.secret = somesecret
# set repoze cookie secret
cookie_secret = 'your-own-secret'

il paramtero cookie_secret, essendo config, un argomento passato alla precedente funzione add_auth(),
ci serve per generare i cookies.
Ora aggiungiamo il middleware (lo strato di cipolla Repoze).
Editiamo il file ..FantaManagerfantamanagerconfigmiddleware.py

&quot;&quot;&quot;Pylons middleware initialization&quot;&quot;&quot;
from beaker.middleware import SessionMiddleware
from paste.cascade import Cascade
from paste.registry import RegistryManager
from paste.urlparser import StaticURLParser
from paste.deploy.converters import asbool
from pylons.middleware import ErrorHandler, StatusCodeRedirect
from pylons.wsgiapp import PylonsApp
from routes.middleware import RoutesMiddleware

from fantamanager.config.environment import load_environment
from fantamanager.lib.auth import add_auth
# from repoze.who
from repoze.who.config import make_middleware_with_config as make_who_with_config

def make_app(global_conf, full_stack=True, static_files=True, **app_conf):
    ...
    # Configure the Pylons environment
    config = load_environment(global_conf, app_conf)

    # The Pylons WSGI app
    app = PylonsApp(config=config)

    # Routing/Session/Cache Middleware
    app = RoutesMiddleware(app, config['routes.map'], singleton=False)
    app = SessionMiddleware(app, config)

    # CUSTOM MIDDLEWARE HERE (filtered by error handling middlewares)
    # added for repoze.what auth
    app = add_auth(app, config)

    if asbool(full_stack):
        # Handle Python exceptions
        app = ErrorHandler(app, global_conf, **config['pylons.errorware'])

        # Display error documents for 401, 403, 404 status codes (and
        # 500 when debug is disabled)
        if asbool(config['debug']):
            app = StatusCodeRedirect(app)
        else:
            app = StatusCodeRedirect(app, [400, 401, 403, 404, 500])

    # Establish the Registry for this application
    app = RegistryManager(app)

    if asbool(static_files):
        # Serve static files
        static_app = StaticURLParser(config['pylons.paths']['static_files'])
        app = Cascade([static_app, app])
    app.config = config
    return app

il nostro strato di cipolla è rappresentato dalla riga di codice

app = add_auth(app, config)

subito sotto

    # CUSTOM MIDDLEWARE HERE

Aggiungiamo ora un Gruppo ‘admin’, un paio di utenti e i permessi di admin.
Inseriamo il codice necessario, all’interno del file
..FantaManagerfantamanagerwebsetup.py

&quot;&quot;&quot;Setup the FantaManager application&quot;&quot;&quot;
import logging

import pylons.test

from fantamanager import model
from fantamanager.model import meta
from fantamanager.config.environment import load_environment
from fantamanager.model.meta import Session
from fantamanager.model.auth import User, Group, Permission
from fantamanager.model.models import Giocatore, Squadra

log = logging.getLogger(__name__)


def setup_app(command, conf, vars):
    &quot;&quot;&quot;Place any commands to setup fantamanager here&quot;&quot;&quot;
    # Don't reload the app if it was loaded under the testing environment
    if not pylons.test.pylonsapp:
        load_environment(conf.global_conf, conf.local_conf)

    meta.metadata.bind = meta.engine
    # Create the tables if they don't already exist
    log.info(&quot;Creating tables&quot;)
    meta.metadata.drop_all(checkfirst=True, bind=Session.bind)
    meta.metadata.create_all(bind=Session.bind)
    # Now let's create users, group and permission

    # ADMIN group
    log.info(&quot;Adding initial users, groups and permissions...&quot;)
    log.info(&quot;Adding 'Admin' Group...&quot;)
    g = Group()
    g.name = u'admin'
    meta.Session.add(g)
    meta.Session.commit()
    log.info(&quot;+++ 'Admin' Group Done!&quot;)

    # ADMIN permission
    log.info(&quot;-&gt; Adding 'Admin' permission...&quot;)
    p = Permission()
    p.name = u'admin'
    p.groups.append(g)
    meta.Session.add(p)
    meta.Session.commit()
    log.info(&quot;+++ 'Admin' permission assigned to 'Admin' group!&quot;)

    # User ADMIN
    log.info(&quot;-&gt; Adding 'Admin' user...&quot;)
    u = User()
    u.username = u'admin'
    u.fullname = u'admin'
    u._set_password('admin')
    u.email = u'[email protected]'
    u.groups.append(g)
    meta.Session.add(u)
    meta.Session.commit()
    log.info(&quot;+++ 'Admin' user created!&quot;)
    log.info(&quot;+++ 'Admin' user assigned to 'Admin' group!&quot;)

    # User TEST
    log.info(&quot;-&gt; Adding 'Test' user...&quot;)
    u = User()
    u.username = u'test'
    u.fullname = u'test'
    u._set_password('test')
    u.email = u'[email protected]'
    meta.Session.add(u)
    meta.Session.commit()
    log.info(&quot;+++ 'Test' user created!&quot;)
    log.info(&quot;+++ Done!&quot;)

alternativamente utilizzare la shell di paster e creare gli oggetti interattivamente:

paster shell

Ora non resta che definire il controller e la action di Login:

paster controller account

Edito il controller appena creato (..FantaManagerfantamanagercontrollersaccount.py)

import logging

from pylons import request, response, session, tmpl_context as c, url
from pylons.controllers.util import abort, redirect

from fantamanager.lib.base import BaseController, render

from repoze.what.predicates import not_anonymous, has_permission
from repoze.what.plugins.pylonshq import ActionProtector
from pylons.controllers.util import redirect

log = logging.getLogger(__name__)

class AccountController(BaseController):

    def login(self):
        &quot;&quot;&quot;
        This is where the login form should be rendered.
        Without the login counter, we won't be able to tell if the user has
        tried to log in with wrong credentials
        &quot;&quot;&quot;
        identity = request.environ.get('repoze.who.identity')
        came_from = str(request.GET.get('came_from', '')) or 
                    url(controller='account', action='welcome')
        if identity:
            redirect(url(came_from))
        else:
            c.came_from = came_from
            c.login_counter = request.environ['repoze.who.logins'] + 1
            return render('/derived/account/login.html')

    @ActionProtector(not_anonymous())
    def welcome(self):
        &quot;&quot;&quot;
        Greet the user if she logged in successfully or redirect back
        to the login form otherwise(using ActionProtector decorator).
        &quot;&quot;&quot;
        identity = request.environ.get('repoze.who.identity')
        return 'Welcome back %s' % identity['repoze.who.userid']

    @ActionProtector(not_anonymous())
    def test_user_access(self):
        return 'You are inside user section'

    @ActionProtector(has_permission('admin'))
    def test_admin_access(self):
        return 'You are inside admin section'

…e la relativa template ..FantaManagerfantamanagertemplatesderivedaccountlogin.html

% if c.login_counter &gt; 1:
    Incorrect Username or Password
% endif
&lt;form action=&quot;${h.url(controller='account', action='login_handler'
,came_from=c.came_from, __logins=c.login_counter)}&quot; method=&quot;POST&quot;&gt;
&lt;label for=&quot;login&quot;&gt;Username:&lt;/label&gt;
&lt;input type=&quot;text&quot; id=&quot;login&quot; name=&quot;login&quot; /&gt;&lt;br /&gt;
&lt;label for=&quot;password&quot;&gt;Password:&lt;/label&gt;
&lt;input type=&quot;password&quot; id=&quot;password&quot; name=&quot;password&quot; /&gt;&lt;br /&gt;
&lt;input type=&quot;submit&quot; id=&quot;submit&quot; value=&quot;Submit&quot; /&gt;
&lt;/form&gt;

Prima di lanciare il server, ricodifico le class/table della mia applicazione
(precedentemente create utilizzando il declarative_base).
Per renderla breve utilizzo solo la classe Giocatore e Squadra:

# -*- coding: utf-8 -*-#
## ..modelmodels.py
'''model for FantaManager application powered by Pylons''' 

from fantamanager.model.meta import metadata
from sqlalchemy.types import Unicode
from sqlalchemy import create_engine, Column, Integer, String, Float
from sqlalchemy import ForeignKey, Table
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import backref, aliased
from sqlalchemy.orm import relation as relationship
from sqlalchemy.orm import sessionmaker
from sqlalchemy import orm, func, desc, asc, and_

#metadata = Base.metadata

giocatore_table = Table('giocatori', metadata,
    Column('id', Integer(), primary_key=True),
    Column('idgaz', Integer()),
    Column('nome', Unicode(255)),
    Column('squadra', Unicode(255)),
    Column('valore', Integer()),
    Column('ruolo', Unicode(255)),
)

squadra_table = Table('squadre', metadata,
    Column('id', Integer(), primary_key=True),
    Column('nome', Unicode(255)),
    Column('budget', Integer()),
    Column('cost', Integer()),
    Column('pts', Float()),
)
# M2M relation
squadre_giocatori_table = Table('squadre_giocatori', metadata,
    Column('squadre_id', Integer, ForeignKey('squadre.id')),
    Column('giocatori_id', Integer, ForeignKey('giocatori.id')),
)

class Giocatore(object):
    pass

class Squadra(object):
    pass

# Map SQLAlchemy table definitions to python classes
orm.mapper(Squadra, squadra_table, properties={
    'giocatori':orm.relation(Giocatore, secondary=squadre_giocatori_table),})
orm.mapper(Giocatore, giocatore_table, properties={
    'squadre':orm.relation(Squadra, secondary=squadre_giocatori_table),})

Lanciamo il setup dell’applicazione:

paster setup-app development.ini

e avviamo il server:

paster serve --reload development.ini

se visito la pagina:
http://127.0.0.1:5000/account/test_user_access
e inserisco username e password (‘test’, ‘test’)
vedo renderizzato correttamente: You are inside user sectionse visito la pagina
http://127.0.0.1:5000/account/test_admin_access
ottengo il FORBIDDEN default di pylons poichè mi vedo ancora assegnato il cookie
relativo al test-user

ATTENZIONE! svuotare la cache di firefox prima di testare la pagina di Admin!

se ri-visito la pagina:
http://127.0.0.1:5000/account/test_admin_access
e inserisco username e password (‘admin’, ‘admin’)
vedo renderizzato correttamente: You are inside ADMIN section
Tutto ok!

Ora se vogliamo decorare la azioni del mio controller personale (la mia app),
basta appunto anteporre il decorator desiderato, prima della action che vogliamo
proteggere, es:

import logging

from sqlalchemy import distinct

from pylons import request, response, session, tmpl_context as c, url
from pylons.controllers.util import abort, redirect
from fantamanager.lib.base import Session, BaseController, render
from fantamanager.model.models import Giocatore

from repoze.what.predicates import not_anonymous, has_permission
from repoze.what.plugins.pylonshq import ActionProtector

import fantamanager.lib.helpers as h

log = logging.getLogger(__name__)

class SerieaController(BaseController):
    def __before__(self):
        self.ateam_q = Session.query(distinct(Giocatore.squadra))

    @ActionProtector(not_anonymous())
    def index(self):
        return 'Test: &gt;&gt; Controller Serie_A &gt;&gt; Connection OK!'

    @ActionProtector(has_permission('admin'))
    def squadre(self):
        c.squadre = [team[0] for team in self.ateam_q.all()]
        return render('/seriea.mako')

se visito la pagina http://127.0.0.1:5000/seriea/squadre
devo fare il login: se utilizzo test mi becco il Forbidden
se svuoto la cache ed entro come admin, passo!

Questo è quanto.

Categorie:Fantacalcio, pylons, python, sqlalchemy Tag:

Pylons: Fantamanager parte 2

11 Ottobre 2011 Commenti chiusi

<part_1
Proviamo ora ad interrogare il database e a fare il render del risultato
di una query, utilizzando un banale template.

Innanzitutto configuriamo il file ..FantaManagerfantamanagerconfigrouting.py
in modo da poter utilizzare degli url dinamici.

# CUSTOM ROUTES HERE

    map.connect('/{controller}/{action}')
    map.connect('/{controller}/{action}/{id}')
    map.connect('map_giocatori_1', '/team/:team',
                controller = 'giocatori', action = 'team',
                team = '[noteam]')

nel terzo map, con la sintassi :variable si assegna a variable, il valore presente
nella url (es. /team/inter -> team = inter)

Con questa mappatura posso usare la stessa regola, per ogni squadra che voglio
renderizzare (vediamo dopo), ma sopratutto serve per passare un argomento alla
action del controller.

Creiamo quindi il controller ‘giocatori’ (già battezzato in routing.py)

paster controller giocatori

Dopo che il controller è stato creato, lo editiamo ed aggiungiamo la action “team”
(anch’essa battezzata in routing.py), che utilizzerò per l’estrapolazione dei dati
e l’import del model Giocatore e della Session con i quali creerò la query:

import logging

from pylons import request, response, session, tmpl_context as c, url
from pylons.controllers.util import abort, redirect

from fantamanager.lib.base import Session, BaseController, render
from fantamanager.model import Giocatore


log = logging.getLogger(__name__)

class GiocatoriController(BaseController):

    def __before__(self):
        self.giocatori_q = Session.query(Giocatore)

    def index(self):
        return 'Test: &gt;&gt; Connection OK!'

    def team(self, team):
        c.team = team.upper()
        c.giocatori = [giocatore for giocatore in self.giocatori_q.filter(
                       Giocatore.squadra == c.team).all()]
        return render('/team.mako')

come si nota, nello skeleton generato da paster, ho aggiunto:

from fantamanager.model import Giocatore
from fantamanager.lib.base import Session

il metodo __before__() (viene chiamato subito prima dell’azione, vedere documentazione pylons)

    def __before__(self):
        self.giocatori_q = Session.query(Giocatore)

e la action “team”.

    def team(self, team):
        c.team = team.upper()
        c.giocatori = [giocatore for giocatore in self.giocatori_q.filter(
                       Giocatore.squadra == c.team).all()]
        return render('/team.mako')

ecco che al metodo (action) team, viene passato come argomento “team”, che è il
nome della squadra con la quale effettuerò la query e che ho estrapolato con
la url dinamica in routing.py.
team mi serve maiuscolo inquanto i nomi delle squadre nel db, sono maiuscoli.
Dopodichè associo al context_object “c”, la mia variabile e la lista derivata
dalla query, per poter accedervi nella template.

La template ..FantaManagerfantamanagertemplatesteam.mako avrà il seguente contenuto:

&lt;%inherit file=&quot;/base.mako&quot; /&gt;

&lt;%def name=&quot;head_tags()&quot;&gt;
&lt;!-- add some head tags here --&gt;
&lt;/%def&gt;

&lt;p&gt;Giocatori ${c.team}&lt;/p&gt;

&lt;ul&gt;
% for giocatore in c.giocatori:
&lt;li&gt;${giocatore.nome}&lt;/li&gt;
% endfor
&lt;/ul&gt;

&lt;a href=&quot;${h.url(controller='seriea', action='squadre')}&quot;&gt; back &lt;/a&gt;

Creiamo inoltre la template padre ..FantaManagerfantamanagertemplatesbase.mako:

&lt;!DOCTYPE html PUBLIC &quot;-//W3C//DTD XHTML 1.0 Transitional//EN&quot;
&quot;http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd&quot;&gt;

&lt;html&gt;
  &lt;head&gt;
    ${self.head_tags()}
  &lt;/head&gt;

&lt;h1&gt;Fantacalcio Manager&lt;/h1&gt;

  &lt;body&gt;
    ${self.body()}
  &lt;/body&gt;
&lt;/html&gt;

Avviamo il server per un test:

paster serve --reload development.ini

e visitiamo le seguenti url:

http://127.0.0.1:5000/team/bologna
http://127.0.0.1:5000/team/inter

Ora voglio creare una pagina che mi elenchi tutte le squadre della serie A,
con i link che mi ridirezionino alla pagina relativa alla rosa della squadra
(esempio precedente)

Creo innanzitutto un nuovo controller:

paster controller seriea

Edito e modifico il file ..FantaManagerfantamanagercontrollersseriea.py:

...
from sqlalchemy import distinct
...
from fantamanager.lib.base import Session, BaseController, render

from fantamanager.model import Giocatore
...
class SerieaController(BaseController):
    def __before__(self):
        self.ateam_q = Session.query(distinct(Giocatore.squadra))

    def squadre(self):
        c.squadre = [team[0] for team in self.ateam_q.all()]
        return render('/seriea.mako')

Come si nota, il context_object c.squadre sarà disponibile dopo, nella template.
Nota: importanti gli import di distinct e Session (sqlalchemy)

Per accedere alla pagina /seriea/squadre, non importa aggiungere un map.connect nel
file routing.py, inquanto la url risponde già al formato richiesto

map.connect('/{controller}/{action}')

Siccome dalla pagina che elenca le squadre, voglio avere i link alle rose (cioè essere
redirezionato), voglio anche poter ritornare indietro alla pagina di partenza.
Per fare questo utilizzo un helper, richiamabile con “h” all’interno del template, previa
importazione degli helpers, nel file ..FantaManagerfantamanagerlibhelpers.py:

from webhelpers.html import escape, HTML, literal, url_escape
from webhelpers.html.tags import *
from pylons import url

Nota: attenzione che in pylons < 1.0 l'import è

from routes import url_for

Ecco che posso creare il template seriea.mako con il seguente contenuto:

&lt;%inherit file=&quot;/base.mako&quot; /&gt;

&lt;%def name=&quot;head_tags()&quot;&gt;
&lt;!-- add some head tags here --&gt;
&lt;/%def&gt;

&lt;p&gt;Squadre Serie A&lt;/p&gt;

&lt;ul&gt;
% for squadra in c.squadre:
&lt;li&gt;&lt;a href=&quot;/team/${squadra}&quot;&gt;${squadra}&lt;/a&gt;&lt;/li&gt;
% endfor
&lt;/ul&gt;

Se ora lanciamo il server e visitiamo la pagina http://127.0.0.1:5000/seriea/squadre
avremo la lista delle squadre con i relativi link, che cliccati porteranno alla pagina
relativa alla rosa. In quest'ultima abbiamo il link che ci riporti alla pagina di partenza.

Utilizzare il seguente db di test.

Categorie:Fantacalcio, pylons, python Tag:

Pylons: ImportError: cannot import name redirect_to

6 Ottobre 2011 Commenti chiusi

Altro deprecato.Seguendo gli esempi del pylons book ci si imbatte quando si
tenta un redirect utilizzando l’helper redirect_to.

Le correzioni da apportare sono:
nel file lib.helpers.py sostituire:

from pylons.controllers.util import redirect_to

con

from pylons.controllers.util import redirect

mentre nel controller:

h.redirect_to(controller='formtest', action='result')

con

h.redirect(url('/formtest/result'))
Categorie:pylons, python Tag:

Pylons: ImportError: cannot import name redirect_to

6 Ottobre 2011 Commenti chiusi

Altro deprecato.
Seguendo gli esempi del pylons book ci si imbatte quando si
tenta un redirect utilizzando l’helper redirect_to.

Le correzioni da apportare sono:
nel file lib.helpers.py sostituire:

from pylons.controllers.util import redirect_to

con

from pylons.controllers.util import redirect

mentre nel controller:

h.redirect_to(controller='formtest', action='result')

con

h.redirect(url('/formtest/result'))
Categorie:pylons, python Tag:

Pylons: AttributeError: ‘thread._local’ object has no attribute ‘mapper’

6 Ottobre 2011 Commenti chiusi

Se come me stai studiando Pylons e stai seguendo il Pylons Book,
è probabile tu sia incappato in un errore durante l’utilizzo degli helpers.

AttributeError: 'thread._local' object has no attribute 'mapper'

Questo errore è causato dal helper url_for che, utilizzando pylons 1.0, è
ad oggi deprecato. Al posto di url_for si utilizza url.

La soluzione all’esercizio è:
sostituire nel file lib.helpers l’importazione che fa riferimento all’helper url.
Invece di

from routes import url_for

utilizzare

from pylons import url

Ovviamente se l’importazione è stata utilizzata anche nei controllers, procedere
alla sostituzione anche lì.

Per quel che riguarda invece il template, sostituire tutti i richiami a

&lt;p&gt;Hello ${c.name}! You are visiting ${h.url_for()}&lt;/p&gt;

con

&lt;p&gt;Hello ${c.name}! You are visiting ${h.url()}&lt;/p&gt;.
Categorie:pylons Tag: