Bookmark and Share
Document Actions

Moduli

Up one level
Come implementare un modulo di ricerca specializzato.

Andando avanti con un semplice modulo web utile al nostro prodotto Notizie Regionali, potremmo immaginare d’implementare un modulo di ricerca specializzato, con un testo di ricerca generico usando l’indice Plone SearcheableText e l’indice region come criteri possibili. Per fare questo, adotteremo la library z3c.form, la useremo per costruire il nostro modulo di vista browser, e creeremo anche un vocabolario delle regioni. Inoltre in questa sezione, otterrete una buona conoscenza su come interrogare lo strumento portal_catalog e presentare i suoi risultati. Facciamolo!

Fino ad ora, abbiamo considerato Archetypes come nostro migliore amico nello sviluppo del Plone. Ma è qui che ci perdiamo qualcosa: gestendo moduli web senza alcun contenuto oggetto dietro di essi.

Per esempio, qualche volta utilizziamo moduli web solo per collezionare informazione, non per gestire il contenuto.

La generazione di moduli web Archetypes è abbastanza potente, ricca di widgets e di ganci di validazione, facile da gestire, poiché è integrato con i modelli di campi che descrivono il nostro tipo di contenuto.

Sfortunatamente, abbiamo bisogno di un modello di campi per farlo lavorare, ciò implica di istanziare un tipo di contenuto oggetto esistente nel nostro database. Infatti, ogni linea di codice Archetypes fa delle ipotesi che c’è un tipo di contenuto oggetto esistente come il contesto da utilizzare.

Per qualche tempo, questa questione potrebbe non essere risolta in modo elegante, ma le tecnologie di Zope 3 ci aiutano con un paio di buone soluzioni, sia autorizzarci a gestire moduli web utilizzando classi Python e interfacce: Zope.formlib e z3c.form.

Il secondo sembra un po’ più flessibile e utilizzato, così in questa sezione esamineremo il suo schema di sviluppo.

 

 

NOTA z3c form è un modulo HTML avanzato e un framework widget per Zope 3- cioè, un modo per generare e controllare moduli e widgets da codici Python. Con uno schema simile a Archetypes, esso fornisce un modulo e i suoi widgets partendo da uno schema (non uno schema di campi, ma un’interfaccia Zope 3). Per una profonda comprensione di come lavora z3c.form, per favore date un occhiata a http://pypi.Python.org/pypi/z3c.form.

 

 

Ottenere e utilizzare il form z3c

 

Prima di tutto, abbiamo bisogno di ottenere il pacchetto plone.z3c e tutte le sue dipendenze per Plone.

Possiamo avere il pacchetto eggified aggiungendo plone.z3cform all’opzione eggs della sezione buildout del nostro file buildout.cfg:

 

[buildout]

Parts =

       Plone

       productdistros

       instance

       Zopepy

       fakeZope2eggs

 

 

...

 

 

eggs =

         elementtree

         Plonebook.regional_news

         Plone.z3cform

 

...

[instance]

zcml=

 

 

          Plonebook.regional_news

          Plone.z3cform

 

...

[fakeZope2eggs]

recipe = affinitic.recipe.fakeZope2eggs

Zope2-part = instance

Zope2-location=${instance:Zope2-location}

additional-fake-eggs=

       Zope.testing

       Zope.i18n

       Zope.component

 

In realtà, è un po’ più complicato: Plone.z3cform dipende da z3c.form, che è un pacchetto originale Zope 3. Guardate l’ultima sezione del codice: a causa delle dependencies attaccate all’egg the z3c.form, il processo di buildout finirebbe per prendere le dependencies errate per il nostro ambiente Zope 2, ecco perché abbiamo bisogno di utilizzare la sezione [fakeZope2eggs].

Dopo aver modificato il file buildout.cfg, eseguite di nuovo lo script buildout:

 

$ cd~/Plonebook

$ ./bin/instance stop

$ ./bin/buildout-v

 

Alla fine di questo processo, dovremmo essere in grado di implementare la nostra nuova vista di ricerca usando Plone.z3cform. Andate nella cartella browser del pacchetto e aggiungete un nuovo file chiamato regionalnewssearch.py, contenente questo codice:

 

from Zope import interface,schema

from z3c.form import form, field, button

from Plone.z3cform.layout import wrap_form

 

class IRegionalNewsSearch(interface.Interface):

 

      SearchableText=schema.TextLine(title=u"Text")

      region=schema.TextLine(title=u"Region")

 

class RegionalNewsSearchForm(form.Form):

      fields=field.Fields(IRegionalNewsSearch)

      ignoreContext=True # don't use context to get widget data

      label=u"SearchRegionalNews"

 

      @button.buttonAndHandler(u'Search')

      def handleSearch(self, action):

            data, errors=self.extractData()

            self.status=data

 

RegionalNewsSearchView = wrap_form(RegionalNewsSearchForm)

 

 

Infine, aprite browser/configure.zcml nel vostro editor e aggiungete la registrazione per la nuova visualizzazione di ricerca:

 

<browser:page

     for="*"

     name="rn_search"

     class=".regionalnewssearch.RegionalNewsSearchView"

     permission="Zope.Public"

/>

 

Notate che non abbiamo specificato alcun modulo per questa visualizzazione. Infatti la funzione wrap_form  che noi abbiamo ottenuto da plone.z3cform  fa una magia, unendo  la classe RegionalNewsSearchForm - che è responsabile per il meccanismo del modulo web- alla classe RegionalNewsSearchView , la classe di visualizzazione browser attuale.

Così, torniamo indietro al terminale e avviamo il nostro servizio Zope:

 

$ cd ~/Plonebook

$ ./bin/instance  start

 

Poi puntate il vostro browser su http://localhost:8080/plone/rn_search e godetevi la vostra prima visualizzazione z3c.form (vedi figura 9-6). Non fa molto, ma è facile implementarla con pochissimi sforzi, e include validazioni su richiesta campi e un tasto di invio lavoro.

 

 

 

Per completare la nostra missione, abbiamo ancora bisogno di un paio di cose importanti: implementare la logica di ricerca e rendere più facile la scelta della regione desiderata.

Iniziamo con quest’ultima. Quando abbiamo introdotto l’attributo region per il nostro tipo di contenuto nella sezione “Tipi di Contenuto Personalizzati”, non abbiamo costruito un vocabolario configurabile sul web. Ricordate, abbiamo bisogno di questo in modo che i nostri amministratori di portale non debbano armeggiare con nessun codice Python per aggiornare le regioni disponibili per gli elementi delle notizie sul sito. Non lo faremo neppure ora, ma implementiamo un vocabolario Zope 3 da utilizzare sia per l’attributo del nostro tipo di contenuto, sia per l’interfaccia della ricerca.

Andate nella cartella root del vostro pacchetto e aggiungete un nuovo file chiamato vocabularies.py, contenente questo codice:

 

from Zope.interface import implements

from Zope.app.schema.vocabulary import IVocabularyFactory

from Zope.schema.vocabulary import SimpleTerm

from Zope.schema.vocabulary import SimpleVocabulary

 

REGIONS = (('Italy','Italy'),

                       ('France','France'),

                       ('England','England'),

                      )

 

class RegionsVocabulary(object):

      """Vocabulary factory for regional news regions.

      """

      implements(IVocabularyFactory)

    

      def __call__(self,context):

            items=[SimpleTerm(value, title)for\

                                      value, title in REGIONS]

            return SimpleVocabulary(items)

 

RegionsVocabularyFactory=RegionsVocabulary()

 

Poi aprite configure.zcml nella cartella root del pacchetto e registrate il vostro nuovo vocabolario come un’utility Zope 3:

 

...

<!---*-extra stuff goes here-*--->

<utility

    component=".vocabularies.RegionsVocabularyFactory"

    name="Plonebook.regional_news.vocabularies.regions"

    />

...

 

Adesso abbiamo un nuovo vocabolario da utilizzare. Modifichiamo l’interfaccia IRegionalNewsSearch nel browser/regionalnewssearch.py come questa:

 

class IRegionalNewsSearch(interface.Interface):

 

 

       SearchableText=schema.TextLine(title=u"Text")

       region = schema.Choice(title=u"Region",

                     vocabulary="Plonebook.regional_news.vocabularies.regions")

 

Come potete vedere, abbiamo cambiato l’attributo region in un attributo Choice, poi abbiamo utilizzato il nostro nuovo vocabolario per riempirlo.

Se volete sapere, quali sono i risultati, riavviate il vostro servizio Zope, digitando i comandi seguenti:

 

$cd ~/Plonebook

$ ./bin/instance restart

 

Rimandate indietro il vostro browser su http://localhost:8080/plone/rn_search, l’attributo regioni vi permetterà di selezionare solo le regioni che avete specificato nel vostro vocabolario.

Avete ancora bisogno di cambiare il vostro schema del tipo di contenuto, così che potete anche utilizzare il vostro vocabolario.

Aprite il modulo content/regionalnews.py nel vostro pacchetto e cambiate la definizione dell’attributo region come segue:

 

atapi.StringField(

       name='region',

       storage=atapi.AnnotationStorage(),

       required=False,

       #searchable=1,

       #default='  ',

       #schemata='default',

       vocabulary_factory = "Plonebook.regional_news.vocabularies.regions",

       widget=atapi.SelectionWidget(

          label=_(u"region"),

          description=_( u"Description of region"),

       ),

),­­

 

Archetypes è abbastanza intelligente da utilizzare un vocabolario Zope 3 solo passando il suo nome attraverso la proprietà vocabulary_factory.

Ora proviamo a rendere più interessante il nostro modulo implementando ciò che stiamo perdendo lasciandogli fare la ricerca attuale.

Senza alcun ulteriore lavoro. Possiamo rispedire (un nuovo indirizzo mail) quello che l’utente sta cercando alla nuova visualizzazione di ricerca standard Plone. Fatto questo, redigete il modulo come questo:

 

...

fromProducts.CMFCore.utils  import  getToolByName

 

...

 

Class RegionalNewsSearchForm(form.Form):

       fields=field.Fields(IRegionalNewsSearch)

       ignoreContext=True#don'tusecontexttogetwidgetdata

       label=u"SearchRegionalNews"

 

       @property

       Def portal(self):

               returngetToolByName(self.context,

                                                      'portal_url').getPortalObject()

 

        @button.buttonAndHandler(u'Search')

        def handleSearch(self, action):

               data,errors=self.extractData()

               if errors:

                    return

base_url="%s/search"%self.portal.absolute_url()

qstring="?portal_type=RegionalNews"

qstring+="&SearchableText=%s"%data['SearchableText']

qstring+="&region=%s"%data['region']

qstring+="&sort_on=effective&sort_order=descending"

self.request.response.redirect(base_url+qstring)     

 

 

RegionalNewsSearchView=wrap_form(RegionalNewsSearchForm)

 

 

 

Qui abbiamo appena permesso al metodo handleSearch della nostra classe regionalnewssearchform di rimappare i criteri forniti in una sequenza di richieste corrette e reindirizzare correttamente alla vista del portale di ricerca. È semplice se la vista di ricerca standard è abbastanza per voi.

Comunque, complicheremo un po’ la cosa: ricordate dalla sezione all’inizio del capitolo “Struttura di un Prodotto Plone”, una delle nostre esigenze era di creare la nostra vista di risultati specializzata.

Tornate alla finestra terminale e chiedete a Paster di costruire una nuova vista browser:

 

$ cd~/plonebook/src/plonebook.regional_news

$ paster addcontent view

 

Inserite rn_search_results come view_name, e poi aprite browser/configure.zcnl per cambiare il nome della vista predefinita in rn_search_results. Vogliamo il modello per fornire il criterio utilizzato per la ricerca e i risultati corrispondenti: fornirà un dizionario contenente i criteri scelti e una lista di dizionari corrispondente alle attuali notizie risultanti, entrambi creati dalla classe di vista rn_search_resultsView. Quindi modifichiamo il file browser/rn_search_resultsview.pt come questo:

 

<html xmlns="http://www.w3.org/1999/xhtml"xml:lang="en"

lang="en"

metal:use-macro="here/main_template/macros/master"

i18n:domain="plonebook.regional_news">

<body>

<div metal:fill-slot="main">

<tal:main-macro metal:define-macro="main">

 

<h1>Regional News Search Results</h1>

<p tal:define="criteria view/criteria">

         Selected Keys:<br />

         <tal:block tal:repeat="key criteria/keys">

            <strong tal:content="key">key</strong>:

            <span tal:replace="criteria/key">value</span>

            <span tal:condition="not:repeat/key/end">,</span>

    </tal:block>

</p>

<ul tal:define="resultsview/get_results">

    <li tal:repeat="item results">

         [<span tal:replace="item/region">]

         <a href=" " tal:attributes= "href item/url"

               tal:content="item/title"> item </a>

            </li>

         </ul>

      </tal:main-macro>

   < /div>

</body>

</html>

 

Ora per la parte interessante — aprite browser/rn_search_resultsview.py e inserire il seguente codice:

 

from Zope.interface import implements, Interface

 

from Products.Five import BrowserView

fromProducts.CMFCore.utils import getToolByName

 

from Plonebook.regional_news import regional_newsMessageFactory as _

 

class Irn_search_results View(Interface):

   " " "

   rn_search_results view interface

   " " "

   Def criteria():

          " " " return the criteria for our search as a Python dict.

          " " "

 

Def get_results():

       " " "return a list of dicts corresponding to the requested criteria.

       " " "

Class rn_search_resultsView(BrowserView):

       " " "

rn_search_results browser view

 

"""

implements(Irn_search_resultsView)

 

def __init__(self,context,request):

self.context=context

self.request=request

 

@property

def portal_catalog(self):

returngetToolByName(self.context,'portal_catalog')

 

@property

def portal(self):

returngetToolByName(self.context,'portal_url').getPortalObject()

 

def criteria(self):

request=self.request

criteria={}

criteria['SearchableText']=request.get('SearchableText','&nbsp;')

criteria['region']=request.get('region','&nbsp;')

return criteria

 

def get_results(self):

request=self.request

query={'portal_type':'RegionalNews',

'SearchableText':request.get('SearchableText'),

'region':request.get('region'),

'sort_on':'effective',

sort_order':'descending'}

brains=self.portal_catalog.searchResults(**query)

 

items=[]

for brain in brains:

items.append({'title':brain.Title,

'url':brain.getURL(),

'region':brain.getRegion})

 

return items

 

 

Il metodo dei criteri è abbastanza semplice: chiediamo l’oggetto request per i nostri nomi specifici, e se non ci sono, inviamo un carattere di spazio.

Per quanto riguarda il metodo get_results, notate che abbiamo utilizzato lo stesso esempio/campione che abbiamo utilizzato prima nella sezione del capitolo “Scelta del Tema”. Chiariremo meglio questa cosa nella prossima sezione. Nella Figura 9-7, potete vedere la vista rn_search_results in azione.

 

 

Figura 9-7. Il motore di ricerca Notizie Regionali funziona!

 

Comprendere il Plone Catalog

  

Come avete capito dai precedenti capitoli, lo strumento portal_catalog è uno dei servizi più utili in Plone. È utilizzato per ricerche standard nonché per l’elenco cartelle.

Il ZODB non offre alcun servizio originario di creazione indici sugli oggetti che memorizza. Cioè dove entrano in gioco i cataloghi Zope. Fondamentalmente  un catalogo indicizza le informazioni di un oggetto su richiesta, secondo una lista di indici desiderati, e immagazzina quelle informazioni per ogni singolo oggetto, usando una specie di segnaposto chiamato un cervello, che è  un oggetto già di per sé.

Sono disponibili differenti tipi di indice, dedicati a tipi specifici di dati e di query; per esempio, possiamo definire un semplice campo un indoce per immagazzinare semplici sequenze di dati, un indice di dati per interrogare campi di dati, un indice di percosrso per interrogare oggetti su una posizione base, e cosi via.

Naturalmente i cervelli memorizzano le informazioni dell’indice nel loro formato specifico per semplificare gli algoritmi di interrogazione.  Quindi, per mostrare i risultati di un interrogazione in un formato ­leggibile,avremmo bisogno di cercare l’oggetto reale che corrisponde ai cervelli derivanti.­ Ma è un operazione del tutto pesante, specialmente quando abbiamo bisogno di sapere che il reale oggetto è li e non lo utilizzeremo.

 

Questo perché con gli indici, i cataloghi Zope memorizzano anche i reali oggetti metadati nei cervelli: l’oggetto informazione annotato nel cervello nel momento di indicizzazione, ciò è utile per sviluppare pagine di ricerca risultati senza risvegliare l’oggetto reale.

 

NOTA Per una profonda comprensione dei cataloghi Zope, per favore leggere il capitolo del libro Zope “ricerca e categorizzazione del contenuto” (www.Zope.org/Documentation/Books/ZopeBook/2_6Edition/

SearchingZCatalog.stx).

 

Fin’ora è abbastanza facile capire che il servizio di catalogo base di Zope non ha traccia di disposizioni di sicurezza del vero oggetto, e che permette il pieno accesso a qualsiasi ruolo ammesso a richiedere il catalogo. Ma CMF e Plone hanno un catalogo Zope specializzato, capace di mettere in conto la configurazione della sicurezza, e quindi funziona tutto come dovrebbe.

Come ultimo avvertimento, ricordate che i cervelli non sono veri oggetti. Quindi, in alcuni casi, potreste incontrare un catalogo corrotto che è inconsistente rispetto ai veri oggetti. Non preoccupatevi: come con un database relazionale, probabilmente sarete capaci di ricostruire abbastanza facilmente il vostro catalogo utilizzando l’informazione dei veri oggetti.

Ora che avete visto la teoria, tornate al mondo reale, richiamando il nostro metodo di vista get_results e calcolado il suo codice. Come esempio, componiamo un dizionario con tutti i nostri attributi richiesti, e lo passiamo al metodo portal_catalog’s searchResults, che porta allo scoperto tutti i cervelli visibili all’utente che sta facendo la ricerca. Poi ripetiamo i risultati per costruire la lista di dizionari che il modello di pagina della vista mostrerà all’utente.

Come potete vedere, vogliamo mostrare l’informazione dell’attributo regione, che non è presente nello strumento portal_catalog come metadati di base di Plone. Quindi abbiamo bisogno di registrarlo come nuovo metadata, inserendo l’indirizzo http://localhost:8080/plone/portal_catalog/manage_catalogSchema, andando in fondo alla pagina, e aggiungendo i metadati getRegion.

Per fare questo lavoro, è necessario fornire il nome corrispondente al metodo di accesso dell’attributo regione: getRegion.

Questa veloce dimostrazione di z3c.form ha lasciato fuori molti aspetti importanti. Per una comprensione più approfondita dell’utilizzo di z3c.form in Plone, leggete il tutorial di Daniel Nouri su Plone.org, all’indirizzo http://plone.org/documentation/how-to/easy-forms-with-plone3.

 

 

 

 

Figura 9-6. Il modulo di ricerca Notizie Regionali

by Dario Pollino last modified 2011-06-23 18:33
Contributors: Maurizio Delmonte, Davide Moro, Alice Narduzzo, Fabrizio Reale, Enrico Barra, Andrea Cannizzaro, Andrea D'Este, Maurizio Lupo, Giuseppe Masili, Dario Pollino, Matteo Sorba.