Visualizzazione post con etichetta Python. Mostra tutti i post
Visualizzazione post con etichetta Python. Mostra tutti i post

mercoledì 24 giugno 2015

Dealing with elasticsearch reindex and haystack

Prerequisites: some knowledge on haystack and elasticsearch and obviously django are required.

When we're using e.g. synonyms or stopwords in our indices, we need to reindex our data in order to have the new settings on board. Elasticsearch documentation rises this problem and suggest how to fix, see Reindex Your Data. Well, let's do with haystack!

Suppose we have this haystack settings:

HAYSTACK_CONNECTIONS = {
    'default': {
        'ENGINE': 'search.backend.ElasticsearchEngineCustom',
        'URL': 'http://127.0.0.1:9200/',
        'INDEX_NAME': 'haystack',
        'BATCH_SIZE': 1000
    }
}

As you can see I created a custom backend, but it's not the focus on this article, please read the links below to get an idea:
As a sample, for this article the backend module looks like:

from haystack.backends.elasticsearch_backend import ElasticsearchSearchBackend

class ElasticsearchEngineBackendCustom(ElasticsearchSearchBackend):
    def __init__(self, connection_alias, **connection_options):
        super(ElasticsearchEngineBackendCustom, self).__init__(connection_alias, **connection_options)

        self.setup_complete = True

class ElasticsearchEngineCustom(ElasticsearchSearchEngine):
    backend = ElasticsearchEngineBackendCustom

Setting self.setup_complete to True avoids the 'put index' call from haystack setup and permits us to use INDEX_NAME as index alias name.

Now we have to manage all the haystack index setup via management commands overriding rebuild_index and creating the reindex_index command, let's do it.

In a module named utils, I created the current_index function that is an utility function that return the current index in use and the next version number for the current index (we'll use the number on management commands). Below the code:
 
INDEX_TEMPLATE = "{}_v{}"

def number_sequence():
    n = 0
    while True:
        yield n
        n += 1

def current_index(es_client, index_name):
    version = number_sequence()
    index = INDEX_TEMPLATE.format(index_name, version.next())

    if not es_client.indices.exists_alias(name=index_name):
        return INDEX_TEMPLATE.format(index_name, 0), 1

    while not es_client.indices.exists(index=index):
        index = INDEX_TEMPLATE.format(index_name, version.next())

    return index, version.next()

The guard for exists_alias is used to know if the rebuild_index has been run at least once. If no alias is present, there is no index. The version number added to the name start from 0. The default index, when the rebuild_index command is called will be haystack_v0.
An objection is the use of while not... because the function search from zero to current index version number and it could be a great number and for each number hits the elasticsearch; it could be more efficient having two indexes name. Take this as a "quick win" solution and improving it!

Let's go to write the reindex_index command, the steps are:
  • Delete the new index ignoring the errors
  • Create the new index with the current index settings and fields mapping
  • Reindex data from current index to new index
  • Zero downtime: remove from alias the current index and add the new index
  • Delete current index, that is old. The new one is used 
from elasticsearch import Elasticsearch
from elasticsearch.helpers import reindex
from django.core.management.base import BaseCommand
from django.conf import settings
from search.utils import current_index, INDEX_TEMPLATE

class Command(BaseCommand):

    def __init__(self):
        super(Command, self).__init__()

        self.es_client = Elasticsearch(hosts=settings.HAYSTACK_CONNECTIONS['default']['URL'])
        self.index_alias = settings.HAYSTACK_CONNECTIONS['default']['INDEX_NAME']
        self.current_index, version = current_index(self.es_client, self.index_alias)
        self.new_index = INDEX_TEMPLATE.format(self.index_alias, version)
        # Update settings with fields mapping
        self.index_settings = settings.ELASTICSEARCH_INDEX_SETTINGS
        self.index_settings.update(self.es_client.indices.get_mapping()[self.current_index])

    def handle(self, *args, **options):
        self.es_client.indices.delete(index=self.new_index, ignore=[404, 400])
        self.es_client.indices.create(index=self.new_index, body=self.index_settings)
        reindex(self.es_client, self.current_index, self.new_index)
        update_aliases = {
            "actions": [
                {"remove": {"index": self.current_index, "alias": self.index_alias}},
                {"add": {"index": self.new_index, "alias": self.index_alias}}
            ]
        }
        self.es_client.indices.update_aliases(body=update_aliases)
        self.es_client.indices.delete(index=self.current_index)

        print(u"Successfully reindex.")


Now we need to override rebuild_index command with:
  • Rewriting clear_index call
  • Creating the alias using the INDEX_NAME from haystack settings
    • delete all index that match haystack_v*
    • create the index "v0" with haystack settings
    • create the alias haystack to haystack_v0
  • Call the update_index
from elasticsearch import Elasticsearch

from django.conf import settings
from django.core.management import call_command
from haystack.backends.elasticsearch_backend import ElasticsearchSearchBackend
from haystack.management.commands import rebuild_index
from search.utils import INDEX_TEMPLATE

class Command(rebuild_index.Command):

    def __init__(self):
        super(Command, self).__init__()
        self.es_client = Elasticsearch(hosts=settings.HAYSTACK_CONNECTIONS['default']['URL'])
        self.index_name = INDEX_TEMPLATE.format(settings.HAYSTACK_CONNECTIONS['default']['INDEX_NAME'], 0)
        self.index_alias = settings.HAYSTACK_CONNECTIONS['default']['INDEX_NAME']

    def _create_index_alias(self):
        self.es_client.indices.delete(index=self.index_name.replace("0", "*"))
        self.es_client.indices.create(index=self.index_name,
                                      body=ElasticsearchSearchBackend.DEFAULT_SETTINGS)
        print(u"Created index {}".format(self.index_name))
        self.es_client.indices.put_alias(index=self.index_name, name=self.index_alias)
        print(u"Added index {}_v0 to alias {}.".format(self.index_name, self.index_alias))

    def handle(self, **options):
        self._create_index_alias()
        call_command('update_index', **options)

Instead of ElasticsearchSearchBackend.DEFAULT_SETTINGS you should use your elasticsearch settings. The links above explained how to make it.
A nice improvement is having the yes/no options, the rebuild command is destructive.

Any suggestions will be appreciated! That's it. Enjoy! :-)

Cheers

giovedì 18 giugno 2015

Django Haystack Elasticsearch: index pdf files

In this article I would like to explain how to index pdf files into a haystack elasticsearch backend and to follow you need some knowledge about django and haystack.
Elasticsearch configuration is not treated.

Beginning 

IMHO, haystack documentation is not very clear about "FileIndex", or better, yes but only for Solr backend, see Rich Content Extraction; for elasticsearch backend you need to get your hands dirty :-)

Issue
  • The pdf files for index are located into the django media directory under document folder and subfolders.

That we need
  1. Retrieve all files and put some data into a list of dictionaries
  2. A pdf file model, haystack requires a model in order to perform index
  3. A custom haystack elasticsearch backend, we need to override the extract_file_contents method
  4. A search_indexes.py file
Solving 1.

This is simple, walk through the directories and store the full path into a list of dictionaries.
I left the exercise to the reader.
The final result is to obtain a result like this:

[
  {"path": "/path/to/media/my_fantastic_pdf.pdf, "url": "media/url/my_fantastic_pdf.pdf"},
  {"path": "...", "url": "..."},
   ...
]

Solving 2.

First of all we need a model, not managed:

class PdfFileInfo(models.Model):
    path = models.CharField(max_length=250)
    url = models.CharField(max_length=250)

    objects = PdfFileInfoManager()

    def get_absolute_url(self):
        return self.url

    class Meta:
        managed = False

As you can see we don't have a real table, then we need to create a custom QuerySet and Manager in order to supply to this lack.
Searching around the net I've found this article how to quack like a QuerySet that explains how to have a copy of original django QuerySet and having some nice tricks.

Below the code of QuerySet:

class PdfFileInfoQuerySet(object):

    def __init__(self):
        # avoid circular dependencies
        from .models import PdfFileInfo

        self.pdf_files = []
        docs = retrieve_files()  # remember it's your homework :-P
        for pk, doc in enumerate(docs):
            doc['id'] = pk 
            self.pdf_files.append(PdfFileInfo(**doc))

    def __iter__(self):
        for pdf_file in self.pdf_files:
            yield pdf_file

    def __repr__(self):
        return repr(self.pdf_files)

    def __getitem__(self, k):
        if not isinstance(k, (slice, int, long)):
            raise TypeError
        assert ((not isinstance(k, slice) and (k >= 0))
                or (isinstance(k, slice) and (k.start is None or k.start >= 0)
                    and (k.stop is None or k.stop >= 0))), "Negative indexing is not supported."
        if isinstance(k, slice):
            return self.pdf_files[k]
        else:
            return self.pdf_files[k:k + 1][0]

    def count(self):
        return len(self.pdf_files)

    def all(self):
        return self._clone()

    def filter(self, *args, **kwargs):
        return self._clone()

    def exclude(self, *args, **kwargs):
        return self._clone()

    def order_by(self, *ordering):
        return self._clone()

    def _clone(self):
        qs = PdfFileInfoQuerySet()
        qs.pdf_files = self.pdf_files[:]
        return qs

Note on a pitfall: assign the pk to the model ensures that indexer will create all the documents into the index, otherwise it will create only one document (the last item).

And for the Manager:

class PdfFileInfoManager(models.Manager):
    def all(self):
        return PdfFileInfoQuerySet()

Solving 3.

Creating a custom backend... The "easy" part :-)
I choose pyPdf for extracting pdf contents. [Python recipe]

class ElasticsearchEngineBackendCustom(ElasticsearchSearchBackend):
    # ... 
    def extract_file_contents(self, file_obj):

        pdf = pyPdf.PdfFileReader(file_obj)

        content = ""
        for num_page in range(0, pdf.getNumPages()):
            content += pdf.getPage(num_page).extractText() + "\n"

        content = (" ".join(content.replace(u"\xa0", " ").strip().split())).encode("ascii", "xmlcharrefreplace")

        pdf_info = {
            'contents': content
        }

        return pdf_info

class ElasticsearchEngineCustom(ElasticsearchSearchEngine):
    backend = ElasticsearchEngineBackendCustom

You can find some other info about extending backend on my stackoverflow answer

Solving 4.

Cool, now we have the basis to build the index like haystack documentation says.

class FileIndex(indexes.SearchIndex, indexes.Indexable):
    # ...
    def prepare(self, obj):
        data = super(FileIndex, self).prepare(obj)

        extracted_data = self._get_backend(None).extract_file_contents(open(obj.path, "rb"))

        t = loader.select_template(('search/indexes/file_text.txt',))
        data['text'] = t.render(Context({'object': obj, 'extracted': extracted_data}))

        return data

    def get_model(self):
        return PdfFileInfo

    def index_queryset(self, using=None):
        return PdfFileInfo.objects.all()

The template search/indexes/file_text.txt is very simple:

{{ extracted.contents|striptags|safe }}

That's it, run the rebuild_index command and see indexer in action.

This is a working example, maybe require some adjustments for your purpose, e.g. I think that with a little effort you can index file if it's an "attachment file" in your django model.

Any suggestions will be appreciated!
Cheers!

martedì 10 luglio 2012

EuroPython 2012 - FTW!

Settimana scorsa si è svolta la conferenza europea di Python ed essendo fortemente appassionato al linguaggio, quest anno mi son organizzato per tempo per partecipare.

Ho selezionato le track da seguire, una bella dose di Flask, MongoDB, Plone e "Python white magic" :)

Da buon nerd e per vincere una maglietta ho risolto i vari "giochini ad incastro" (aka "cazzilli") proposti da Google e Spotify.
I "cazzilli" di Google mi hanno intrippato a tal punto che ho sentito il bisogno di risolverli tutti, una sfida personale :)

Cazzilli a parte è stata una settimana impegnata e ricca di incontri, difficile riassumere tutto in poche righe una settimana di evento!

L'organizzazione dell'evento è stata di alto livello, tanti complimenti, anche perché da fonti certe il prossimo anno si fa tris! :)

Sani

PS: tutti i video della conferenza sono già online! http://www.youtube.com/user/PythonItalia

venerdì 27 aprile 2012

domenica 22 aprile 2012

DjangoDay 2012 - Brescia

L'emozione di tenere un talk al primo djangoday italiano è stata fin troppo alta: "debito d'ossigeno".

Aspetto di vedere online il video, solo per vedere che faccia cianotica avevo :)
Spero solo di non aver detto castronerie, l'emozione ha vinto sulla mia preparazione... del resto era la mia prima volta con così tanta gente!

E' stata una bella conferenza, ricca di argomenti e di gente simpatica, guardate i video e le slide appena saranno online, c'è da imparare.

Bravi tutti, dagli organizzatori agli speaker, una "conferenza di livello" come si suol dire.

E poi alla fine, in semi disparte... Marco Beri mi ha fatto una domanda!!! Ho ancora gli occhi lucidi!!!

Qui trovate le mie slide e una "risposta": http://bit.ly/HXG1Xa

Ancora grazie al WebdeBS! Mi auguro ci siano ancora una e più edizioni!

Sani!

PS:
"Prendevi cura delle vostre palle e loro si prenderanno cura di voi" cit. Dodgeball
Nelle slide ci sono anche vari riferimenti alla "Guida galattica per autostoppisti", ma anche a codice morse, The Big Lebowski, Star Wars e ai Monty Python... a voi trovarle! ;)

sabato 11 giugno 2011

RubyDay 2011 - Milano

Bene, cosa spinge il sottoscritto, che predica di usare e imparare Python al RubyDay italiano?

La risposta è semplice: "la curiosità e la voglia di crescere"

Cominciamo allora!

KeyNote - Giovanni Intini

Ho visto i progressi e il cammino del linguaggio e della community italiana di Ruby.
Interessante notare che Ruby ha avuto un picco enorme con la creazione di RoR, per poi stabilizzarsi.
Altra cosa positiva è che sembra ci sia movimento e adozione del linguaggio da parte delle aziende, che si rendono conto che i vecchi modelli di business non sono più vantaggiosi.
Bello sentire che nella mia regione di nascita ci sia movimento! :-)

Ruby on Rails survaival guide of an aged Java developer - Gian Carlo Pace

Si può riassumere tutta la presentazione in due parole: resilienza e coesione.
Gian Carlo ha spiegato il perché di una scelta coraggiosa, quindi l'utilizzo di Ruby/RoR, per un progetto dove la barca si era ribaltata, da qui il termine resilienza; ma per far accadere tutto questo sono stati affrontati vari problemi.
Tra i tanti è che il team di sviluppo aveva conoscenze disomogenee (Java e Coldfusion), che il programmatore Java tende ad arroccarsi alle proprie conoscenze e che imparare un nuovo linguaggio non è banale, ma possibile.
Adottata però una metodologia per affrontarli, alla fine, il progetto in ritardo di un mese, è stato consegnato entro i termini e il team di sviluppo era unito e motivato, quindi coeso.

Ora sono un gruppo di felici sviluppatori Ruby!

Symfony2 and RoR3 friends for an hour - Alessandro Cinelli, Sandro Paganotti, Alberto Barillà

Bravi, sono riusciti in un'oretta a mostrare le differenze (molte) e similitudini (poche) tra i due framework.

Ero interessato soprattuto da Symfony2, visto che ne sentivo parlare molto, ma sono rimasto deluso.
Ho visto la disperazione negli occhi di @madchicken e anche mia, quando abbiamo visto che il datamapper che è molto simile ad Hibernate, con relative annotation e la configurazione delle varie route è fatta via xml/yaml e assomigliano molto a Spring.
Il template engine è Twigh, che è il template engine di Django portato su PHP e con @cgabriel ci siamo detti che il vantaggio è che puoi utilizzarlo anche con Django.
Delusione, pensavo di trovare una sfida almeno alla pari, ma sia l'architettura che il modo di sviluppare non ci azzeccano.
Mi spiace che Alessandro, da sviluppatore PHP/Symfony, abbia detto che PHP non sia poi così buono e che gli sviluppatori PHP vanno insultati.
Vero, stanno utilizzando PHP come se fosse Java, e peccato dirlo, non sta uscendo nulla di buono per PHP... forse Yii o Lithium...
Apprezzabile che ci sia una community di bravi e diligenti sviluppatori PHP che si poongono a tutta la schiera di scribani e fuffari.
RoR3, nulla da dire, l'ho trovato semplice da configurare, poi da utilizzare... dovrei provarlo!
Una cosa non mi è piaciutua di Sandro, non so perché è saltato fuori con questa frase, ma dice che Python non ha un unico framework per lo sviluppo web, come lo ha Ruby, quindi c'è frammentazione.
Vero, ma a mio avviso, la frammentazione non c'è, la maggior parte dei progetti Python per il web ha una buona community e sono anche documentati!
Sinatra, ad esempio, assomiglia a Flask, ma non mi devo portar dietro altri framework, come invece lo è per Ruby, visto che necessita sempre di RoR, dettagli.
Qui si potrebbe discutere un sacco, alla fine usare uno o l'altro è questione di gusto e di soluzione veloce per il problema che ho sotto mano.
Il "chi ce l'ha più duro" è sempre in agguato!

A metaprogramming Spell Book for Ruby and Rails - Paolo Perrotta

Complimenti, il buon Paolo mi ha aperto gli occhi sul linguaggio Ruby e tante di quelle magie che ci ha fatto vedere, alla fine sono solo codice Ruby.
Il suo libro è da leggere, per me è diventato il libro per imparare Ruby, altri non ce ne sono.
Mi spiace che ero in prima fila e ho tentato di distrarlo con una supercazzola, ma era dopo pranzo, mi ero perso un attimo in pensieri e mi mancava un pezzo di discorso
;-)
D'altra parte l'ho fatto solo in conto della nostra amicizia! :-)

Dopo la sua presentazione, Paolo si è messo a far il maestro con carta e penna nell'atrio e a dispensare esempi, fantastico!
Poi ho seguito a singhiozzo gli altri interventi, stavo accusando la giornata.
Ricapitolo brevemente.

"Progettare e sviluppare mobile web..." era già stata presentata al whyMCA, purtroppo però ci si è focalizzati di più su Sencha che su Sinatra.
Un po' spenta come presentazione, in ogni caso, bravi Matteo Collina e Daniele Bottillo (non mi ricordo chi c'era dei due) che hanno sviluppato un'applicazione per mobile cross platform e resa disponibile sui vari app store.

Mi sono fatto una cultura sul testing con RSpec grazie a Simone Carletti (che va a profilare anche i test!) e su continuous integration/deployment con Sam Reghenzi.

Forse sono stato breve e pecco in dettagli, vi consiglio uno sguardo su http://www.rubyday.it/ dove penso saranno caricati i link dei video delle presentazioni e slide.

Dimenticavo!
A fine giornata c'è stata l'estrazione dei premi e io, dopo aver più o meno risolto l'enigma proposto, all'unanimità di voti, ho vinto una licenza di Ruby Mine!

Bello Ruby!
Ora non mi resta che iniziare a scrivere!!! :D

Sani

martedì 8 marzo 2011

"Dumb" SMTP server

Lo snippet seguente permette di ricevere email e di mostrarle in un terminale; dette email non verranno inviate, ma solamente visualizzate.


python -m smtpd -n -c DebuggingServer localhost:1025


Ricordo che in un ambiente *nix like, le porte fino alla 1024 sono privilegiate, quindi bisogna 'sudarsele' ;).

Sani

venerdì 14 gennaio 2011

Tips'n'Tricks: Django, autocomplete e virtualenv

Appunti di viaggio su virtualenv/virtualenvwrapper e Django.

Esportare i settings del progetto in Django
Nell'ambiente virtuale creato editare il file postactivate.

$ workon virtualenvironment
$ cdvirtualenv
$ vim bin/postactivate


e aggiungere:

export DJANGO_SETTINGS_MODULE=virtualenvironment.settings


Poi, si potrebbe aggiungere anche l'autocomplete in bash.
Scaricare django_bash_completion dal trunk.

Mettere il file scaricato all'interno della directory bin dell'ambiente virtualenv e modificare il file postactivate aggiungendo la seguente riga.

. django_bash_completion


Il punto è un alias di source.

HTH,
Sani

giovedì 13 gennaio 2011

Tips'n'Tricks: python2.4, virtualenv e sqlite

Come installare e usare sqlite con python2.4.

Supponendo un virtualenvwrapper configurato e una macchina Ubuntu:

$ sudo apt-get install libsqlite3-dev
$ workon virtualenv
$ pip install pysqlite


HTH,
Sani

mercoledì 12 gennaio 2011

Tips'n'Tricks: virtualenv e MySQL

Come installare le librerie mancanti per utilizzare MySQL in un ambiente virtualenv.

Supponendo di avere virtualenvwrapper configurato e una macchina con Ubuntu:


$ sudo apt-get build-dep python-mysqldb
$ workon virtualenv-environments
$ pip install MySQL-python


HTH,
Sani

martedì 11 gennaio 2011

Ubuntu: installare le vecchie versioni di Python

Un po' di pazienza e un po' di google-fu, ho trovato un progetto con relativo repository ppa, che mantiene i pacchetti deb delle vecchie versioni di Python per Ubuntu.

$ sudo add-apt-repository ppa:fkrull/deadsnakes
$ sudo apt-get update
$ sudo apt-get install python2.4 python2.4-dev python2.5 python2.5-dev


Dopodiché si possono utilizzare con virtualenv utilizzando l'opzione --python per indicarne la versione, ad esempio:

$ mkvirtualenv --no-site-packages --python=python2.4



Link e risorse:

https://launchpad.net/~fkrull/+archive/deadsnakes



HTH,
Sani

lunedì 10 gennaio 2011

Ubuntu: setup di un 'virtualenv environment'

Appunti di viaggio per installare e configurare un ambiente Python con virtualenv/virtualenvwrapper.


Prima di tutto installare pip, virtualenv e virtualenvwrapper

$ sudo apt-get install python-virtualenv python-pip
$ mkdir ~/.virtualenvs
$ sudo pip install virtualenvwrapper


Esportiamo le variabili d'ambiente in ~/.bashrc, editando il file con il nostro editor preferito vim ;)

export WORKON_HOME=$HOME/.virtualenvs
source /usr/local/bin/virtualenvwrapper.sh
export PIP_VIRTUALENV_BASE=$WORKON_HOME


Set ed export del PYTHONPATH nell'ambiente virtualenv editando il file /.virtualenvs/postactivate

export PYTHONPATH=$PYTHONPATH:$VIRTUAL_ENV


Elenco dei comandi di base, con una piccola descrizione.

$ workon myenv # setta l'attuale ambiente virtualenv (myenv)

$ deactivate # Disattiva l'ambiente corrente

$ mkvirtualenv --no-site-packages myenv # Crea un ambiente virtualenv pulito

$ rmvirtualenv myenv # Rimuove l'ambiente

$ cdvirtualenv # Va alla directory dell'ambiente attivo

$ lssitepackages # Elenco dei packages installati nell'ambiente




Setup di un ambiente di esempio:

$ mkvirtualenv --no-site-packages myenv
$ cdvirtualenv



Link e risorse:
http://www.doughellmann.com/projects/virtualenvwrapper/
http://pip.openplans.org/
http://mathematism.com/2009/jul/30/presentation-pip-and-virtualenv/


HTH,
Sani

mercoledì 5 gennaio 2011

Propositi per l'anno 2011

Ieri un amico in chat mi ha chiesto quali sono i miei propositi per il nuovo anno e la mia risposta secca e divertente è stata: 'cambiare lavoro ;)'.
Scherzi a parte, la battuta l'ho fatta perché, vuoi per diletto, vuoi per scelte tecnologiche o forse perché ho ancora voglia di mettermi in gioco, cambio lavoro (datore) più o meno con la stessa frequenza dei rilasci stabili di Ubuntu.

BTW, come si usa, i miei propositi sono per quest anno primo:

  • Non unirmi a loro, ma combatterli, si veda Bastian contrario.

  • Scrivere di più e in italiano 'robe tenniche' nel blog, post in inglese ce ne sono già tanti. Vorrei conservare il mio cervello in Italia, sperando non fugga!

  • Rilasciare qualche riga di codice utile su bitbucket/github...

  • Andare in produzione online con un progetto in Django.

  • /Leggere/Studiare/ Seven Languages in Seven Weeks e qualcosa su mobile e HTML5, CSS3 ecc (ho una serie di libri sulla scrivania...)

  • Utilizzare il più possibile Free e Open Source Software.

  • Perseverare sul fatto che le porte vanno chiuse, altrimenti fa corrente... ma 'sta gente a casa le tiene sempre aperte? :S

  • Varie ed eventuali...

  • Ultimo e non per importanza, ma per sostanza... Cambiare lavoro! :D



Sani

sabato 19 dicembre 2009

Django - delete logico

A dire il vero può essere scontato come fare... però visto che c'ho sbattuto la testa voglio condividere con i miei lettori questa 'soluzione soffritta' :) (restando in tema con il libro di Marco).

Creiamo un Model con override su 'delete':


class Customer(models.Model):
email = models.EmailField()
nickname = models.CharField(max_length=50)
deleted = models.BooleanField(default = False)

def delete(self):
self.deleted = True
self.save()


Poi in admin.py creiamo un ModelAdmin relativo al nostro 'Customer':


class CustomerAdmin(admin.ModelAdmin):
search_fields = ['nickname']
exclude = ('deleted',)

def queryset(self, request):
return super(CustomerAdmin, self).queryset(request).filter(deleted=False)


Fatto.
Ora nella vista in admin saranno presenti solamente i 'Customer' non cancellati logicamente, sia che venga mostrato l'elenco, sia che venga effettuata una ricerca.

Vi rimando per approfondimenti alla documentazione di Django e ad altri due link interessanti:
Django Trip: Ready, Set, Go
Is there a way to filter a queryset in the django admin?

Ho messo meno tempo a scrivere questo post che a trovar la soluzione! :D

Sani

sabato 18 ottobre 2008

pyInstantShooter2.0

Ho scritto questa splendida applicazione che farà felice ngw, micampe, Zeff, Sbetti, BrC, 'Madness' e tutti quelli che sanno cosa significa essere vittima di un rimshot...

Dimenticavo: l'applicazione si trova qui. Eh già... Strap.it - Strike Back! (forse manca la S... la lascio qua no? :D)

Sani

domenica 8 giugno 2008

Poi dopo leggo anche...

Stacktrace, visto che oggi piove e non c'ho voglia di uscire... e mi ritorna quella voglia di fare... quella speranza che un giorno sarà migliore... che poetico... certe volte mi faccio schifo... quasi sempre... :D



Sani

martedì 20 maggio 2008

E Flickr Foo... (solo per intenditori)

Ed ecco le quattro foto che ho scattato al PyConDue.
Devo caricare anche quelle da turista... ma c'è tempo... mi sono appena ora appassionato di fotodigitalcazziemazzi...

Sani

martedì 13 maggio 2008

Mi suda il cervello!

Lo scorso fine settimana ho partecipato al PyCon a Firenze. Tre giorni di puro Python!
In realtà venerdì c'è stato solo l'evento di apertura con il keynote di Stallman, che purtroppo ho perso...
Niente paura... ho recuperato alla PyFiorentina incontrando vecchi amici e facendo nuove amicizie.
Le giornate di sabato e domenica sono state davvero entusiasmanti... forse perché ho scelto di seguire i talk giusti :), Plone, Zope, Twisted, Django, Google App Engine... Pazzesco cosa si riesce a fare!
Uno dei talk che mi ha colpito è stato "Falling in love with... Zope 3" dove Fabio Tranchitella ha illustrato come sono riusciti a realizzare un sito di incontri (Love is Match.com).
Ho seguito con una grande attenzione anche i talk di Raymond Hettinger (The savory new flavor of Py2.6 e Py3.0), di Brian Fitzpatrick (Do you believe in the users?) e di Alex Martelli (Google App Engine).
C'è stata una parentesi divertente al talk di Fitzpatrick: ha chiesto al pubblico chi utilizzasse emacs, poi vi e alla fine ha detto: "Fight!" LOL...
Credo di essermi innamorato di Plone/Zope... con un po' di training e un paio di progetti 'semplici' credo di riuscire a fare magie e mollare il mattone Magnolia, con il quale riesco a fare molte cose, molte delle quali però sono sviluppate a 'bestemmie e calci in culo'... purtroppo non sono io a scegliere... per ora :)
Anche l'interesse su Django è aumentato, soprattuto perché la versione in trunk ha dei miglioramenti rispetto alla 0.96 stabile (ad esempio l'implementazione delle form, dei bugfixing per la parte di amministrazione... diciamo che il class Admin: pass era passabile solo per il talk... i relatori hanno gridato "QUESTE COSE NON SI FANNO BAMBINI! NON GUARDATE!" LOL)
Lo stand dell'O'Reilly ha avuto molto successo... libri al 30% di sconto... mi sono portato a casa (contrariamente a quanto mi avevano consigliato) Programming Python, Bash Cookbook e Java Poket Guide... ora non resta che iniziare a 'codare'.
Oltre alla 'sudorazione cervellare' che l'aria condizionata ha provveduto ad asciugare ho incontrato amici (dimentico sempre... io non sono loro amico :)) che non vedevo da tanto, forse troppo tempo e mi ha fatto veramente piacere; spero che non passino ancora degli anni prima di rivederci :).
Bhe... ho scritto anche troppo... per 'chi sono/chi non sono' i personaggi che hanno tenuto i vari talk rimando al sito di pycon.it.
Appena carico le foto su flickr posto il link... ci sono anche quelle classiche da turista ;)... e poi non sono riusito a scattare in tempo per prendere le slide di "Programmer expet meritocracy" e "Oh. My. God", avevo i riflessi di un bradipo, al talk di Fitzpatrick; metterò un link o il video direttamente qui appena viene pubblicato.
Ho anche una discreta scorta di adesivi... la maglietta del PyCon, quella di Google... roba da nerd! :)
Abbuffarsi al buffet, lustrarsi gli occhi con le due hostess... miiiinchia!

Segmentation fault
Core dump
Reset

Alla fine come i Black Sabbath vi lascio con un misterioso messaggio... pythonico... no AC! Non è un pangramma! :D

- Google usa Python... perché non usarlo?
- Non c'è mercato, niente Python
- Aaah... ok... ho capito... :S

Sempre a far polemica e lamentarsi... RULEZ!!!

Sani