Γράφοντας το πρώτο σας patch για το Django

Εισαγωγή

Ενδιαφέρεστε να συνεισφέρετε στην κοινότητα; Ίσως έχετε βρει κάποιο σφάλμα προγράμματος (bug) στο Django και θέλετε να διορθωθεί ή θέλετε να προσθέσετε κάποια επιπλέον λειτουργία ή χαρακτηριστικό.

Ο καλύτερος τρόπος να δείτε τις δικές σας προτάσεις-ιδέες να γίνονται πραγματικότητα είναι συνεισφέροντας στο Django. Αυτό στην αρχή ίσως σας φανεί περίπλοκο αλλά στην πραγματικότητα είναι αρκετά απλό. Θα σας καθοδηγήσουμε βήμα-βήμα μέσα σε όλη την διαδικασία ώστε να μπορέσετε να μάθετε μέσα από ένα παράδειγμα.

Σε ποιους απευθύνεται αυτό το tutorial

Δείτε επίσης

Αν ψάχνετε κάποια αναφορά σχετικά με το πως να υποβάλετε patches, δείτε το έγγραφο Submitting patches.

Για αυτόν τον οδηγό (tutorial), θα πρέπει να γνωρίζετε τουλάχιστον τη βασική λειτουργία του Django. Αυτό σημαίνει ότι θα πρέπει να είστε εξοικειωμένοι με τους ήδη υπάρχοντες οδηγούς Γράφοντας το πρώτο σας Django app. Επίσης, θα πρέπει να γνωρίζετε αρκετά καλά τη γλώσσα προγραμματισμού Python. Αν πάλι όχι, το βιβλίο Dive Into Python είναι ένα εκπληκτικό (και δωρεάν) online βιβλίο για νέους προγραμματιστές στη γλώσσα Python.

Για όσους από εσάς δεν είστε εξοικειωμένοι με τα εργαλεία συστημάτων ελέγχου εκδόσεων (VCS – version control system) και το Trac, δεν υπάρχει λόγος να ανησυχείτε. Αυτός ο οδηγός (και όποιοι σύνδεσμοι-links του) έχει τις απαραίτητες πληροφορίες για να ξεκινήσετε. Ωστόσο, ίσως να θέλετε να μάθετε περισσότερα σχετικά με αυτά τα εργαλεία, αν σκοπεύετε να συνεισφέρετε στο Django τακτικά.

Εν τούτοις, αυτός ο οδηγός στο μεγαλύτερο του μέρος, προσπαθεί να εξηγήσει όσα περισσότερα μπορεί, ώστε να μπορεί να φανεί χρήσιμος στο μεγαλύτερο δυνατό ευρύ κοινό.

Που να ψάξετε για βοήθεια

Αν δυσκολεύεστε με αυτόν τον οδηγό, μπορείτε να στείλετε ένα μήνυμα στους django-developers ή να συμμετάσχετε στο κανάλι #django-dev στο irc.freenode.net για να μιλήσετε με άλλους Django χρήστες οι οποίοι μπορεί να σας βοηθήσουν.

Τι καλύπτει αυτός ο οδηγός;

Σε αυτόν τον οδηγό θα φτιάξουμε παρέα ένα patch για το Django και θα το συνεισφέρουμε (contribute) στην κοινότητα για πρώτη φορά. Στο τέλος αυτού του οδηγού, θα έχετε λάβει τις βασικές γνώσεις των εργαλείων (tools) και των διαδικασιών (processes) που εμπλέκονται. Πιο συγκεκριμένα, θα καλύψουμε τα ακόλουθα:

  • Εγκατάσταση Git.

  • Δημιουργία αντίγραφου της αναπτυξιακής έκδοσης (development version) του Django.

  • Εκτέλεση της σουίτας δοκιμών του Django (test suite).

  • Γράφοντας τεστ για το patch.

  • Γράφοντας κώδικα για το ίδιο το patch.

  • Πραγματοποίηση τεστ για το patch.

  • Υποβολή αιτήματος έλξης.

  • Που να κοιτάξετε για περισσότερες πληροφορίες.

Αφού ολοκληρώσετε τον οδηγό, μπορείτε να διαβάσετε το :doc:` εγχειρίδιο (documentation) του Django σχετικά με την συνεισφορά </internals/contributing/index>`. Περιέχει πολλές πληροφορίες και αν θέλετε να συνεισφέρετε τακτικά στο Django θα πρέπει να το διαβάσετε. Αν έχετε απορίες, τότε πιθανόν το εγχειρίδιο να έχει την απάντηση.

Python 3!

Αυτός ο οδηγός προϋποθέτει ότι χρησιμοποιείτε την Python 3. Κατεβάστε την τελευταία έκδοση στην επίσημη σελίδα της Python ή με τον αντίστοιχο διαχειριστή πακέτων (packet manager) του λειτουργικού σας συστήματος.

Για τους χρήστες Windows

Κατά την εγκατάσταση της Python σε περιβάλλον Windows, σιγουρευτείτε ότι η επιλογή “Προσθήκη python.exe στο Path” είναι επιλεγμένη, ούτως ώστε οι εντολές που έχουν σχέση με την Python να είναι διαθέσιμες μέσω της γραμμής εντολών.

Κώδικας δεοντολογίας

Ως συνεισφέρων, μπορείτε να μας βοηθήσετε να διατηρήσουμε την Django κοινότητα ανοιχτή (open) και περιεκτική (inclusive). Παρακαλούμε διαβάστε και ακολουθήστε το δικό μας Κώδικα Δεοντολογίας.

Εγκατάσταση Git

Για αυτόν τον οδηγό θα χρειαστεί να έχετε εγκατεστημένο το Git ούτως ώστε να κατεβάσετε την αναπτυξιακή έκδοση (development version) του Django και να δημιουργήσετε patch αρχεία για τις αλλαγές που κάνετε.

Για να ελέγξετε αν έχετε εγκατεστημένο το Git, γράψτε στη κονσόλα (ή στη γραμμή εντολών αν έχετε Windows) τη λέξη git. Αν βλέπετε μηνύματα που λένε ότι αυτή η εντολή δεν υπάρχει, θα χρειαστεί να κατεβάσετε το Git και να το εγκαταστήσετε. Δείτε πως, στην επίσημη σελίδα του Git.

Για τους χρήστες Windows

Κατά την διάρκεια της εγκατάστασης του Git σε λειτουργικό Windows, προτείνετε να τσεκάρετε την επιλογή “Git Bash” ούτως ώστε το Git να τρέχει σε δικό του κέλυφος (shell). Αυτός ο οδηγός προϋποθέτει ότι έχει εγκατασταθεί το Git με αυτόν τον τρόπο.

Αν δεν είστε εξοικειωμένοι με το Git, μπορείτε να μάθετε περισσότερα σχετικά με τις εντολές του (αφού εγκατασταθεί) πληκτρολογώντας git help στην κονσόλα (ή στη γραμμή εντολών για χρήστες Windows).

Δημιουργία αντίγραφου της αναπτυξιακής έκδοσης του Django

Το πρώτο βήμα για την συνεισφορά στο Django είναι να αποκτήσετε ένα αντίγραφο του πηγαίου κώδικα. Πρώτα, κάντε fork το Django από το GitHub. Από την γραμμή εντολών, χρησιμοποιείστε την εντολή cd για να μεταβείτε στον φάκελο στον οποίο θέλετε να αποθηκεύσετε τον πηγαίο κώδικα του Django. Αν δεν υπάρχει κάποιος φάκελος της αρεσκείας σας, τότε χρησιμοποιήστε την εντολή mkdir για να τον δημιουργήσετε και μετά την εντολή cd επιθυμητός_φάκελος για να αποθηκεύσετε εκεί τον πηγαίο κώδικα.

Κατεβάστε το αποθετήριο (εφεξής repository ή repo) του πηγαίου κώδικα του Django χρησιμοποιώντας την ακόλουθη εντολή:

$ git clone git@github.com:YourGitHubName/django.git

Τώρα που έχετε ένα τοπικό αντίγραφο του Django, μπορείτε να το εγκαταστήσετε, όπως ακριβώς ένα οποιοδήποτε πακέτο, με την χρήση της εντολής pip. Ο πιο βολικός και προτεινόμενος τρόπος να το κάνετε είναι μέσω ενός εικονικού περιβάλλοντος (εφεξής virtualenv, από τις λέξεις virtual environment) το οποίο είναι ένα προεγκατεστημένο χαρακτηριστικό (feature) της Python για την διαχείριση ξεχωριστών εγκαταστάσεων ανά εφαρμογή. Η φιλοσοφία είναι ότι κρατάτε σε ξεχωριστό φάκελο (περιβάλλον) τα πακέτα (π.χ Django 1.9 ή Django 1.10 ή κάποια third party εφαρμογή) που απαιτούνται για την εφαρμογή σας (π.χ Django app) και έτσι κάθε εφαρμογή δεν θα παρεμβαίνει με τις υπόλοιπες.

Είναι καλή πρακτική να κρατάτε όλα τα virtualenvs σε ένα σημείο, για παράδειγμα στο φάκελο .virtualenvs/ μέσα στο home φάκελο σας. Δημιουργήστε το, αν δεν υπάρχει ήδη:

$ mkdir ~/.virtualenvs

Τώρα δημιουργήστε ένα καινούργιο virtualenv (π.χ με το όνομα djangoenv) τρέχοντας την εντολή:

$ python3 -m venv ~/.virtualenvs/djangodev

Η διαδρομή (path) που εισαγάγατε αποτελεί και το καινούργιο virtualenv το οποίο θα αποθηκευτεί στον υπολογιστή σας.

Για τους χρήστες Windows

Χρησιμοποιώντας τον τρόπο που αναφέρθηκε παραπάνω (Εγκατάσταση Git) για την εγκατάσταση του Git, η εντολή venv δεν θα δουλέψει, επειδή τα activation scripts δημιουργούνται μόνο στο κέλυφος συστήματος (.bat) και στο PowerShell (.ps1). Για να δουλέψει χρησιμοποιείστε το πακέτο virtualenv:

$ pip install virtualenv
$ virtualenv ~/.virtualenvs/djangodev

Για τους χρήστες Ubuntu

Σε κάποιες εκδόσεις του λειτουργικού Ubuntu, η εντολή venv ενδέχεται να μην δουλέψει. Χρησιμοποιείστε το πακέτο virtualenv, αφού πρώτα σιγουρευτείτε ότι έχετε εγκατεστημένο το pip3:

$ sudo apt-get install python3-pip
$ # Prefix the next command with sudo if it gives a permission denied error
$ pip3 install virtualenv
$ virtualenv --python=`which python3` ~/.virtualenvs/djangodev

Το τελευταίο βήμα σχετικά με την ολοκλήρωση του virtualenv είναι η ενεργοποίηση του:

$ source ~/.virtualenvs/djangodev/bin/activate

Αν η εντολή source δεν είναι διαθέσιμη, χρησιμοποιήστε την τελεία, όπως:

$ . ~/.virtualenvs/djangodev/bin/activate

Για τους χρήστες Windows

Για να ενεργοποιήσετε το virtualenv σας σε λειτουργικό Windows, τρέξτε:

$ source ~/virtualenvs/djangodev/Scripts/activate

Κάθε φορά που ανοίγετε μια κονσόλα (γραμμή εντολών) και σκοπεύετε να αλληλεπιδράτε με την εφαρμογή σας, θα πρέπει σε κάθε κονσόλα να ενεργοποιείτε το virtualenv. Το εργαλείο virtualenvwrapper θα σας φανεί πολύ χρήσιμο και βολικό για τέτοιου είδους καταστάσεις.

Όταν ένα virtualenv ενεργοποιηθεί, το πρόθεμα στη γραμμή εντολών θα είναι το όνομα του virtualenv σας μέσα σε παρένθεση. Οποιοδήποτε πακέτο εγκαταστήσετε, από δω και στο εξής μέσω του pip, θα εγκατασταθεί στον φάκελο που αποθηκεύονται όλα τα virtualenvs (π.χ .virtualenvs) και πιο συγκεκριμένα μέσα στο φάκελο με την ονομασία του virtualenv σας (π.χ .virtualenvs/djangoenv). Με αυτό τον τρόπο κάθε virtualenv περιέχει τα δικά του πακέτα και έτσι επιτυγχάνεται η απομόνωση μεταξύ άλλων πακέτων άλλων virtualenvs και των πακέτων του συστήματος σας (global/system-wide). Εγκαταστήστε τώρα την έκδοση του Django που κατεβάσατε πριν μέσω του git clone:

$ pip install -e /path/to/your/local/clone/django/

Η εγκατεστημένη έκδοση του Django είναι τώρα μέσα στο virtualenv σας και μπορείτε να κάνετε import django σε Python κονσόλα χωρίς να σας επιστρέψει σφάλμα (cannot import module django). Τώρα μπορείτε να δείτε απ’ ευθείας τις αλλαγές που κάνετε σε αυτό το πακέτο, το οποίο είναι εξαιρετικά χρήσιμο, ειδικά όταν θα γράψετε το πρώτο σας patch αρχείο.

Επαναφορά σε μια προγενέστερη έκδοση του Django

Σε αυτό τον οδηγό, θα χρησιμοποιήσουμε το ticket #24788 ως αντικείμενο μελέτης. Γι’ αυτό το λόγο θα χρειαστεί να μεταβούμε σε μια προγενέστερη έκδοση του Django πριν εφαρμοστεί αυτό το patch. Με άλλα λόγια θέλουμε να αναπαράγουμε το σφάλμα που εμφανιζόταν πριν, να γράψουμε το patch και να διορθώσουμε το σφάλμα εφαρμόζοντας το, στην τότε έκδοση. Αυτό θα μας βοηθήσει να δούμε όλα τα εμπλεκόμενα βήματα τα οποία απαιτούνται για να γραφτεί από την αρχή ένα patch (συμπεριλαμβάνοντας και την σουίτα δοκιμών του Django).

Έχετε υπόψη σας ότι παρόλο που θα χρησιμοποιήσουμε μια παλιά έκδοση του Django σε αυτόν τον οδηγό, εσείς θα πρέπει πάντα να χρησιμοποιείτε την τρέχουσα αναπτυξιακή έκδοση (development version) του Django όταν πρόκειται να γράψετε ένα patch για κάποιο ticket

Σημείωση

Το patch, για αυτό το ticket, γράφτηκε από τον Paweł Marczewski και ενσωματώθηκε στον πηγαίο κώδικα του Django ως commit 4df7e8483b2679fc1cba3410f08960bac6f51115. Συνεπώς, θα χρησιμοποιήσουμε μια έκδοση του Django πριν το ανωτέρω commit, commit 4ccfc4439a7add24f8db4ef3960d02ef8ae09887.

Μεταβείτε στο root φάκελο του Django (είναι αυτός που περιέχει τους φακέλους django, docs, tests, AUTHORS, κλπ), ο οποίος βρίσκεται μέσα στο φάκελο με το όνομα του virtualenv σας. Αφού μεταβείτε εκεί, μπορείτε να κάνετε checkout (εντολή git) σε μια παλαιότερη έκδοση του Django, την οποία θα χρησιμοποιήσουμε σε αυτόν τον οδηγό:

$ git checkout 4ccfc4439a7add24f8db4ef3960d02ef8ae09887

Εκτέλεση της σουίτας δοκιμών του Django για πρώτη φορά

Όταν συνεισφέρετε στο Django είναι πολύ σημαντικό ο κώδικας που γράφετε (π.χ για την επίλυση ενός ticket) να μην επιφέρει σφάλματα (bugs) σε άλλα σημεία του Django. Με άλλα λόγια όταν προσπαθείτε να διορθώσετε κάποια κομμάτια στον πηγαίο κώδικα του Django, να μην δημιουργούνται νέα σφάλματα σε άλλο σημείο του πηγαίου κώδικα. Ένας τρόπος για να γνωρίζετε ότι το Django τρέχει ορθά μετά τις αλλαγές σας, είναι τρέχοντας την σουίτα δοκιμών του Django (test suite). Αν όλα τα τεστ περάσουν, τότε μπορείτε εκ του ασφαλούς να ξέρετε ότι ο κώδικας σας δεν προκάλεσε σφάλματα αλλού. Αν δεν έχετε τρέξει ποτέ τη σουίτα δοκιμών του Django, μια καλή ιδέα είναι να την τρέξετε πριν ξεκινήσετε τη διαδικασία των αλλαγών (πριν την συγγραφή του patch), ούτως ώστε να εξοικειωθείτε με την εμφάνιση των αποτελεσμάτων του (output results).

Πριν τρέξετε την σουίτα, θα πρέπει να εγκαταστήσετε πρώτα κάποια πακέτα τα οποία είναι απαραίτητα για να ολοκληρωθούν τα τεστ. Μεταβείτε στον φάκελο tests/ (με την εντολή cd tests/) και τρέξτε την εντολή:

$ pip install -r requirements/py3.txt

Αν αντιμετωπίσατε κάποιο πρόβλημα κατά την εγκατάσταση, ίσως να λείπει απ’ το σύστημα σας κάποιο dependency (τα dependencies είναι και αυτά με τη σειρά τους πακέτα) για ένα ή περισσότερα Python πακέτα. Συμβουλευτείτε το εγχειρίδιο των πακέτων που δεν εγκαταστάθηκαν ή αναζητήστε στο ίντερνετ το σφάλμα, όπως ακριβώς αυτό εμφανίστηκε όταν προέκυψε.

Τώρα είμαστε έτοιμοι να τρέξουμε τη σουίτα δοκιμών του Django. Αν χρησιμοποιείτε GNU/Linux, Mac OS X ή κάποια άλλη Unix διανομή, τρέξτε:

$ ./runtests.py

Καθίστε αναπαυτικά και χαλαρώστε! Η σουίτα δοκιμών του Django περιλαμβάνει πάνω από 9,600 διαφορετικά τεστ. Αυτό σημαίνει ότι η διαδικασία ενδέχεται να διαρκέσει μεταξύ 5 – 15 λεπτά, ανάλογα την ισχύ του υπολογιστή σας.

Όσο τα τεστ τρέχουν, θα παρατηρήσετε διάφορες ακολουθίες χαρακτήρων οι οποίες αναπαριστούν την κατάσταση (status) του κάθε τεστ. Το E υποδηλώνει ότι προέκυψε σφάλμα κατά την εκτέλεση του συγκεκριμένου τεστ. Το F ότι τα assertions του τεστ απέτυχαν. Οι δύο ανωτέρω καταστάσεις δηλώνουν ότι το τεστ απέτυχε. Επίσης, τα x και s υποδηλώνουν αναμενόμενες αποτυχίες (expected failures) και παραλειπόμενα τεστ (skipped tests), αντίστοιχα. Οι τελείες υποδηλώνουν επιτυχή τεστ.

Τα παραλειπόμενα τεστ (τεστ που δεν έγιναν) προκύπτουν συνήθως λόγω έλλειψης κάποιων εξωτερικών βιβλιοθηκών (external libraries) οι οποίες χρειάζονται για να τρέξει το τεστ. Δείτε στο Running all the tests για μια λίστα με όλα τα dependencies (εξαρτώμενα πακέτα – εξωτερικές βιβλιοθήκες). Επίσης σιγουρευτείτε ότι έχετε εγκαταστήσει τα απαραίτητα πακέτα (στα οποία εξαρτώνται τα τεστ) προτού τρέξετε κάποιο τεστ (δεν θα χρειαστούμε κάποιο από αυτά σε αυτόν τον οδηγό). Μερικά τεστ είναι γραμμένα και συνδέονται με μια συγκεκριμένη βάση δεδομένων και δεν θα τρέξουν αν δεν υπάρχει εγκατεστημένη η αντίστοιχη βάση δεδομένων καθώς και το πακέτο (βιβλιοθήκη) που συνδέει την database με το Django (π.χ για την PostgreSQL το πακέτο είναι το psycopg2). Η SQLite είναι η βάση δεδομένων που χρησιμοποιεί το Django ως προεπιλογή. Μπορείτε επίσης να Using another settings module.

Όταν ολοκληρωθούν τα τεστ, θα σας εμφανιστεί κάποιο μήνυμα που θα σας ενημερώνει για την επιτυχή ή όχι εκτέλεση τους. Εφόσον δεν έχετε κάνει κάποια αλλαγή μέχρι τώρα στον πηγαίο κώδικα του Django, όλα τα τεστ θα πρέπει να έχουν επιτυχή έκβαση. Αν δείτε τυχόν σφάλματα ή αποτυχίες κάποιων τεστ, σιγουρευτείτε ότι έχετε ακολουθήσει κατάλληλα τα ανωτέρω βήματα. Δείτε στο Running the unit tests για περισσότερες πληροφορίες. Αν χρησιμοποιείτε την Python 3.5+, θα υπάρξουν κάποιες αποτυχίες μερικών τεστ οι οποίες οφείλονται σε προειδοποιήσεις υποβάθμισης (deprecation warnings) στις οποίες μπορείτε να μην δώσετε σημασία. Αυτές οι αποτυχίες έχουν αποκατασταθεί στην τρέχουσα έκδοση του Django. Σας υπενθυμίζουμε ότι σε αυτόν τον οδηγό χρησιμοποιούμε μια παλιά έκδοση του Django.

Υπογραμμίζεται ότι η τελευταία αναπτυξιακή έκδοση του Django ενδέχεται να μην είναι σταθερή (stable). Όταν δουλεύετε με τέτοιου είδους εκδόσεις, μπορείτε να ενημερώνεστε μέσω των εκδόσεων συνεχούς ενσωμάτωσης του Django (Django’s continuous integration builds) για να διαπιστώσετε αν οι αποτυχίες των τεστ οφείλονται στο σύστημα σας ή οι αποτυχίες συμβαίνουν και στις επίσημες αναπτυξιακές εκδόσεις του Django. Αν κλικάρετε για να δείτε μια συγκεκριμένη έκδοση, μπορείτε να δείτε το “Configuration Matrix” το οποίο δείχνει τις αποτυχίες (failures) αναλυτικά ανά έκδοση Python και βάση δεδομένων.

Σημείωση

Για το συγκεκριμένο οδηγό και για το ticket πάνω στο οποίο δουλεύουμε, τα τεστ που αφορούν την SQLite είναι αρκετά, ωστόσο, είναι δυνατόν (και μερικές φορές απαραίτητο) να τρέχουμε τεστ χρησιμοποιώντας διαφορετικές βάσεις δεδομένων.

Δημιουργία ενός τμήματος για το αρχείου σας

Πριν κάνετε αλλαγές, δημιουργήστε ένα νέο τμήμα για το εισητήριο

$ git checkout -b ticket_24788

Μπορείτε να επιλέξετε οποιοδήποτε όνομα θέλετε για το τμήμα, το «ticket_24788” είναι ένα παράδειγμα. Όλες οι αλλαγές που έγιναν σε αυτόν το τμήμα θα είναι ειδικά για το εισιτήριο και δεν θα επηρεάσουν το κύριο αντίγραφο του κώδικα που έχουμε κλωνοποιήσαμε νωρίτερα.

Γράφοντας μερικά τεστ για το ticket

Στις περισσότερες περιπτώσεις, για να θεωρηθεί ένα patch αποδεκτό για την ενσωμάτωση του στο Django, θα πρέπει να περιέχει τεστ. Για περιπτώσεις που αφορούν κάποια επιδιόρθωση σφάλματος (bug fix), θα πρέπει να γραφτεί ένα τεστ οπισθοδρόμησης (regression test) του οποίου η σημασία είναι η βεβαίωση ότι το σφάλμα (που επρόκειτο να διορθωθεί) δεν θα ξαναεμφανιστεί σε μεταγενέστερη έκδοση του Django. Ένα regression τεστ θα πρέπει να γραφτεί με τέτοιο τρόπο ούτως ώστε θα αποτυγχάνει (fail) όσο το σφάλμα θα υπάρχει και θα επιτυγχάνει όταν το σφάλμα διορθωθεί. Για patches τα οποία περιέχουν καινούργιο κώδικα (δεν προσπαθούν, δηλαδή, να διορθώσουν υπάρχον κώδικα αλλά προσθέτουν νέα χαρακτηριστικά-λειτουργία), θα χρειαστεί να συμπεριλάβετε τεστ τα οποία εξασφαλίζουν ότι το καινούργιο χαρακτηριστικό λειτουργεί ορθά (π.χ δημιουργία ενός καινούργιου django.db.models.fields ή δημιουργία καινούργιου template tag). Τα τεστ αυτά, θα πρέπει να αποτυγχάνουν όταν τα καινούργια αυτά χαρακτηριστικά δεν υπάρχουν (π.χ σε παλαιότερες εκδόσεις του Django) και αντιθέτως να επιτυγχάνουν όταν αυτά (τα χαρακτηριστικά) εμφανιστούν.

Μια καλή πρακτική για να επιτύχετε τα παραπάνω είναι να γράψετε πρώτα τα τεστ, προτού κάνετε οποιεσδήποτε αλλαγές στον πηγαίο κώδικα. Αυτού του είδους το development ονομάζεται test-driven development και μπορεί να εφαρμοστεί από απλά patches μέχρι ολόκληρα projects. Αφού γράψετε τα τεστ, μπορείτε να τα τρέξετε για να διαπιστώσετε ότι όντως αποτυγχάνουν (λογικό, αφού δεν έχετε γράψει ακόμη κώδικα που να διορθώνει το σφάλμα ή κώδικα που προσθέτει κάποιο καινούργιο χαρακτηριστικό στο Django). Αν τα τεστ σας δεν αποτυγχάνουν, θα πρέπει να φροντίσετε ώστε να το κάνουν. Αν ένα regression test περνά, ανεξαρτήτως αν το σφάλμα (bug) υπάρχει ή όχι, δεν βοηθάει καθόλου και θα δυσκολέψει το έργο (όχι μόνο το δικό σας) της αποσφαλμάτωσης (debugging) στο μέλλον.

Ας εργαστούμε λοιπόν στο παράδειγμα μας.

Γράφοντας μερικά τεστ για το ticket #24788

Το ticket #24788 προτείνει μια μικρή προσθήκη ενός νέου χαρακτηριστικού: την δυνατότητα να προσδιοριστεί το class attribute με το όνομα prefix μέσα στις Form classes, ούτως ώστε:

[…] forms which ship with apps could effectively namespace themselves such
that N overlapping form fields could be POSTed at once and resolved to the
correct form.

Για να επιλύσουμε αυτό το ticket, θα προσθέσουμε το prefix attribute στην BaseForm class. Έτσι, όταν θα δημιουργούμε instances από αυτή την class περνώντας ένα prefix όρισμα στην __init__() μέθοδο, το instance θα έχει καταχωρημένο αυτό το prefix attribute. Αλλά όταν δεν περνάμε (μέσω της __init__()) το prefix (ή περνάμε None) τότε θα χρησιμοποιηθεί το prefix σε επίπεδο class (το prefix attribute της class δηλαδή). Πριν γράψουμε αυτές τις αλλαγές όμως, θα γράψουμε μερικά τεστ τα οποία θα αντικατοπτρίζουν τις αλλαγές αυτές και θα ακολουθούν την ιδεολογία του Test Driven Development (TDD), όπως αναφέρθηκε παραπάνω.

Μεταβείτε στον φάκελο tests/forms_tests/tests/ και ανοίξτε το αρχείο test_forms.py. Προσθέστε τον ακόλουθο κώδικα στην γραμμή 1674, ακριβώς πριν την συνάρτηση test_forms_with_null_boolean:

def test_class_prefix(self):
    # Prefix can be also specified at the class level.
    class Person(Form):
        first_name = CharField()
        prefix = 'foo'

    p = Person()
    self.assertEqual(p.prefix, 'foo')

    p = Person(prefix='bar')
    self.assertEqual(p.prefix, 'bar')

Αυτό το καινούργιο τεστ ελέγχει δύο πράγματα (τα οποία ήταν στις προδιαγραφές παραπάνω). Πρώτον, αν το instance δημιουργηθεί χωρίς prefix (δεν περαστεί στην __init__) τότε το p.prefix θα είναι ίσο με ένα προεπιλεγμένο prefix attribute που θα το ορίζει η εκάστοτε class. Δεύτερον, αν το instance δημιουργηθεί με κάποιο prefix attribute (πάντα μέσω της __init__) τότε το p.prefix θα ισούται με αυτό που πέρασε στην __init__.

Μήπως σας φάνηκε δύσκολο αυτό το τεστ;

Αν δεν έχετε γράψει ποτέ στο παρελθόν κάποιο τεστ και πρώτη φορά συναναστρέφεστε με αυτό, στην αρχή ίσως σας φανεί κάπως δύσκολο. Δεν υπάρχει όμως λόγος να ανησυχείτε καθώς η ενότητα testing είναι ένα τεράστιο κεφάλαιο στον προγραμματισμό, που σημαίνει ότι υπάρχει εκτενής και πολλή πληροφορία πάνω σε αυτό το θέμα:

  • Μια πρώτη ματιά για τη σύνταξη των τεστ για το Django μπορείτε να βρείτε στην ενότητα του εγχειριδίου (documentation) Writing and running tests.

  • Το βιβλίο Dive Into Python (δωρεάν για αρχάριους developers στην γλώσσα προγραμματισμού Python) περιέχει μια πολύ καλή εισαγωγή στο Unit Testing.

  • Αφού διαβάσετε τα ανωτέρω και επιθυμείτε να εμβαθύνετε τις γνώσεις σας περί του θέματος, υπάρχει πάντα το εγχειρίδιο unittest της Python.

Τρέχοντας το τεστ

Θυμηθείτε ότι δεν έχουμε πειράξει (ακόμη) τον πηγαίο κώδικα του Django (παρεμβαίνοντας συγκεκριμένα στην BaseForm). Το μόνο που αλλάξαμε είναι η προσθήκη ενός ακόμη τεστ (το οποίο θα πρέπει να αποτυγχάνει). Ας τρέξουμε όλα τα τεστ στον φάκελο forms_tests ούτως ώστε να σιγουρευτούμε ότι το δικό μας αποτυγχάνει. Από τη γραμμή εντολών, χρησιμοποιώντας την εντολή cd tests/ μεταβαίνετε στο φάκελο tests/ και τρέχετε την εντολή:

$ ./runtests.py forms_tests

Αν τα τεστ εκτελεστούν με επιτυχία (προσοχή, αυτό δεν σημαίνει ότι πέρασαν, απλώς ότι εκτελέστηκαν επιτυχώς) τότε θα πρέπει να δείτε στο τεστ που γράψαμε πριν, αποτυχία (failure). Αν όλα τα τεστ πέρασαν, τότε κάτι δεν πήγε καλά και θα πρέπει να σιγουρευτείτε ότι προσθέσατε το παραπάνω τεστ στο σωστό φάκελο κάτω από τη σωστή class.

Γράφοντας κώδικα για το ticket

Παρακάτω θα γράψουμε τον κώδικα που απαιτείται για να επιλύσουμε το ticket #24788.

Γράφοντας κώδικα για το ticket #24788

Μεταβείτε στο φάκελο django/django/forms/ και ανοίξτε το αρχείο forms.py. Βρείτε την BaseForm class στη γραμμή 72 και προσθέστε το prefix class attribute ακριβώς κάτω από το field_order attribute:

class BaseForm(object):
    # This is the main implementation of all the Form logic. Note that this
    # class is different than Form. See the comments by the Form class for
    # more information. Any improvements to the form API should be made to
    # *this* class, not to the Form class.
    field_order = None
    prefix = None

Επιβεβαίωση ότι το νέο τεστ περνά επιτυχώς

Όταν τελειώσετε την τροποποίηση του πηγαίου κώδικα του Django, πρέπει να σιγουρευτούμε ότι τα τεστ που γράψαμε νωρίτερα περνούν επιτυχώς, κάτι το οποίο θα επιβεβαιώσει ότι ο κώδικας που γράψαμε (στην BaseForm) είναι σωστός. Για να τρέξουμε τα τεστ στο φάκελο forms_tests, πληκτρολογήστε cd tests/ (για μετάβαση στον φάκελο tests/) και μετά:

$ ./runtests.py forms_tests

Oops, φαίνεται ότι κάναμε καλά και γράψαμε αυτό το τεστ νωρίτερα! Θα πρέπει να δείτε μια αποτυχία (failure) με το ακόλουθο exception:

AssertionError: None != 'foo'

Παραλείψαμε να προσθέσουμε μια δομή ελέγχου (conditional statement) μέσα στην μέθοδο __init__. Πηγαίνετε λοιπόν και αλλάξτε το self.prefix = prefix που βρίσκεται στη γραμμή 87 του αρχείου django/forms/forms.py, με το:

if prefix is not None:
    self.prefix = prefix

Ξανά-τρέξτε τα τεστ και πλέον όλα θα έχουν περάσει επιτυχώς. Αν όχι, σιγουρευτείτε ότι έχετε τροποποιήσει την BaseForm σωστά (όπως στο παράδειγμα) και επίσης έχετε γράψει το τεστ όπως στο παράδειγμα.

Εκτέλεση της σουίτας δοκιμών του Django για δεύτερη φορά

Όταν έχετε επιβεβαιώσει ότι το patch και το τεστ δουλεύουν ορθά, τότε είναι ώρα για ένα τελευταίο τεστ όλης της σουίτας του Django, ούτως ώστε να είστε σίγουροι ότι οι αλλαγές που κάνατε (στο συγκεκριμένο οδηγό, στην BaseForm) δεν επιφέρουν σφάλματα σε άλλα σημεία του Django. Ωστόσο, αν το patch περάσει επιτυχώς απ’ όλα τα τεστ, αυτό δεν εγγυάται ότι ο κώδικας σας δεν περιέχει σφάλματα (bug free). Βοηθάει, όμως, στην αναγνώριση πολλών σφαλμάτων που ειδάλλως θα περνούσαν απαρατήρητα.

Για να τρέξετε ολόκληρη τη σουίτα δοκιμών του Django, πληκτρολογήστε cd tests/ και τρέξτε:

$ ./runtests.py

Εφόσον δε δείτε κάποια αποτυχία (failure), είστε έτοιμοι!

Γράφοντας το εγχειρίδιο (documentation)

Το εγχειρίδιο είναι ένα νέο χαρακτηριστικό και θα πρέπει να σας απασχολήσει στον ίδιο βαθμό με τον κώδικα που γράφετε. Κάθε κομμάτι που γράφετε μέσα στο εγχειρίδιο, βοηθάτε όχι μόνο τον εαυτό σας αλλά και τους υπόλοιπους προγραμματιστές οι οποίοι θα διαβάσουν τον κώδικα σας. Προσθέστε το ακόλουθο κομμάτι στη γραμμή 1068, στο τέλος του αρχείου django/docs/ref/forms/api.txt:

The prefix can also be specified on the form class::

    >>> class PersonForm(forms.Form):
    ...     ...
    ...     prefix = 'person'

.. versionadded:: 1.9

    The ability to specify ``prefix`` on the form class was added.

Εφόσον αυτό το νέο χαρακτηριστικό (prefix) θα είναι διαθέσιμο από την έκδοση 1.9 (και μετά) του Django, θα πρέπει να προστεθεί στις σημειώσεις έκδοσης (release notes) του Django 1.9. Το προσθέσαμε λοιπόν στη γραμμή 164 κάτω από την ενότητα “Forms” στο αρχείο docs/releases/1.9.txt:

* A form prefix can be specified inside a form class, not only when
  instantiating a form. See :ref:`form-prefix` for details.

Για περισσότερες πληροφορίες σχετικά με οδηγίες σύνταξης του εγχειριδίου (περιέχει και την εξήγηση του versionadded), δείτε στο Writing documentation. Αυτή η σελίδα εξηγεί και το πως να δημιουργήσετε ένα αντίγραφο του εγχειριδίου σας τοπικά (στον υπολογιστή σας), ούτως ώστε να δείτε πως θα φαίνεται online στο επίσημο site του Django.

Προβολή των αλλαγών σας

Τώρα ήρθε η ώρα να περάσει μέσα από όλες τις αλλαγές που έγιναν στην ενημέρωσή μας. Για να εμφανίσετε τις διαφορές μεταξύ του τρέχοντος αντίγραφο σας του Django (με τις αλλαγές σας) και την αναθεώρηση που αρχικά ελέγξατε νωρίτερα στο σεμινάριο:

$ git diff

Χρησιμοποιήστε τα βέλη για να κινηθείτε προς τα επάνω και προς τα κάτω.

diff --git a/django/forms/forms.py b/django/forms/forms.py
index 509709f..d1370de 100644
--- a/django/forms/forms.py
+++ b/django/forms/forms.py
@@ -75,6 +75,7 @@ class BaseForm(object):
     # information. Any improvements to the form API should be made to *this*
     # class, not to the Form class.
     field_order = None
+    prefix = None

     def __init__(self, data=None, files=None, auto_id='id_%s', prefix=None,
                  initial=None, error_class=ErrorList, label_suffix=None,
@@ -83,7 +84,8 @@ class BaseForm(object):
         self.data = data or {}
         self.files = files or {}
         self.auto_id = auto_id
-        self.prefix = prefix
+        if prefix is not None:
+            self.prefix = prefix
         self.initial = initial or {}
         self.error_class = error_class
         # Translators: This is the default suffix added to form field labels
diff --git a/docs/ref/forms/api.txt b/docs/ref/forms/api.txt
index 3bc39cd..008170d 100644
--- a/docs/ref/forms/api.txt
+++ b/docs/ref/forms/api.txt
@@ -1065,3 +1065,13 @@ You can put several Django forms inside one ``<form>`` tag. To give each
     >>> print(father.as_ul())
     <li><label for="id_father-first_name">First name:</label> <input type="text" name="father-first_name" id="id_father-first_name" /></li>
     <li><label for="id_father-last_name">Last name:</label> <input type="text" name="father-last_name" id="id_father-last_name" /></li>
+
+The prefix can also be specified on the form class::
+
+    >>> class PersonForm(forms.Form):
+    ...     ...
+    ...     prefix = 'person'
+
+.. versionadded:: 1.9
+
+    The ability to specify ``prefix`` on the form class was added.
diff --git a/docs/releases/1.9.txt b/docs/releases/1.9.txt
index 5b58f79..f9bb9de 100644
--- a/docs/releases/1.9.txt
+++ b/docs/releases/1.9.txt
@@ -161,6 +161,9 @@ Forms
   :attr:`~django.forms.Form.field_order` attribute, the ``field_order``
   constructor argument , or the :meth:`~django.forms.Form.order_fields` method.

+* A form prefix can be specified inside a form class, not only when
+  instantiating a form. See :ref:`form-prefix` for details.
+
 Generic Views
 ^^^^^^^^^^^^^

diff --git a/tests/forms_tests/tests/test_forms.py b/tests/forms_tests/tests/test_forms.py
index 690f205..e07fae2 100644
--- a/tests/forms_tests/tests/test_forms.py
+++ b/tests/forms_tests/tests/test_forms.py
@@ -1671,6 +1671,18 @@ class FormsTestCase(SimpleTestCase):
         self.assertEqual(p.cleaned_data['last_name'], 'Lennon')
         self.assertEqual(p.cleaned_data['birthday'], datetime.date(1940, 10, 9))

+    def test_class_prefix(self):
+        # Prefix can be also specified at the class level.
+        class Person(Form):
+            first_name = CharField()
+            prefix = 'foo'
+
+        p = Person()
+        self.assertEqual(p.prefix, 'foo')
+
+        p = Person(prefix='bar')
+        self.assertEqual(p.prefix, 'bar')
+
     def test_forms_with_null_boolean(self):
         # NullBooleanField is a bit of a special case because its presentation (widget)
         # is different than its data. This is handled transparently, though.

Αφότου ρίξετε μια ματιά στο κείμενο, πιέστε το πλήκτρο q για να βρεθείτε πάλι στη γραμμή εντολών. Αν το περιεχόμενο του patch δείχνει σωστό, μπορείτε να κάνετε commit τις αλλαγές (υποτίθεται ότι βρίσκεστε στο root directory του project):

Κάνοντας commit τις αλλαγές του patch

Για να κάνετε commit τις αλλαγές γράψτε:

$ git commit -a

Η παραπάνω εντολή ανοίγει έναν επεξεργαστή κειμένου για να γράψετε το μήνυμα του commit. Ακολουθήστε τις οδηγίες για ένα σωστό μήνυμα commit και γράψτε ένα μήνυμα όπως:

Fixed #24788 -- Allowed Forms to specify a prefix at the class level.

Κάνοντας push το commit καθώς και ένα pull request

Αφού κάνατε commit το patch, στείλτε το στο fork που φτιάξατε στο GitHub (αντικαθιστώντας το “ticket_24788” με το όνομα του δικού σας branch, αν είναι διαφορετικό):

$ git push origin ticket_24788

Μπορείτε να δημιουργήσετε ένα pull request πηγαίνοντας στην επίσημη σελίδα του Django στο GitHub. Εκεί θα δείτε το δικό σας branch κάτω από την ετικέτα “Your recently pushed branches”. Κάντε κλικ στο “Compare & pull request”.

Παρακαλούμε να μην το κάνετε για αυτό τον οδηγό, αλλά στην επόμενη σελίδα που θα εμφανιστεί μια προεπισκόπιση του patch σας, θα κάνετε κλικ στο “Create pull request”.

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

Συγχαρητήρια! Μόλις μάθατε πως να κάνετε pull requests στο Django! Λεπτομέρειες για περισσότερο ανεπτυγμένες τεχνικές, που ίσως χρειαστείτε, θα βρείτε στο Working with Git and GitHub.

Τώρα μπορείτε να χρησιμοποιήσετε αυτές τις τεχνικές για καλό σκοπό βελτιώνοντας τον πηγαίο κώδικα του Django.

Περισσότερες πληροφορίες για του νέους στην συνεισφορά

Προτού ξεκινήσετε να γράφετε patches για το Django, υπάρχουν μερικές ακόμη πληροφορίες στις οποίες θα πρέπει να ρίξετε μια ματιά:

  • Θα πρέπει να διαβάσετε το εγχειρίδιο του Django σχετικά με κατάθεση patches και διεκδίκηση tickets. Καλύπτει την δεοντολογία του Trac, πως να διεκδικήσετε tickets για τον εαυτό σας, προτεινόμενο στυλ γραφής κώδικα για patches και πολλές άλλες σημαντικές λεπτομέρειες.

  • Όσοι από εσάς συνεισφέρουν για πρώτη φορά στο Django θα πρέπει να διαβάσουν το εγχειρίδιο του Django για τους νέους συνεισφέροντες. Περιέχει πολλές και χρήσιμες συμβουλές για εκείνους που θέλουν να βοηθήσουν-υποστηρίξουν την Django κοινότητα.

  • Κατόπιν των ανωτέρω, αν θέλετε να μάθετε ακόμη περισσότερα σχετικά με την συνεισφορά, μπορείτε πάντα να ανατρέχετε στην ενότητα του εγχειρίδίου του Django σχετικά με την συνεισφορά. Περιέχει τόνους πληροφορίας και θα πρέπει να είναι η πρώτη πηγή που θα κοιτάξετε για τυχόν ερωτήσεις ή απορίες που μπορεί να προκύψουν.

Εύρεση του πρώτου αληθινού ticket

Αφού διαβάσατε και κατανοήσατε τη λογική των patches και των συνεισφορών, πιθανόν να είστε έτοιμοι να αναζητήσετε κάποιο ticket προκειμένου να γράψετε το κατάλληλο patch. Δώστε μεγάλη προσοχή στα tickets με το κριτήριο “easy pickings”. Αυτού του είδους τα tickets είναι αρκετά απλά στη φύση τους (όπως απλό ήταν και αυτό που γράψαμε σε αυτόν τον οδηγό) και είναι κομμένα και ραμμένα στα μέτρα των νέων προγραμματιστών που θέλουν να συνεισφέρουν. Όταν εξοικειωθείτε με την συνεισφορά στο Django, μπορείτε να συνεχίσετε και να γράφετε patches και για πιο δύσκολα-περίπλοκα tickets. Η λογική είναι να ξεκινήσετε την επίλυση απλών tickets και σιγά σιγά να επιλύετε ακόμη πιο δύσκολα. Αν πάλι θέλετε να παραμείνετε στην επίλυση απλών tickets κανένα πρόβλημα.

Αν θέλετε να ξεκινήσετε άμεσα (και κανείς δε πρόκειται να σας κατηγορήσει γι’ αυτό!), ρίξτε μια ματιά στη λίστα των easy tickets που χρειάζονται patches και στα easy tickets που έχουν patches αλλά χρειάζονται βελτίωση. Αν έχετε εξοικειωθεί με την συγγραφή τεστ, μπορείτε επίσης να κοιτάξετε τη λίστα των easy tickets που χρειάζονται τεστ. Θυμηθείτε μόνο τις κατευθηντήριες γραμμές (guidelines) σχετικά με την διεκδίκηση tickets οι οποίες αναφέρθηκαν στον σύνδεσμο του εγχειρίδίου του Django σχετικά με την :doc:` κατάθεση patches και διεκδίκηση tickets </internals/contributing/writing-code/submitting-patches>`.

Τι γίνετε μετά, αφότου δημιουργήσω ένα pull request;

Όταν ένα ticket συνοδεύεται από ένα patch, θα χρειαστεί να κοιταχτεί και από άλλους. Αφού κατατεθεί ένα pull request, θα πρέπει να ενημερώσετε τα metadata του ticket θέτοντας τα κατάλληλα flags πάνω σε αυτό, πχ “has patch”, “doesn’t need tests” κλπ, ούτως ώστε άλλοι προγραμματιστές να μπορέσουν να το βρουν και να το κοιτάξουν και εκείνοι. Η συνεισφορά δεν σημαίνει πάντα να γράφετε ένα patch από το μηδέν. Αναθεωρώντας ήδη υπάρχοντα patches αποτελεί μια πολύ σημαντική συνεισφορά. Δείτε λεπτομέρειες στο Triaging tickets.

Back to Top