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

# Γράφοντας δικά σας πεδία μοντέλων

## Εισαγωγή

Το άρθρο μοντέλα εξηγεί πως να χρησιμοποιήσετε τις στάνταρντ κλάσεις πεδίων του Django – `CharField`, `DateField` κλπ. Στις περισσότερες περιπτώσεις αυτές οι κλάσεις θα σας είναι αρκετές. Ωστόσο, η έκδοση του Django που χρησιμοποιείτε ίσως να μην καλύπτει τις ανάγκες και τις απαιτήσεις σας ή θα θέλατε να χρησιμοποιήσετε ένα πεδίο μοντέλου το οποίο είναι εντελώς διαφορετικό από αυτά που έρχονται με το Django.

Οι τύποι των προεγκατεστημένων πεδίων του Django δεν καλύπτουν όλους τους πιθανούς τύπους στηλών μιας βάσης δεδομένων (υπόψιν ότι μερικές βάσεις έχουν αποκλειστικά δικούς τους τύπους στηλών που δεν βρίσκονται σε κάποια άλλη βάση) – μόνο τους βασικούς τύπους, όπως `VARCHAR` και `INTEGER`. Για περισσότερους τύπους στηλών, όπως γεωγραφικά πολύγονα ή ακόμη και τύπους που μπορεί να δημιουργήσει ο χρήστης όπως οι PostgreSQL custom types, μπορείτε να ορίσετε τις δικές σας Django `Field` subclasses.

Εναλλακτικά, μπορείτε να έχετε ένα πολύπλοκο Python object το οποίο να μπορεί κάπως να γίνει serialized για να μπορέσει να αναπαρασταθεί ως στάνταρντ στήλη στη βάση δεδομένων. Αυτή είναι μια άλλη περίπτωση όπου μια subclass της `Field` θα σας βοηθήσει να χρησιμοποιήσετε το object σας με τα μοντέλα σας.

### Το object του παραδείγματος μας

Η δημιουργία δικών μας παραμετροποιήσιμων πεδίων απαιτεί προσοχή στη λεπτομέρεια. Για να κάνουμε τα πράγματα πιο εύκολα, θα χρησιμοποιήσουμε το ίδιο παράδειγμα σε όλο αυτό το άρθρο: θα δημιουργήσουμε ένα Python object που θα αναπαριστά το μοίρασμα των χαρτιών σε μια παρτίδα Bridge. Μην ανησυχείτε, όμως, δεν χρειάζεται να γνωρίζετε πως παίζεται το Bridge για να καταλάβετε αυτό το παράδειγμα. Το μόνο που χρειάζεται να ξέρετε είναι ότι απαιτούνται 52 χαρτιά να μοιραστούν ισομερώς σε τέσσερις παίκτες, οι οποίοι ονομάζονται, παραδοσιακά, north, east, south και west. Η κλάση μας θα μοιάζει κάπως έτσι:

    class Hand(object):
        """A hand of cards (bridge style)"""

        def __init__(self, north, east, south, west):
            # Input parameters are lists of cards ('Ah', '9s', etc.)
            self.north = north
            self.east = east
            self.south = south
            self.west = west

        # ... (other possibly useful methods omitted) ...

Η κλάση που γράψαμε δεν είναι τίποτε άλλο παρά μια συνηθισμένη κλάση Python, χωρίς να την συνδέει κάτι με το Django. Θα θέλαμε να κάνουμε πράγματα, όπως αυτό, στα Django μοντέλα μας (υποθέτουμε ότι το attribute `hand` του μοντέλου είναι ένα instance της κλάσης `Hand`):

    example = MyModel.objects.get(pk=1)
    print(example.hand.north)

    new_hand = Hand(north, east, south, west)
    example.hand = new_hand
    example.save()

Εκχωρούμε στο και ανακτούμε από το attribute `hand` του μοντέλου μας όπως ακριβώς θα κάναμε με κάθε άλλη κλάση της Python. Το κόλπο είναι να πούμε στο Django πως να διαχειριστεί την αποθήκευση στην και τη φόρτωση από τη βάση δεδομένων αυτού του object.

Για να χρησιμοποιήσουμε την κλάση `Hand` στα μοντέλα μας, δεν χρειάζεται να αλλάξουμε αυτή την κλάση καθόλου. Αυτό είναι ιδανικό, καθώς σημαίνει ότι μπορείτε να γράψετε Django μοντέλα για ήδη υπάρχουσες κλάσεις χωρίς να χρειαστεί να πειράξετε τον πηγαίο κώδικα.

Σημείωση

Ίσως να θέλετε, μόνο, να επωφεληθείτε από τους δικούς σας τύπους στηλών στη βάση δεδομένων και να διαχειριστείτε τα δεδομένα σας ως στάνταρντ τύπους Python objects στα μοντέλα σας, πχ strings, floats κλπ. Αυτή η περίπτωση είναι παρόμοια με το παράδειγμα μας, `Hand`, όπου θα υποδεικνύουμε τυχόν διαφορές καθώς προχωράμε.

## Βασική θεωρία

### Αποθηκευτικός χώρος στη βάση δεδομένων

Ο πιο απλός τρόπος για να σκεφτείτε έναν τύπο μοντέλου (model field) είναι ότι προσφέρει έναν τρόπο ενός συνηθισμένου Python object – string, boolean, `datetime`, ή κάτι πιο περίπλοκο όπως το `Hand` – να μετατραπεί σε και από μια μορφή η οποία είναι χρήσιμη όταν επικοινωνούμε με τη βάση δεδομένων (και με το serialization, αλλά, όπως θα δούμε αργότερα, αυτό δεν θα σας απασχολήσει όταν έχετε τακτοποιήσει τη βάση).

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

Συνήθως, είτε θα γράφετε ένα πεδίο Django για να ταιριάζει με έναν συγκεκριμένο τύπο στήλης της βάσης είτε θα μετατρέπετε σε μορφή string, με κάποιον τρόπο, τα δεδομένα προς αποθήκευση στη βάση.

Για το δικό μας παράδειγμα `Hand`, θα μπορούσαμε να μετατρέψουμε τα δεδομένα των χαρτιών της τράπουλας σε ένα string 104 χαρακτήρων ενώνοντας (concatenating) όλα τα χαρτιά μεταξύ τους σε μια προκαθορισμένη σειρά – πχ, όλα τα north χαρτιά πρώτα, μετά τα east, μετά τα south και τέλος τα west. Επομένως, τα objects τύπου `Hand` θα μπορούσαν να αποθηκευτούν ως στήλη κειμένου (text) ή ως στήλη χαρακτήρων (character) στη βάση δεδομένων. Για άλλη μια φορά, επειδή η βάση δεν καταλαβαίνει την γλώσσα Python, θα πρέπει να μετατρέψουμε τα δεδομένα μας σε κάποια μορφή που η βάση καταλαβαίνει. Αντίστροφα, όταν αντλούμε δεδομένα από τη βάση, θα πρέπει να τα μετατρέψουμε σε Python objects για να τα διαχειριστούμε, ανάλογα τις ανάγκες μας.

### Τι κάνει μια κλάση τύπου Field;

Όλα τα πεδία του Django (και όταν λέμε πεδία σε αυτό το άρθρο, εννοούμε πάντα πεδία μοντέλων και όχι πεδία φορμών) είναι subclasses της κλάσης `django.db.models.Field`. Οι περισσότερες πληροφορίες που καταγράφει το Django σχετικά με ένα πεδίο είναι κοινές για όλα τα πεδία – όνομα, βοηθητικό κείμενο, μοναδικότητα κοκ. Η αποθήκευση όλων αυτών των πληροφοριών γίνεται από την κλάση `Field`. Θα πούμε με λεπτομέρειες τι μπορεί να κάνει η `Field` σε λίγο. Για τώρα, αρκεί να πούμε ότι όλα τα πεδία κληρονομούν από την κλάση `Field` και παραμετροποιούν διάφορα βασικά κομμάτια της συμπεριφοράς της.

Είναι σημαντικό να συνειδητοποιήσετε ότι η κλάση ενός πεδίου του Django (πχ `CharField`, `BooleanField` κλπ) δεν αποθηκεύεται στα attributes των μοντέλων σας. Τα attributes του μοντέλου περιέχουν συνηθισμένα Python objects. Η κλάσεις των πεδίων που ορίζετε σε ένα μοντέλο αποθηκεύονται στην ουσία σε μια `Meta` κλάση όταν δημιουργείται το μοντέλο (οι ακριβείς λεπτομέρειες του πως γίνεται αυτό, δεν έχουν σημασία τώρα). Αυτό γίνεται επειδή οι κλάσεις των πεδίων δεν είναι απαραίτητες όταν απλώς δημιουργείτε και αλλάζετε τα attributes. Αντ’ αυτού οι κλάσεις προσφέρουν τον μηχανισμό για την μετατροπή μεταξύ του attribute και αυτού που είναι αποθηκευμένο στη βάση δεδομένων ή για την αποστολή στον serializer.

Κρατήστε στο μυαλό σας τα ακόλουθα, όταν δημιουργείτε δικά σας πεδία. Η subclass `Field` του Django που γράφετε, σας προσφέρει έναν μηχανισμό μετατροπής μεταξύ Python instances και τιμών database/serializer με διάφορους τρόπους (πχ, υπάρχουν διαφορές μεταξύ της αποθήκευσης της τιμής και της χρησιμοποίησης της στα lookups). Αν αυτό ακούγεται κάπως περίπλοκο, μην ανησυχείτε – θα γίνει κατανοητό στα παραδείγματα παρακάτω. Να θυμάστε, μόνο, ότι συχνά θα καταλήγετε με τη δημιουργία δύο κλάσεων, όταν γράφετε δικά σας πεδία:

- Η πρώτη κλάση θα είναι το Python object που θα χρησιμοποιούν οι χρήστες. Θα το αναθέσουν σε ένα attribute του μοντέλου, θα διαβάζουν από αυτό για να βλέπουν τα δεδομένα που αντιστοιχούν σε αυτό, θα αποθηκεύουν δεδομένα μέσω αυτού κλπ. Αυτή η κλάση είναι η `Hand` στο παράδειγμα μας.

- Η δεύτερη κλάση θα είναι μια subclass της `Field`. Αυτή είναι μια κλάση η οποία ξέρει πως να μετατρέψει την πρώτη σας κλάση σε δεδομένα που καταλαβαίνει η βάση δεδομένων και αντίστροφα τα δεδομένα που είναι αποθηκευμένα στη βάση δεδομένων σε object που καταλαβαίνει η Python.

## Γράφοντας μια subclass ενός πεδίου

Όταν σχεδιάζετε τη subclass της κλάσης `Field` (το δικό σας νέο πεδίο, αν θέλετε), θα πρέπει πρώτα να σκεφτείτε με ποια από τις ήδη υπάρχουσες κλάσεις `Field` το νέο σας πεδίο μοιάζει περισσότερο. Ίσως να μπορείτε να κάνετε subclass ένα ήδη υπάρχον Django πεδίο και να γλυτώσετε τον εαυτό σας πολύτιμο χρόνο. Αν όχι, θα πρέπει να κάνετε subclass την κλάση `Field`, απ’ όπου όλα κληρονομούν.

Η αρχικοποίηση (initialization) του νέου σας πεδίου έχει να κάνει με τον διαχωρισμό των arguments που είναι συγκεκριμένα για αυτό που θέλετε να κάνει το πεδίο σας και των συνηθισμένων arguments. Στην μέθοδο `__init__()` της κλάσης `Field` (ή στην parent κλάση) περνάτε τα arguments που είναι συγκεκριμένα για το πεδίο σας.

Στο παράδειγμα μας, θα ονομάσουμε το πεδίο μας `HandField`. (Είναι, γενικά, καλή πρακτική να καλείτε τη subclass της `Field` ως `<Something>Field`, ούτως ώστε να εύκολα αναγνωρίσιμη ως μια subclass της `Field`). Η `HandField` δεν συμπεριφέρεται ως ένα ήδη υπάρχον Django πεδίο (πχ `CharField` ή `TextField`), οπότε θα κάνουμε subclass απ’ ευθείας από την `Field`:

    from django.db import models

    class HandField(models.Field):

        description = "A hand of cards (bridge style)"

        def __init__(self, *args, **kwargs):
            kwargs['max_length'] = 104
            super(HandField, self).__init__(*args, **kwargs)

Η `HandField` δέχεται τις περισσότερες από τις στάνταρντ επιλογές πεδίων (δείτε τη λίστα παρακάτω), αλλά εδώ εξασφαλίζουμε ότι έχει ένα συγκεκριμένο μέγιστο μήκος, αφού το μέγιστο που χρειάζεται είναι 52 τιμές φύλλων μαζί με το χρώμα του καθενός, πχ άσσος μπαστούνι (As, ace of spades) ή τρία κούπα (3h, 3 of hearts) κλπ: 104 χαρακτήρες στο σύνολο.

Σημείωση

Πολλά από τα πεδία των μοντέλων του Django δέχονται μερικές επιλογές με τις οποίες δεν κάνουν τίποτα, απλώς τις αγνοούν. Για παράδειγμα, μπορείτε να περάσετε την παράμετρο `editable` και την `auto_now` σε μια κλάση τύπου `django.db.models.DateField` και η παράμετρος `editable` απλά θα αγνοηθεί (όταν το `auto_now` είναι `True` προϋποθέτει ότι `editable=False`). Κανένα σφάλμα δεν θα γίνει raised σε αυτή την περίπτωση.

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

Η μέθοδος `Field.__init__()` δέχεται τις ακόλουθες παραμέτρους:

- `verbose_name`
- `name`
- `primary_key`
- `max_length`
- `unique`
- `blank`
- `null`
- `db_index`

- `rel`: Χρησιμοποιείται για συσχετισμένα πεδία (όπως το `ForeignKey`). Για προχωρημένους χρήστες μόνο.

- `default`
- `editable`

- `serialize`: Αν είναι `False`, το πεδίο δεν θα γίνει serialized όταν το μοντέλο περάσει στους serializers του Django. Η προεπιλεγμένη τιμή είναι `True`.

- `unique_for_date`
- `unique_for_month`
- `unique_for_year`
- `choices`
- `help_text`
- `db_column`

- `db_tablespace`: Μόνο για τη δημιουργία περιεχομένων (indexes), αν η βάση δεδομένων υποστηρίζει τα tablespaces. Τις πιο πολλές φορές, μπορείτε να αγνοείτε αυτή την επιλογή.

- `auto_created`: Είναι `True` αν το πεδίο δημιουργήθηκε αυτόματα, όπως το `OneToOneField` που χρησιμοποιείται από την κληρονομικότητα των μοντέλων. Για προχωρημένους χρήστες μόνο.

Όλες οι επιλογές χωρίς εξήγηση, στην ανωτέρω λίστα, έχουν την ίδια έννοια με τα συνηθισμένα πεδία του Django. Δείτε στο άρθρο εγχειρίδιο πεδίων των μοντέλων για παραδείγματα και λεπτομέρειες.

### Deconstruction του πεδίου

Όταν υλοποιείτε τη μέθοδο `__init__()` θα πρέπει να υλοποιήσετε και τη μέθοδο `deconstruct()`. Αυτή η μέθοδος λέει στο Django πως να μετατρέψει το instance του καινούργιου σας πεδίου σε μια σειριακή μορφή (serialized form) - πιο συγκεκριμένα, ποια arguments θα πρέπει να περαστούν στη μέθοδο `__init__()` για να ξαναφτιαχτεί το instance.

Αν δεν έχετε προσθέσει τυχόν έξτρα επιλογές στη μέθοδο `__init__()` του δικού σας πεδίου, δεν χρειάζεται να γράψετε κάποια νέα μέθοδο `deconstruct()`. Αν, ωστόσο, αλλάξετε τα arguments που περνούν στην `__init__()` (όπως κάναμε στην κλάση `HandField` που αλλάξαμε το `kwargs['max_length']`), θα χρειαστεί να προσθέσετε τις τιμές που περάσατε.

Το συμβόλαιο με την μέθοδο `deconstruct()` είναι απλό. Επιστρέφει ένα tuple τεσσάρων στοιχείων: το όνομα του attribute του field, το πλήρες import path της κλάσης του πεδίου, τα positional arguments (ως λίστα) και τα keyword arguments (ως dictionary). Σημειώστε ότι αυτή η μέθοδος είναι διαφορετική από τη μέθοδο `deconstruct()` όταν γράφεται δικές σας κλάσεις η οποία επιστρέφει ένα tuple τριών στοιχείων.

Ως συντάκτης του δικού σας πεδίου, δεν θα σας απασχολήσουν τα δύο πρώτα στοιχεία του tuple. Η κύρια κλάση της `Field` έχει όλο τον απαραίτητο κώδικα για να υπολογίσει το όνομα και το import path του πεδίου σας. Ωστόσο, σίγουρα θα σας απασχολήσουν τα positional και keyword arguments, αφού αυτά είναι τα στοιχεία που πιθανόν να αλλάξετε.

Για παράδειγμα, στην κλάση `HandField` θέτουμε το `max_length` μέσα στην `__init__()`. Η μέθοδος `deconstruct()` της κλάσης `Field` (την οποία καλούμε - `super(HandField, self).deconstruct()`) θα δει ότι έχει οριστεί το `max_length` και θα το επιστρέψει μέσα στο `kwargs` dictionary. Για λόγους αναγνωσιμότητας μπορούμε να το διαγράψουμε από το dictionary και έτσι το `kwargs` να είναι κενό. Όταν, επομένως, πάρουμε αυτό το tuple των τεσσάρων στοιχείων και προσπαθήσουμε να χτίσουμε ένα instance του `HandField` δεν έχει πολύ νόημα το `kwargs` να περιέχει το `max_length` αφού κατά το χτίσιμο του instance θα κληθεί η `__init__` η οποία το ορίζει μέσα της:

    from django.db import models

    class HandField(models.Field):

        def __init__(self, *args, **kwargs):
            kwargs['max_length'] = 104
            super(HandField, self).__init__(*args, **kwargs)

        def deconstruct(self):
            name, path, args, kwargs = super(HandField, self).deconstruct()
            del kwargs["max_length"]
            return name, path, args, kwargs

Αν προσθέσετε ένα νέο keyword argument, θα χρειαστεί να γράψετε κώδικα για να προσθέσετε την τιμή του στο `kwargs`:

    from django.db import models

    class CommaSepField(models.Field):
        "Implements comma-separated storage of lists"

        def __init__(self, separator=",", *args, **kwargs):
            self.separator = separator
            super(CommaSepField, self).__init__(*args, **kwargs)

        def deconstruct(self):
            name, path, args, kwargs = super(CommaSepField, self).deconstruct()
            # Only include kwarg if it's not the default
            if self.separator != ",":
                kwargs['separator'] = self.separator
            return name, path, args, kwargs

Πιο περίπλοκα παραδείγματα είναι πέρα από τους σκοπούς αυτού του άρθρου, αλλά να θυμάστε – για κάθε παραμετροποίηση του instance του Field, η μέθοδος `deconstruct()` πρέπει να επιστρέφει arguments τα οποία αν περάσετε στην `__init__` θα ξαναφτιάξετε το πεδίο σας.

Δώστε μεγάλη προσοχή στην περίπτωση που δώσετε τυχόν νέες προεπιλεγμένες τιμές στα arguments της superclass της `Field`. Πρέπει να εξασφαλίσετε ότι πάντοτε περιλαμβάνονται παρά εξαφανίζονται όταν λαμβάνουν την παλιά προεπιλεγμένη τιμή.

Επιπροσθέτως, προσπαθήστε να αποφύγετε να περάσετε τιμές ως positional arguments, όπου δυνατόν και αντί αυτού να επιστρέφετε τις τιμές ως keyword arguments για περισσότερη συμβατότητα στο μέλλον. Φυσικά, αν αλλάζετε τα ονόματα πιο συχνά από τη θέση τους στη λίστα των arguments του constructor, ίσως να προτιμήσετε τα positional arguments, αλλά έχετε στο μυαλό σας ότι άλλοι προγραμματιστές θα κάνουν reconstruct το πεδίο σας από την σειριακή έκδοση του για αρκετό καιρό (πιθανόν χρόνια), αναλόγως το χρόνο ζωής των migration σας.

Μπορείτε να δείτε τα αποτελέσματα του deconstruction κοιτάζοντας τα migrations τα οποία περιέχουν το πεδίο και μπορείτε, επίσης, να τεστάρετε το deconstruction σε unit tests απλώς κάνοντας deconstruct και reconstruct το πεδίο:

    name, path, args, kwargs = my_field_instance.deconstruct()
    new_instance = MyField(*args, **kwargs)
    self.assertEqual(my_field_instance.some_attribute, new_instance.some_attribute)

### Αλλάζοντας την base class του δικού σας πεδίου

Δεν μπορείτε να αλλάξετε την base class ενός παραμετροποιήσιμου πεδίου επειδή το Django δεν ανιχνεύει την αλλαγή και δεν θα δημιουργήσει migration για αυτή την αλλαγή. Για παράδειγμα αν ξεκινήσετε με αυτό:

    class CustomCharField(models.CharField):
        ...

και μετά αποφασίσετε ότι θέλετε να χρησιμοποιήσετε αντί αυτού ένα `TextField`, δεν μπορείτε να αλλάξετε την subclass όπως:

    class CustomCharField(models.TextField):
        ...

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

    class CustomCharField(models.CharField):
        ...

    class CustomTextField(models.TextField):
        ...

Όπως συζητήθηκε στο αφαιρώντας πεδία μοντέλων, θα πρέπει να διατηρήσετε την αρχική κλάση `CustomCharField` στον κώδικα σας όσο έχετε migrations τα οποία αναφέρονται σε αυτή.

### Τεκμηριώνοντας το δικό σας πεδίο

Όπως πάντα, θα πρέπει να τεκμηριώνετε (περιγραφικά και βοηθητικά σχόλια) τον τύπο του πεδίου σας, προκειμένου οι χρήστες αυτού να γνωρίζουν περί τίνος πρόκειται. Πέρα από το γεγονός του να γράψετε ένα docstring για αυτό, το οποίο είναι χρήσιμο για τους developers, μπορείτε επίσης να προσφέρετε στους χρήστες του Django admin να δουν μια μικρή περιγραφή του τύπου του πεδίου μέσω της εφαρμογής django.contrib.admindocs. Για να γίνει αυτό απλώς προσθέστε ένα περιγραφικό κείμενο στο class attribute `description` του δικού σας πεδίου. Στο παραπάνω παράδειγμα, η περιγραφή που εμφανίζεται από την εφαρμογή `admindocs` για το πεδίο `HandField` θα είναι η ‘A hand of cards (bridge style)’.

Στην εμφάνιση του module `django.contrib.admindocs`, η περιγραφή του πεδίου ενσωματώνεται στο `field.__dict__` το οποίο επιτρέπει στην περιγραφή να συνεργαστεί με arguments του πεδίου. Για παράδειγμα, η περιγραφή του `CharField` είναι η:

    description = _("String (up to %(max_length)s)")

### Χρήσιμες μέθοδοι

Όταν φτιάξετε την subclass της κλάσης `Field`, μπορείτε, αν θέλετε, να παρακάμψετε (override) μερικές στάνταρντ μεθόδους, ανάλογα τη συμπεριφορά του πεδίου σας. Η λίστα με τις μεθόδους, παρακάτω, είναι περίπου κατά φθίνουσα σειρά σπουδαιότητας, οπότε ξεκινήστε από πάνω προς τα κάτω.

#### Δικά σας πεδία βάσης δεδομένων

Ας υποθέσουμς ότι έχετε δημιουργήσει ένα δικό σας πεδίο στη PostgreSQL με το όνομα `mytype`. Μπορείτε να κάνετε subclass τη κλάση `Field` και να υλοποιήσετε τη μέθοδο `db_type()`, ως εξής:

    from django.db import models

    class MytypeField(models.Field):
        def db_type(self, connection):
            return 'mytype'

Όταν θα έχετε γράψει το `MytypeField`, μπορείτε να το χρησιμοποιήσετε σε οποιοδήποτε από τα μοντέλα σας, όπως ακριβώς με οποιοδήποτε άλλο τύπο `Field`:

    class Person(models.Model):
        name = models.CharField(max_length=80)
        something_else = MytypeField()

Αν σκοπεύετε να φτιάξετε μια εφαρμογή η οποία είναι ανεξάρτητη από τη βάση δεδομένων που χρησιμοποιείται, θα πρέπει να λάβετε υπόψη σας τις διαφορές τύπων στηλών μεταξύ των βάσεων δεδομένων. Για παράδειγμα, η στήλη τύπου date/time στην PostgreSQL καλείτε `timestamp`, ενώ η ίδια στήλη στη MySQL καλείται `datetime`. Ο πιο απλός τρόπος να χειριστείτε αυτή την κατάσταση μέσα στη μέθοδο `db_type()` είναι να ελέγξετε το attribute `connection.settings_dict['ENGINE']`.

Για παράδειγμα:

    class MyDateField(models.Field):
        def db_type(self, connection):
            if connection.settings_dict['ENGINE'] == 'django.db.backends.mysql':
                return 'datetime'
            else:
                return 'timestamp'

Οι μέθοδοι `db_type()` και `rel_db_type()` καλούνται από το Django όταν το framework φτιάχνει τα `CREATE TABLE` statements για την εφαρμογή σας – δηλαδή, στο αρχικό στάδιο δημιουργίας των πινάκων σας. Οι μέθοδοι, καλούνται, επίσης, όταν δημιουργείται μια `WHERE` εντολή η οποία περιλαμβάνει το πεδίο του μοντέλου – δηλαδή, όταν αντλείτε δεδομένα χρησιμοποιώντας μεθόδους QuerySet όπως η `get()`, `filter()` και `exclude()` και έχετε περασμένο το πεδίο του μοντέλου ως argument. Οι μέθοδοι αυτές δεν καλούνται κάποια άλλη στιγμή, οπότε αξίζει να εκτελέσουν έναν ελαφρά περίπλοκο κώδικα, όπως το να ελέγξουν το `connection.settings_dict` όπως στο παραπάνω παράδειγμα.

Μερικοί τύποι στηλών βάσης δεδομένων δέχονται παραμέτρους, όπως `CHAR(25)`, όπου η παράμετρος `25` αναπαριστά το μέγιστο μήκος χαρακτήρων της στήλης. Σε παρόμοιες περιπτώσεις, είναι πιο ευέλικτο η παράμετρος να δηλωθεί στο μοντέλο (ως `max_length` attribute) παρά να γραφεί με το χέρι μέσα στη μέθοδο `db_type()`. Για παράδειγμα, δεν θα είχε πολύ νόημα να είχατε ένα πεδίο `CharMaxlength25Field`, όπως φαίνεται εδώ:

    # This is a silly example of hard-coded parameters.
    class CharMaxlength25Field(models.Field):
        def db_type(self, connection):
            return 'char(25)'

    # In the model:
    class MyModel(models.Model):
        # ...
        my_field = CharMaxlength25Field()

Ένας καλύτερος τρόπος είναι να προσδιορίσετε την παράμετρο κατά τη διάρκεια του run time – πχ, όταν η κλάση αρχικοποιείται. Για να το κάνετε αυτό, απλώς υλοποιήστε την `Field.__init__()`, ως εξής (εδώ περνάμε την παράμετρο max_length ως positional argument):

    # This is a much more flexible example.
    class BetterCharField(models.Field):
        def __init__(self, max_length, *args, **kwargs):
            self.max_length = max_length
            super(BetterCharField, self).__init__(*args, **kwargs)

        def db_type(self, connection):
            return 'char(%s)' % self.max_length

    # In the model:
    class MyModel(models.Model):
        # ...
        my_field = BetterCharField(25)

Εν τέλει, αν η στήλη σας απαιτεί πραγματικά περίπλοκο SQL στήσιμο, επιστρέψτε `None` από την μέθοδο `db_type()`. Αυτό θα επιτρέψει στον κώδικα του Django που παράγει την SQL να προσπεράσει αυτό το πεδίο. Είστε, πλέον, υπεύθυνοι να δημιουργήσετε εσείς τη στήλη στο σωστό πίνακα με κάποιον άλλο τρόπο. Αυτό, φυσικά, λέει στο Django να βγει από τη μέση.

Η μέθοδος `rel_db_type()` καλείται από πεδία τύπου `ForeignKey` και `OneToOneField` τα οποία δείχνουν σε ένα άλλο πεδίο για να καθορίσουν τον τύπο δεδομένων της στήλης. Για παράδειγμα, αν έχετε ένα πεδίο τύπου `UnsignedAutoField`, χρειάζεστε επίσης τα foreign keys τα οποία δείχνουν σε αυτό το πεδίο για να χρησιμοποιηθεί ο ίδιος τύπος δεδομένων:

    # MySQL unsigned integer (range 0 to 4294967295).
    class UnsignedAutoField(models.AutoField):
        def db_type(self, connection):
            return 'integer UNSIGNED AUTO_INCREMENT'

        def rel_db_type(self, connection):
            return 'integer UNSIGNED'

Προστέθηκε η μέθοδος `rel_db_type()`.

#### Μετατρέποντας τιμές σε Python objects

Αν η δικιά σας κλάση `Field` ασχολείται με δομές δεδομένων (data structures) οι οποίες είναι πιο περίπλοκες από strings, dates, integers, ή floats, τότε ίσως χρειαστεί να παρακάμψετε (override) τις μέθοδους `from_db_value()` και `to_python()`.

Αν η μέθοδος `from_db_value()` έχει υλοποιηθεί στη subclass του πεδίου, τότε θα καλείται σε όλες τις περιπτώσεις που τα δεδομένα φορτώνονται από τη βάση δεδομένων, συμπεριλαμβανομένων των κλήσεων των aggregate και `values()`.

Η μέθοδος `to_python()` καλείται κατά το deserialization και κατά τη διάρκεια της μεθόδου `clean()` που χρησιμοποιείται από τις φόρμες.

Ως γενικός κανόνας, η μέθοδος `to_python()` θα πρέπει συνεργάζεται με οποιοδήποτε από τα ακόλουθα arguments:

- Ένα instance ενός σωστού τύπου (πχ, του `Hand` στο τρέχων παράδειγμα μας).

- Ένα string

- `None` (αν το πεδίο επιτρέπει `null=True`)

Στην κλάση `HandField`, αποθηκεύουμε τα δεδομένα ως ένα πεδίο VARCHAR στη βάση δεδομένων, οπότε θα πρέπει να είμαστε σε θέση να επεξεργαστούμε τύπου strings και τύπου `None` στη μέθοδο `from_db_value()`. Στη μέθοδο `to_python()`, θα πρέπει, επίσης, να δουλέψουμε με instances της κλάσης `Hand`:

    import re

    from django.core.exceptions import ValidationError
    from django.db import models
    from django.utils.translation import ugettext_lazy as _

    def parse_hand(hand_string):
        """Takes a string of cards and splits into a full hand."""
        p1 = re.compile('.{26}')
        p2 = re.compile('..')
        args = [p2.findall(x) for x in p1.findall(hand_string)]
        if len(args) != 4:
            raise ValidationError(_("Invalid input for a Hand instance"))
        return Hand(*args)

    class HandField(models.Field):
        # ...

        def from_db_value(self, value, expression, connection, context):
            if value is None:
                return value
            return parse_hand(value)

        def to_python(self, value):
            if isinstance(value, Hand):
                return value

            if value is None:
                return value

            return parse_hand(value)

Σημειώστε ότι πάντα επιστρέφουμε ένα instance του `Hand` από αυτές τις μεθόδους. Αυτός είναι ο τύπος του Python object που θέλουμε να αποθηκεύσουμε στο attribute του model.

Όσον αφορά τη μέθοδο `to_python()`, αν κάτι πάει στραβά κατά τη μετατροπή της τιμής, θα πρέπει να κάνετε raise ένα `ValidationError` exception (όπως κάνουμε μέσα στην ``parse_hand ``).

#### Μετατρέποντας τα Python objects σε query values

Εφόσον χρησιμοποιούμε μια βάση δεδομέμων θα πρέπει, όπως είπαμε, η μετατροπή των δεδομένων να γίνεται μεταξύ Python και βάσης και αντίστροφα. Αν παρακάμψετε (override) τη μέθοδο `to_python()` θα χρειαστεί, επίσης, να παρακάμψετε τη μέθοδο `get_prep_value()` για να μετατρέψετε τα Python objects σε query values, δηλαδή τιμές που καταλαβαίνει η βάση δεδομένων. Η μέθοδος αυτή θα καλείται κάθε φορά που θα ερωτάται η βάση, μέσω κάποιου query, πχ `MyModel.objects.filter(...)` ή `MyModel.objects.get(...)` κλπ. Με άλλα λόγια, η μέθοδος αυτή προετοιμάζει την τιμή που δόθηκε στο query (η οποία είναι ένα Python object, πχ `MyModel.objects.filter(hand=Hand('...26chars', '...26chars', '...26chars', '...26chars')`) σε μια τιμή που καταλαβαίνει η βάση δεδομένων (στην προκειμένη, ένα string με όλα τα φύλλα της τράπουλας ανά παίκτη, πχ ‘Js6h3s5d6d...’).

Για παράδειγμα:

    class HandField(models.Field):
        # ...

        def get_prep_value(self, value):
            return ''.join([''.join(l) for l in (value.north,
                    value.east, value.south, value.west)])

Προειδοποίηση

Αν τα δικά σας πεδία χρησιμοποιούν του τύπους `CHAR`, `VARCHAR` ή `TEXT` της MySQL, θα πρέπει να σιγουρευτείτε ότι η μέθοδος `get_prep_value()` επιστρέφει πάντα μεταβλητές τύπου string. Όταν κάνετε ένα ερώτημα στη MySQL σχετικά με αυτά τα πεδία και η τιμή που δίνετε στο query είναι τύπου integer τότε η MySQL συμπεριφέρεται κάπως αναπάντεχα και τα αποτελέσματα από το query να μην είναι αυτά που περιμένετε. Αυτό το πρόβλημα δεν θα υπάρξει αν επιστρέφετε, πάντα, μια μεταβλητή τύπου string στη μέθοδο `get_prep_value()`.

#### Μετατρέποντας τα query values σε database values

Κάποιοι τύποι δεδομένων (πχ ημερομηνίες) χρειάζεται να είναι σε κάποια συγκεκριμένη μορφή πριν χρησιμοποιηθούν με κάποια βάση δεδομένων. Η μέθοδος `get_db_prep_value()` είναι το μέρος όπου αυτές οι μετατροπές πρέπει να γίνουν. Η σύνδεση (της βάση δεδομένων) που χρησιμοποιείται με το query περνάει ως το όρισμα με το όνομα `connection`. Αυτό σας επιτρέπει να χρησιμοποιείτε διαφορετική λογική μετατροπής με διαφορετικούς τύπους βάσης δεδομένων, αν αυτό είναι απαραίτητο.

Για παράδειγμα, το Django χρησιμοποιεί την ακόλουθη μέθοδο για πεδία τύπου `BinaryField`:

    def get_db_prep_value(self, value, connection, prepared=False):
        value = super(BinaryField, self).get_db_prep_value(value, connection, prepared)
        if value is not None:
            return connection.Database.Binary(value)
        return value

Σε περίπτωση που το δικό σας πεδίο χρειάζεται μια ξεχωριστή μετατροπή όταν αποθηκεύεται, δηλαδή όχι την ίδια μετατροπή που χρησιμοποιείται για συνηθισμένες παραμέτρους ενός query, μπορείτε να παρακάμψετε (override) τη μέθοδο `get_db_prep_save()`.

#### Επεξεργασία τιμών πριν την αποθήκευση

Αν θέλετε να επεξεργαστείτε την τιμή ακριβώς πριν την αποθήκευση της (στη βάση δεδομένων), μπορείτε να χρησιμοποιήσετε τη μέθοδο `pre_save()`. Για παράδειγμα, η κλάση του Django `DateTimeField` χρησιμοποιεί αυτή τη μέθοδο για να θέσει το attribute στη σωστή τιμή, όταν το `auto_now` ή το `auto_now_add` είναι `True`.

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

#### Προσδιορίζοντας το πεδίο της φόρμας για ένα πεδίο μοντέλου

Για να παραμετροποιήσετε το πεδίο της φόρμας (form field) που χρησιμοποιείται από την κλάση `ModelForm`, μπορείτε να παρακάμψετε (override) τη μέθοδο `formfield()`.

Η κλάση του πεδίου της φόρμας μπορεί να οριστεί μέσω των arguments `form_class` και `choices_form_class`. Το τελευταίο χρησιμοποιείται σε περίπτωση που το πεδίο έχει ορίσει την παράμετρο choices, ενώ το πρώτο όχι. Αν αυτά τα arguments δεν δηλωθούν, τότε το πεδίο `CharField` ή `TypedChoiceField` θα χρησιμοποίηθεί.

Ολόκληρο το `kwargs` dictionary περνάει ως όρισμα απ’ ευθείας στη μέθοδο `__init__()` του πεδίου της φόρμας. Συνήθως, το μόνο που πρέπει να κάνετε είναι να θέσετε ένα καλό `kwargs` ως προεπιλογή για το argument `form_class` (και ίσως για το `choices_form_class`) και μετά να δώσετε τον έλεγχο στην parent class. Αυτό ίσως απαιτήσει να γράψετε ένα δικό σας πεδίο φόρμας (και ίσως ένα widget φόρμας). Δείτε στο εγχειρίδιο για φόρμες, για πληροφορίες σχετικά με αυτό.

Συνεχίζοντας το παράδειγμα μας, μπορούμε να γράψουμε τη μέθοδο `formfield()` ως εξής:

    class HandField(models.Field):
        # ...

        def formfield(self, **kwargs):
            # This is a fairly standard way to set up some defaults
            # while letting the caller override them.
            defaults = {'form_class': MyFormField}
            defaults.update(kwargs)
            return super(HandField, self).formfield(**defaults)

Αυτό προϋποθέτει ότι έχουμε κάνει import μια κλάση φόρμας `MyFormField` (η οποία έχει το δικό της προεπιλεγμένο widget). Το παρών άρθρο δεν καλύπτει τη συγγραφή δικών σας πεδίων για φόρμες.

#### Προσομοιώνοντας προεγκατεστημένους (built-in) τύπους πεδίων

Αν έχετε δημιουργήσει μια μέθοδο `db_type()`, δεν θα χρειαστεί να ανησυχείτε για τη μέθοδο `get_internal_type()` – δεν θα χρησιμοποιηθεί πολύ. Μερικές φορές, όμως, η μονάδα αποθήκευσης της βάσης δεδομένων σας είναι παρόμοια, σε επίπεδο τύπου, με κάποιο άλλο πεδίο, οπότε μπορείτε να χρησιμοποιήσετε τη λογική αυτού του άλλου πεδίου για να δημιουργήσετε τη σωστή τύπου στήλη.

Για παράδειγμα:

    class HandField(models.Field):
        # ...

        def get_internal_type(self):
            return 'CharField'

Ανεξαρτήτως της βάσης δεδομένων που χρησιμοποιούμε, αυτό θα σημαίνει ότι η εντολή `migrate` και άλλες SQL εντολές, θα χρησιμοποιούν τη σωστή τύπου στήλη για την αποθήκευση ενός string.

Αν η μέθοδος `get_internal_type()` επιστρέφει ένα string το οποίο δεν είναι γνωστό στο Django για την βάση δεδομένων που χρησιμοποιείται – δηλαδή, δεν εμφανίζεται στο `django.db.backends.<db_name>.base.DatabaseWrapper.data_types` – το string θα εξακολουθήσει να χρησιμοποιείται από τον serializer, αλλά η προεπιλεγμένη μέθοδος `db_type()` θα επιστρέψει `None`. Δείτε το εγχειρίδιο της μεθόδου `db_type()` για περιπτώσεις που αυτό μπορεί να φανεί χρήσιμο. Είναι μια καλή ιδέα να θέσετε ένα όμορφα περιγραφικό string ως τύπο πεδίου για τον serializer αν πρόκειται να χρησιμοποιήσετε την έξοδο του serializer σε κάποιο άλλο μέρος, εκτός του Django.

#### Μετατρέποντας τα δεδομένα του πεδίου για το serialization

Για την παραμετροποίηση των τιμών ούτως ώστε να γίνουν serialized από τον serializer, μπορείτε να παρακάμψετε (override) τη μέθοδο `value_to_string()`. Η χρήση της `value_from_object()` είναι ο καλύτερος τρόπος να πάρετε την τιμή του πεδίου πριν το serialization. Για παράδειγμα, εφόσον το `HandField` χρησιμοποιεί strings για την αποθήκευση των δεδομένων του, μπορούμε να επαναχρησιμοποιήσουμε τον ήδη υπάρχον κώδικα που θα κάνει τη μετατροπή (`get_prep_value()`):

    class HandField(models.Field):
        # ...

        def value_to_string(self, obj):
            value = self.value_from_object(obj)
            return self.get_prep_value(value)

### Μερικές γενικές συμβουλές

Η συγγραφή ενός δικού σας πεδίου μπορεί να είναι μια περίπλοκη διαδικασία, ειδικά όταν πραγματοποιείτε περίπλοκες μετατροπές μεταξύ Python objects και μορφών της βάσης δεδομένων και του serialization. Παρακάτω παρουσιάζονται δύο συμβουλές για να ομαλοποιήσουν τα πράγματα:

1. Κοιτάξτε στα ήδη υπάρχοντα πεδία του Django (μέσα στο αρχείο `django/db/models/fields/__init__.py`) για έμπνευση. Προσπαθήστε να βρείτε ένα πεδίο που να έχει ομοιότητες με αυτό που προσπαθήστε να επεκτείνετε, αντί να δημιουργήσετε ένα εντελώς νέο πεδίο από την αρχή.

2. Υλοποιήστε τη μέθοδο `__str__()` (την `__unicode__()` αν δουλεύετε με την Python 2) στην κλάση που δηλώνεται ως πεδίο. Υπάρχουν πολλά μέρη όπου η προεπιλεγμένη συμπεριφορά του κώδικα του πεδίου καλεί τη συνάρτηση `force_text()` χρησιμοποιώντας την τιμή (στα παραδείγματα μας σε αυτό το άρθρο, η τιμή, `value`, θα είναι ένα instance της κλάσης `Hand` και όχι της `HandField`). Οπότε αν η μέθοδος `__str__()` (`__unicode__()` στην Python 2) μετατρέπει αυτόματα το Python object σε μια μορφή string, θα γλυτώσετε πολύ χρόνο.

## Γράφοντας μια subclass ενός πεδίου τύπου `FileField`

Επιπροσθέτως των παραπάνω μεθόδων, τα πεδια που ασχολούνται με αρχεία έχουν μερικές ακόμη απαιτήσεις, τις οποίες πρέπει να λάβετε υπόψιν σας. Η πλειονότητα των μηχανισμών που προσφέρει ένα πεδίο τύπου `FileField`, όπως ο έλεγχος της μονάδας αποθήκευσης της βάσης δεδομένων (database storage) καθώς και της ανάκτησης, μπορεί να μείνει ανέπαφος, αφήνοντας τις subclasses να χειριστούν την πρόκληση της υποστήριξης ενός συγκεκριμένου τύπου αρχείου.

Το Django προσφέρει μια κλάση `File`, που χρησιμοποιείται ως μεσάζων (proxy) στα περιεχόμενα και λειτουργίες του αρχείου. Αυτή η κλάση μπορεί να γίνει subclass προκειμένου να παραμετροποιηθεί η πρόσβαση στο αρχείο και το ποιες μέθοδοι θα είναι διαθέσιμες. Βρίσκεται στο `django.db.models.fields.files` και η προεπιλεγμένη συμπεριφορά του εξηγείται στο εγχειρίδιο για αρχεία.

Όταν μια subclass της κλάσης `File` δημιουργηθεί, η νέα subclass `FileField` θα πρέπει να ξέρει πως να χρησιμοποιήσει τη subclass της `File`. Για να γίνει αυτό, πρέπει να ορίσετε το ξεχωριστό attribute `attr_class` της `FileField` στην τιμή της νέας subclass της `File`.

### Μερικές προτάσεις

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

1. Ο πηγαίος κώδικας του πεδίου `ImageField` του Django (βρίσκεται στο αρχείο `django/db/models/fields/files.py`) είναι ένα καλό παράδειγμα του πως να κάνετε subclass την κλάση `FileField` για να υποστηρίξετε έναν συγκεκριμένο τύπο αρχείου, καθώς υιοθετεί όλες τις τεχνικές που αναφέρθηκαν παραπάνω.

2. Όσο μπορείτε να κάνετε cache τα attributes του αρχείου. Εφόσον τα αρχεία μπορεί να αποθηκεύονται σε απομακρυσμένα συστήματα αποθήκευσης, η ανάκτηση τους μπορεί να κοστίσει επιπλέον άσκοπο χρόνο ή ακόμη και χρήματα. Όταν ένα αρχείο ανακτάται ούτως ώστε να δείτε κάποιες πληροφορίες σχετικά με τα περιεχόμενα του, κάντε cache (αποθηκεύστε τα στη λανθάνουσα μνήμη) όσα περισσότερα δεδομένα μπορείτε για να μειώσετε τον αριθμό μελλοντικών προσπελάσεων του αρχείου για την απόκτηση των ίδιων πληροφοριών.