# Τεκμηρίωση του Django - 1.10.7

# Γράφοντας το πρώτο σας Django app, μέρος 7

Καλώς ορίσατε στο τελευταίο μέρος του οδηγού (tutorial) του Django! Ο οδηγός αυτός ξεκινά εκεί που τελειώνει ο οδηγός 6. Συνεχίζουμε με την εφαρμογή μας (Web-poll) και αυτή τη φορά θα επικεντρωθούμε στην παραμετροποίηση του διαχειριστικού site του Django το οποίο δημιουργείται αυτόματα για εσάς. Το Django admin το συναντήσαμε πρώτα στον Οδηγό 2.

## Παραμετροποιώντας την admin form

Κάνοντας register το `Question` model με το Django admin (`admin.site.register(Question)`), το Django ήταν σε θέση να κατασκευάσει μια προεπιλεγμένη εμφάνιση της φόρμας του μοντέλου για επεξεργασία. Με άλλα λόγια σας δίνει τη δυνατότητα να δημιουργείτε, επεξεργάζεστε, ανανεώνετε και διαγράφετε entries στη βάση δεδομένων σας (υπενθυμίζουμε ότι κάθε instance του μοντέλου σας αναπαριστά και μια γραμμή -entry, row- στον αντίστοιχο πίνακα της database) χωρίς τη χρήση της κονσόλας (γραμμής εντολών). Πολύ συχνά θα θέλετε να επεξεργαστείτε την εμφάνιση της φόρμας αυτής καθώς και τον τρόπο που λειτουργεί. Για να το πετύχετε αυτό δεν έχετε παρά να πείτε στο Django τις επιλογές-ρυθμίσεις που θέλετε, για αυτό το μοντέλο, την ώρα που κάνετε register το μοντέλο σας με το Django admin.

Ας δούμε πως δουλεύει αναδιοργανόνοντας τα πεδία (fields) στην φόρμα επεξεργασίας (edit form). Μέσα στο αρχείο `polls/admin.py`, αντικαταστήστε την γραμμή `admin.site.register(Question)` με αυτό το κομμάτι κώδικα:

polls/admin.py

    from django.contrib import admin

    from .models import Question

    class QuestionAdmin(admin.ModelAdmin):
        fields = ['pub_date', 'question_text']

    admin.site.register(Question, QuestionAdmin)

Θα ακολουθείτε αυτή τη διαδικασία (αρχικά θα δημιουργείτε μια model admin class και έπειτα θα περνάτε αυτή τη κλάση ως δεύτερο όρισμα στη μέθοδο `admin.site.register()`) κάθε φορά που θέλετε να αλλάξετε τυχόν επιλογές στο admin (admin options) για ένα μοντέλο.

Αν θυμάστε, πριν την αλλαγή αυτή, στη φόρμα επεξεργασίας εμφανιζόταν πρώτα το πεδίο της ερώτησης και μετά το πεδίο της ημερομηνίας επειδή τα είχαμε δηλώσει με αυτή τη σειρά στο μοντέλο `Question`. Τώρα, παρακάμπτουμε αυτή τη σειρά και πλέον εμφανίζεται πρώτα το πεδίο της ημερομηνίας (“Publication date”) και κατόπιν το πεδίο της ερώτησης (“Question”):

Αυτό δεν είναι και τόσο εντυπωσιακό με μόλις δύο πεδία, αλλά για φόρμες admin (admin forms) με δεκάδες πεδία, η επιλογή της σειράς εμφάνισης αποτελεί σημαντική λεπτομέρεια.

Και μιλώντας για φόρμες με δεκάδες πεδία, ίσως να θέλετε να διαχωρίσετε τη φόρμα σε πεδία φορμών (fieldsets):

polls/admin.py

    from django.contrib import admin

    from .models import Question

    class QuestionAdmin(admin.ModelAdmin):
        fieldsets = [
            (None,               {'fields': ['question_text']}),
            ('Date information', {'fields': ['pub_date']}),
        ]

    admin.site.register(Question, QuestionAdmin)

Το πρώτο element από κάθε tuple μέσα στη λίστα `fieldsets` είναι ο τίτλος του fieldset. Δείτε πως θα εμφανίζεται τώρα η φόρμα:

## Προσθήκη συσχετισμένων objects

Ωραία, έχουμε την σελίδα διαχείρισης του Question model, αλλά ένα `Question` έχει πολλαπλά `Choice`s και η φόρμα δεν εμφανίζει αυτές τις επιλογές.

Ακόμα.

Υπάρχουν δύο τρόποι να λυθεί αυτό το πρόβλημα. Ο πρώτος τρόπος είναι να κάνετε register το μοντέλο `Choice` με το admin, όπως ακριβώς κάναμε με το μοντέλο `Question`. Αυτό είναι πανεύκολο:

polls/admin.py

    from django.contrib import admin

    from .models import Choice, Question
    # ...
    admin.site.register(Choice)

Μετά από αυτό, το μοντέλο `Choice` απέκτησε τη δική του διαχειριστική φόρμα (admin form). Επομένως, τα πεδία της φόρμας θα εμφανιστούν με τη σειρά που δηλώθηκαν στο ίδιο το μοντέλο (εφόσον δεν κάναμε καμία αλλαγή μέσα στο `admin.py` όπως κάναμε για το `Question`). Η φόρμα θα δείχνει κάπως έτσι:

Σε αυτή τη φόρμα, το πεδίο “Question” αναπαριστάται από ένα HTML select box το οποίο περιέχει κάθε ερώτηση μέσα στη βάση δεδομένων. Το Django γνωρίζει ότι η κλάση `ForeignKey` θα πρέπει να αναπαρασταθεί ως ένα `<select>` box μέσα στην admin form. Στη δικιά μας περίπτωση, μόνο μια ερώτηση θα εμφανιστεί σε αυτό το σημείο (εκτός κι αν έχετε δημιουργήσει και άλλες).

Επίσης προσέξτε το link “Προσθήκη Καινούγιου <όνομα_μοντέλου>” (με το σύμβολο της πρόσθεσης) δίπλα από το πεδίο “Question.” Κάθε object με ένα πεδίο `ForeignKey` λαμβάνει αυτή την ευκολία από προεπιλογή. Όταν κλικάρετε πάνω σε αυτό το link, θα δείτε ένα popup παράθυρο με τη φόρμα επεξεργασίας του `Question` μοντέλου (αυτή που είδαμε πριν). Αν προσθέσετε μια ερώτηση σε αυτό το πράθυρο και κλικάρετε “Αποθήκευση”, το Django θα αποθηκεύσει την ερώτηση στη βάση δεδομένων και έπειτα θα εμφανίσει αυτή την ερώτηση ως επιλεγμένη (selected) στο `<select>` box.

Όμως αυτός ο τρόπος προσθήκης επιλογών (`Choice` objects) δεν είναι και ο πιο αποτελεσματικός που υπάρχει. Δε θα ήταν πιο βολικό αν δημιουργούσατε τα ``Choice``s κατά τη διάρκεια δημιουργίας των ερωτήσεων; Ας το υλοποιήσουμε.

Αφαιρέστε την κλήση της `register()` με το μοντέλο `Choice`. Έπειτα, επεξεργαστείτε τον registration κώδικα του `Question` για να γίνει έτσι:

polls/admin.py

    from django.contrib import admin

    from .models import Choice, Question

    class ChoiceInline(admin.StackedInline):
        model = Choice
        extra = 3

    class QuestionAdmin(admin.ModelAdmin):
        fieldsets = [
            (None,               {'fields': ['question_text']}),
            ('Date information', {'fields': ['pub_date'], 'classes': ['collapse']}),
        ]
        inlines = [ChoiceInline]

    admin.site.register(Question, QuestionAdmin)

Αυτό λέει στο Django: “Όλα τα `Choice` objects θα επεξεργάζονται στην διαχειριστική σελίδα του μοντέλου `Question`. Από προεπιλογή, να παρέχεις 3 choices”.

Επισκεφτείτε, πάλι, τη σελίδα “Add question” για να δείτε πως φαίνεται:

Δουλεύει κάπως έτσι: Υπάρχουν τρία slots για τα συσχετισμένα Choices – όπως δηλώθηκε μέσω του attribute `extra` – και κάθε φορά που επισκέπτεστε τη σελίδα ενός ήδη υπάρχοντος object, παίρνετε άλλα τρία επιπλέον slots. Όταν όμως επισκέπτεστε για πρώτη φορά τη σελίδα (δημιουργία νέου object) θα λαμβάνετε τα πρώτα τρία slots.

Στο τέλος των τριών slots θα βρείτε την επιλογή “Προσθήκη καινούργιου Choice”. Αν κλικάρετε πάνω σε αυτό, ένα νέο slot θα δημιουργηθεί. Αν θέλετε να το αφαιρέσετε κλικάρετε πάνω στο X που βρίσκεται πάνω δεξιά στο slot. Υπόψιν ότι δεν μπορείτε να αφαιρέσετε τα αρχικά τρία slots. Η παρακάτω εικόνα δείχνει ένα πρόσθετο slot (κάτω-κάτω):

Υπάρχει όμως ένα μικρό πρόβλημα. Καταλαμβάνει πολύ χώρο στην οθόνη η εμφάνιση των slots. Για αυτό το λόγο το Django προσφέρει έναν πιο περιεκτικό τρόπο εμφάνισης (tabular) για την εμφάνιση των inline συσχετισμένων objects. Το μόνο που έχετε να κάνετε είναι αλλάξετε τον ορισμό του `ChoiceInline` σε:

polls/admin.py

    class ChoiceInline(admin.TabularInline):
        #...

Με αυτό, `TabularInline` (αντί του `StackedInline`), τα συσχετισμένα objects εμφανίζονται πιο συμπυκνωμένα, σαν να βρίσκονται μέσα σε ένα πίνακα:

Σε αυτή τη μορφή υπάρχει και μια επιπλέον στήλη (“Θέλετε να πραγματοποιηθεί διαγραφή?”) η οποία σας επιτρέπει να διαγράψετε γραμμές που προστέθηκαν με την επιλογή “Προσθήκη καινούργιου Choice”. Σημειώστε ότι πάντα θα έχετε το ελάχιστο τρεις επιλογές (ρύθμιση `extra` μέσα στο `ChoiceInline`).

## Παραμετροποιώντας την admin change list

Τώρα που η διαχειριστική σελίδα του μοντέλου Question δείχνει όμορφη, ας κάνουμε μερικές αλλαγές στην σελίδα “change list” – αυτή που εμφανίζει όλες τις ερωτήσεις μέσα στο σύστημα (σαν μια λίστα).

Δείτε πως φαίνεται σε αυτό το σημείο:

Από προεπιλογή, το Django εμφανίζει τη μέθοδο `str()` για κάθε object. Αλλά μερικές φορές θα ήταν πιο βολικό να εμφανίζουμε μεμονομένα πεδία. Για να το κάνουμε αυτό θα χρησιμοποιήσουμε την επιλογή-ρύθμιση `list_display`, η οποία είναι ένα tuple από ονόματα πεδίων (του μοντέλου σας), τα οποία θα εμφανιστούν ως στήλες σε αυτή τη σελίδα:

polls/admin.py

    class QuestionAdmin(admin.ModelAdmin):
        # ...
        list_display = ('question_text', 'pub_date')

Για λόγους πληρότητας ας συμπεριλάβουμε την μέθοδο του μοντέλου `Question` `was_published_recently()` από τον Οδηγό 2:

polls/admin.py

    class QuestionAdmin(admin.ModelAdmin):
        # ...
        list_display = ('question_text', 'pub_date', 'was_published_recently')

Τώρα η σελίδα change list του Question δείχνει κάπως έτσι:

Μπορείτε να κλικάρετε στους headers κάθε στήλης για να ταξινομήσετε την αντίστοιχη στήλη κατά τις τιμές της. Μοναδική εξαίρεση στη δυνατότητα της ταξινόμηση μιας στήλης αποτελεί η περίπτωση του header `was_published_recently`, επειδή η ταξινόμηση με βάση την έξοδο μιας μεθόδου (ενός μοντέλου) δεν υποστηρίζεται. Επίσης, σημειώστε ότι ο header της στήλης `was_published_recently` είναι, από προεπιλογή, το όνομα της μεθόδου (με τα κενά να αντικαθιστούν τις κάτω παύλες) και κάθε κελί της στήλης αυτής περιέχει την έξοδο της συγκεκριμένης μεθόδου (True ή False).

Μπορούμε να βελτιώσουμε την όλη αυτή εμφάνιση παρέχοντας μερικά attributes στην μέθοδο αυτή (μέσα στο αρχείο `polls/models.py`) ως ακολούθως:

polls/models.py

    class Question(models.Model):
        # ...
        def was_published_recently(self):
            now = timezone.now()
            return now - datetime.timedelta(days=1) <= self.pub_date <= now
        was_published_recently.admin_order_field = 'pub_date'
        was_published_recently.boolean = True
        was_published_recently.short_description = 'Published recently?'

Για περισσότερες πληροφορίες σχετικά με τα properties των μεθόδων, δείτε στο `list_display`.

Επεξεργαστείτε το αρχείο `polls/admin.py` για ακόμη μια φορά και προσθέστε ένα φίλτρο στη σελίδα change list του μοντέλου `Question` χρησιμοποιώντας το `list_filter`. Προσθέστε την ακόλουθη γραμμή κώδικα στην κλάση `QuestionAdmin`:

    list_filter = ['pub_date']

Αυτό προσθέτει μια πλευρική στήλη (sidebar) “Filter” η οποία επιτρέπει το φιλτράρισμα των αποτελεσμάτων στην σελίδα change list κατά το πεδίο `pub_date`:

Ο τύπος του φίλτρου που εμφανίζεται εξαρτάται από τον τύπο του πεδίου που προσπαθείτε να φιλτράρετε. Επειδή το πεδίο `pub_date` είναι τύπου `DateTimeField`, το Django ξέρει ότι πρέπει να του δώσει τις ανάλογες επιλογές για φιλτράρισμα, όπως: “Οποιαδήποτε ημερομηνία”, “Σήμερα”, “Τελευταίες 7 μέρες”, “Αυτόν το μήνα”, “Αυτόν το χρόνο”.

Αρχίζει και παίρνει σχήμα η όλη σελίδα μας. Ας προσθέσουμε τη δυνατότητα του searching:

    search_fields = ['question_text']

Αυτό προσθέτει ένα search box κοντά στην κορυφή της σελίδας change list. Όταν κάποιος πληκτρολογήσει κάποια λέξη κλειδί για εύρεση, το Django θα ψάξει μέσα στο πεδίο `question_text`. Μπορείτε να χρησιμοποιήσετε όσα πεδία θέλετε – όμως επειδή η μέθοδος αυτή κάνει χρήση του `LIKE` query στο παρασκήνιο, αν περιορίσετε τον αριθμό των search fields μέσα στο πεδίο `search_fields`, τότε θα κάνει τη δουλειά της database σας πιο εύκολη στην εύρεση της λέξης κλειδί που δώσατε.

Τώρα είναι μια καλή στιγμή να σημειώσουμε ότι η σελίδες change lists σας δίνουν δωρεάν τη δυνατότητα του pagination. Από προεπιλογή, το Djoango εμφανίζει 100 γραμμές ανά σελίδα. Τα attributes `Change list pagination`, `search boxes`, `filters`, `date-hierarchies` και `column-header-ordering` συνεργάζονται μεταξύ τους όπως ακριβώς νομίζετε.

## Παραμετροποιώντας την εμφάνιση του admin site

Βασικά, η λέξη “Διαχείριση Django” στην κορυφή κάθε σελίδας του admin είναι γελοία. Στην πραγματικότητα είναι ένα κείμενο placeholder.

Αυτό αλλάζει πανεύκολα ούτως ώστε να γράφει κάτι που να ταιριάζει με το δικό σας διαχειριστικό site. Θα χρησιμοποιήσουμε το σύστημα των templates του Django. Το Django admin δεν είναι τίποτε άλλο παρά άλλο ένα application και δεν ακολουθά καμία άλλη template λογική παρά αυτή που θα δούμε παρακάτω (template inheritance κλπ).

### Παραμετροποιώντας τα templates του project σας

Δημιουργήστε έναν φάκελο `templates` μέσα στο root φάκελο του project σας (αυτόν που περιέχει το αρχείο `manage.py`). Τα templates μπορούν να υπάρχουν οπουδήποτε μέσα στο filesystem σας αρκεί το Django να έχει πρόσβαση σε αυτά. (Το Django τρέχει υπό τον χρήστη που τρέχει και τον server σας.) Ωστόσο, το να κρατάτε τα templates σας μέσα στο project σας είναι μια, γενικά, καλή πρακτική.

Ανοίξτε το αρχείο ρυθμίσεων-επιλογών του project σας (settings file, `mysite/settings.py`) και προσθέστε την επιλογή `DIRS` μέσα στην επιλογή `TEMPLATES`:

mysite/settings.py

    TEMPLATES = [
        {
            'BACKEND': 'django.template.backends.django.DjangoTemplates',
            'DIRS': [os.path.join(BASE_DIR, 'templates')],
            'APP_DIRS': True,
            'OPTIONS': {
                'context_processors': [
                    'django.template.context_processors.debug',
                    'django.template.context_processors.request',
                    'django.contrib.auth.context_processors.auth',
                    'django.contrib.messages.context_processors.messages',
                ],
            },
        },
    ]

Η επιλογή `DIRS` είναι μια λίστα με strings. Κάθε string είναι η διαδρομή (path) η οποία καταλήγει σε κάποιο φάκελο που περιέχει τα templates σας. Όταν το Django φορτώνει τα templates θα ψάξει και σε αυτά τα paths για να βρει τα templates σας. Με άλλα λόγια αποτελεί ένα μονοπάτι ευρέσεως των templates.

Οργανώνοντας τα templates

Όπως και με τα static files, θα μπορούσαμε να έχουμε όλα τα templates συγκεντρωμένα σε ένα (μεγάλο) φάκελο χωρίς κανένα απολύτως πρόβλημα. Ωστόσο, μια καλή πρακτική είναι να τοποθετείτε τα templates που ανήκουν σε ένα συγκεκριμένο application, στο φάκελο templates του application (π.χ `polls/templates`) παρά στο φάκελο templates του project σας (`templates`). Στο άρθρο οδηγός για προχωρημένους: Πως να γράψετε επαναχρησιμοποιήσιμα apps εξηγούμε γιατί να ακολουθήσετε αυτή την πρακτική.

Δημιουργήστε, τώρα, ένα φάκελο με όνομα `admin` μέσα στο φάκελο `templates` και κάντε αντιγραφή-επικόλληση το αρχείο template `admin/base_site.html` το οποίο θα βρείτε μέσα στον πηγαίο κώδικα του Django (`django/contrib/admin/templates`).

Που είναι ο πηγαίος κώδικας του Django?

Αν δυσκολεύεστε να βρείτε τον πηγαίο κώδικα του Django μέσα στο σύστημα σας, τρέξτε την ακόλουθη εντολή:

    $ python -c "import django; print(django.__path__)"

Επόμενο βήμα είναι να ανοίξετε το αρχείο που μόλις αντιγράψατε και να αντικαταστήσετε το `{{ site_header|default:_('Django administration') }}` (μαζί με τα διπλά άγκιστρα) με το όνομα του site σας ή οτιδήποτε άλλο εσείς θέλετε. Θα καταλήξετε με κάτι σαν αυτό:

    {% block branding %}
    <h1 id="site-name"><a href="{% url 'admin:index' %}">Polls Administration</a></h1>
    {% endblock %}

Χρησιμοποιούμε αυτή τη διαδικασία για να σας δείξουμε πως μπορείτε να κάνετε override τα templates. Σε ένα πραγματικό project, το πιο πιθανόν είναι να χρησιμοποιήσετε το attibute `django.contrib.admin.AdminSite.site_header` κάνοντας την παραπάνω διαδικασία ακόμα πιο εύκολη.

Αυτό το αρχείο template περιέχει αρκετό κείμενο όπως `{% block branding %}` και `{{ title }}`. Τα tags `{%` και `{{` είναι μέρος της γλώσσας template του Django. Όταν το Django κάνει render το αρχείο `admin/base_site.html`, αυτή η γλώσσα θα τρέξει και θα γεμίσει αυτά τα tags με διάφορες τιμές για να παραχθεί στο τέλος η HTML σελίδα που θα επιστρέψει πίσω στον χρήστη (ως response), όπως είδαμε στον Οδηγό 3.

Σημειώστε ότι κάθε template του Django μπορεί να γίνει overridden. Το μόνο που έχετε να κάνετε είναι να ακολουθήσετε την ίδια διαδικασία που κάναμε παραπάνω για το αρχείο `base_site.html` – το αντιγράφετε από την αρχική του θέση (πηγαίο κώδικα του Django) στο φάκελο που κρατάει τα templates της εφαρμογής σας. Αφού αντιγραφεί, μετά είστε ελεύθεροι να κάνετε τυχόν αλλαγές της αρέσκειας σας.

### Παραμετροποιώντας τα templates της εφαρμογής σας

Οι προσεκτικοί αναγνώστες θα αναρρωτηθούν: Αφού η ρύθμιση (λίστα) `DIRS` είναι κενή αρχικά, πως ξέρει το Django που να βρει τα προεπιλεγμένα admin templates; Η απάντηση έχει ως εξής: εφόσον η ρύθμιση (boolean) `APP_DIRS` έχει την τιμή `True`, το Django αυτόματα κοιτάζει στο φάκελο `templates/` κάθε εφαρμογής (που συμπεριλαμβάνεται στη ρύθμιση-λίστα `INSTALLED_APPS`), για τη χρήση τους ως fallback (μην ξεχνάτε ότι το `django.contrib.admin` είναι και αυτό μια εφαρμογή).

Η εφαρμογή ψηφοφορίας μας δεν είναι πολύ περίπλοκη και δεν χρειάζεται κάποια μεγάλη παραμετροποίηση των admin templates. Αλλά αν αναπτυχθεί και μεγαλώσει σε τέτοιο βαθμό που να χρειάζεται απαραιτήτως διάφορες λειτουργικές επεμβάσεις στα default templates του Django, τότε θα ήταν πιο λογικό να γίνει σε επίπεδο application (μέσα στο φάκελο templates της εφαρμογής σας) παρά σε επίπεδο project (μέσα στο φάκελο templates του project σας). Με αυτό τον τρόπο, θα μπορέσετε να συμπεριλάβετε την εφαρμογή ψηφοφορίας σας σε κάποιο μελλοντικό project και θα είστε σίγουροι ότι τα templates αυτής της εφαρμογής θα εφαρμοστούν αμέσως στο νέο project.

Δείτε στο εγχειρίδιο (documentation) φόρτωσης των templates για περισσότερες πληροφορίες σχετικά με το πως το Django βρίσκει-αναζητά τα templates.

## Παραμετροποιώντας την αρχική σελίδα του Django admin

Ομοίως, ίσως να θέλετε να παραμετροποιήσετε (να φέρετε στα μέτρα σας) την εμφάνιση της αρχικής σελίδας του Django admin.

Από προεπιλογή, εμφανίζονται όλες οι εφαρμογές (κατά αλφαβητική σειρά) που συμπεριλαμβάνονται στη ρύθμιση (λίστα) `INSTALLED_APPS` και έχουν γίνει register με το admin. Αν θέλετε να κάνετε σημαντικές αλλαγές στην εμφάνιση της αρχικής σελίδας δεν έχετε παρά να βάλετε τη φαντασία σας να δουλέψει. Είναι γνωστό ότι η εμφάνιση της αρχικής σελίδας του admin είναι η πιο σημαντική και θα πρέπει να είναι εύκολο να την παραμετροποιήσετε.

To template προς παραμετροποίηση είναι το `admin/index.html` (κάντε το ίδιο όπως κάνατε για το αρχείο `admin/base_site.html` πριν – αντιγράψτε το από τον πηγαίο κώδικα του Django στον τοπικό φάκελο templates της εφαρμογής σας). Κατά τη διάρκεια επεξεργασίας του αρχείου θα δείτε ότι χρησιμοποιείται μια template variable με το όνομα `app_list`. Αυτή η μεταβλητή περιέχει κάθε εγκατεστημένη εφαρμογή στο Django (είναι μέσα στο `INSTALLED_APPS` και register στο admin). Αντί αυτού, μπορείτε να γράψετε hard-code συνδέσμους σε object-specific admin σελίδες με όποιον τρόπο πιστεύετε ότι είναι καλύτερος.

## Επόμενα βήματα

Ο οδηγός για αρχάριους τελειώνει κάπου εδώ. Στο μεταξύ μπορείτε να δείτε προτεινόμενες κατευθύνσεις για το τι να κάνετε έπειτα, στο άρθρο τι να κάνω μετά από εδώ.

Αν είστε εξοικειωμένοι με το Python packaging και ενδιαφέρεστε να μάθετε πως να μετατρέψετε την εφαρμογή ψηφοφορίας σε “επαναχρησιμοποιήσιμη εφαρμογή”, δείτε το άρθρο Οδηγός για προχωρημένους: Πως να γράψετε επαναχρησιμοποιήσιμα apps.