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

# Παραμετροποιήσιμα Lookups

Το Django προσφέρει μια μεγάλη ποικιλία από προεγκατεστημένα lookups για το φιλτράρισμα (π.χ `exact` και `icontains`) των δεδομένων σας από την βάση δεδομένων. Το παρών εγχειρίδιο εξηγεί πως να γράψετε δικά σας lookups και πως να αλλάξετε τη συμπεριφορά των ήδη υπαρχουσών lookups. Για την σχετική αναφορά στο API των lookups, δείτε στο άρθρο Lookup API reference.

## Ένα απλό lookup παράδειγμα

Ας ξεκινήσουμε με ένα απλό παραμετροποιήσιμο lookup. Θα γράψουμε ένα δικό μας lookup και θα του δώσουμε το όνομα `ne` (από τις λέξεις not equal), το οποίο θα έχει την ανάποδη συμπεριφορά του `exact`. Για παράδειγμα, αν γράψουμε `Author.objects.filter(name__ne='Jack')`, τότε αυτό θα μεταφραστεί στην SQL ως:

    "author"."name" <> 'Jack'

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

Υπάρχουν δύο βήματα που πρέπει να κάνουμε για να δουλέψει το ανωτέρω. Πρώτα, πρέπει να υλοποιήσουμε το lookup και μετά πρέπει να το αναφέρουμε στο Django. Η υλοποίηση είναι λίγο πολύ ευθέως κατανοητή:

    from django.db.models import Lookup

    class NotEqual(Lookup):
        lookup_name = 'ne'

        def as_sql(self, compiler, connection):
            lhs, lhs_params = self.process_lhs(compiler, connection)
            rhs, rhs_params = self.process_rhs(compiler, connection)
            params = lhs_params + rhs_params
            return '%s <> %s' % (lhs, rhs), params

Σε αυτό το σημείο απλώς έχουμε ορίσει μια κλάση. Τίποτε περισσότερο. Επόμενο βήμα είναι κάνουμε register το νέο μας `NotEqual` lookup με κάποιο πεδίο από τα Django μοντέλα ή με όλα. Θα χρειαστεί να καλέσουμε την `register_lookup` πάνω στο πεδίο που θέλουμε αυτό το lookup να είναι διαθέσιμο. Αυτό γίνεται, γιατί σε μερικές περιπτώσεις δεν θέλουμε να έχουμε διαθέσιμο (να φιλτράρουμε, αν θέλετε) το lookup μας σε όλα τα πεδία (πχ IntegerField, DateField κλπ). Αν φτιάξουμε ένα άλλο lookup που να ελέγχει, πχ το πρώτο γράμμα ενός string δεν θα ήταν λογικό να το συνδέσουμε με ένα DateTimeField. Στην προκειμένη περίπτωση, το lookup μπορεί να εφαρμοστεί σε όλων των ειδών τα `Field` subclasses, οπότε θα το κάνουμε register με το `Field` απ’ ευθείας:

    from django.db.models.fields import Field
    Field.register_lookup(NotEqual)

Το registration του lookup μπορεί να γίνει και με τη χρήση ενός decorator ως εξής:

    from django.db.models.fields import Field

    @Field.register_lookup
    class NotEqualLookup(Lookup):
        # ...

Τώρα μπορούμε να χρησιμοποιήσουμε το `foo__ne` για κάθε τύπο πεδίου του `foo`. Θα χρειαστεί να βεβαιωθείτε ότι το registration του lookup, γίνεται πριν προσπαθήσετε να δημιουργήσετε querysets χρησιμοποιώντας το. Θα μπορούσατε να τοποθετήσετε τον παραπάνω κώδικα μέσα στο αρχείο `models.py` ή να κάνετε register το lookup μέσα στη μέθοδο `ready()` στο αντίστοιχο `AppConfig` της εφαρμογής σας.

Ρίχνοντας μια πιο προσεκτική ματιά στην υλοποίηση αυτού του lookup, το πρώτο απαραίτητο attribute που χρειάζεται είναι το `lookup_name`. Αυτό επιτρέπει στο Django ORM να καταλάβει πως να μεταφράσει το `name__ne` και να χρησιμοποιήσει την κλάση `NotEqual` για να παράξει την SQL. Συμβατικά, αυτά τα ονόματα είναι πάντα πεζοί χαρακτήρες (μόνο γράμματα) και η μοναδική αυστηρή απαίτηση είναι να μην περιέχουν δύο συνεχόμενες κάτω παύλες, `__`.

Έπειτα χρειάζεται να ορίσουμε την μέθοδο `as_sql`. Αυτή δέχεται ως όρισμα ένα `SQLCompiler` object, με το όνομα `compiler` και την ενεργή σύνδεση της βάσης δεδομένων με το όνομα `connection`. Τα `SQLCompiler` objects δεν βρίσκονται κάπου γραμμένα στο εγχειρίδιο του Django αλλά το μόνο πράγμα που χρειάζεται να ξέρετε γι’ αυτά είναι ότι έχουν μια μέθοδο `compile()` η οποία επιστρέφει ένα tuple που περιέχει ένα SQL string και κάποιες παραμέτρους οι οποίες “γεμίζουν” αυτό το SQL string. Στις περισσότερες περιπτώσεις δεν θα χρειαστεί να την χρησιμοποιήσετε και μπορείτε να συνεχίσετε με τις μεθόδους `process_lhs()` και `process_rhs()`.

Ένα `Lookup` συνεργάζεται με δύο τιμές, την `lhs` και την `rhs`, οι οποίες είναι σύντμηση των λέξεων left-hand side (αριστερό μέρος) και right-hand side (δεξί μέρος). Το αριστερό μέρος αναφέρεται συνήθως σε κάποιο πεδίο αλλά μπορεί να είναι οτιδήποτε αρκεί να έχει υλοποιήσει το API για εκφράσεις ερωτημάτων. Το δεξί μέρος είναι η τιμή η οποία δίνεται από τον χρήστη. Στο παράδειγμα `Author.objects.filter(name__ne='Jack')`, το αριστερό μέρος αναφέρεται στο πεδίο `name` του μοντέλου `Author` και το string `'Jack'` αποτελεί το δεξί μέρος.

Καλούμε την `process_lhs` και την `process_rhs` για να μετατρέψουμε το `Author.objects.filter(name__ne='Jack')` στις τιμές που χρειαζόμαστε για την SQL χρησιμοποιώντας το `compiler` object που περιγράψαμε παραπάνω. Αυτές οι μέθοδοι επιστρέφουν tuples που περιέχουν λίγη SQL και κάποιες παραμέτρους που θα “γεμίσουν” αυτή την SQL. Ότι ακριβώς χρειαζόμαστε για να επιστρέψει η μέθοδος `as_sql`. Στο παραπάνω παράδειγμα, η `process_lhs` επιστρέφει `('"author"."name"', [])` και η `process_rhs` επιστρέφει `('"%s"', ['Jack'])`. Όπως θα είδατε, δεν υπάρχουν παράμετροι για το αριστερό μέρος, αλλά αυτό εξαρτάται από την περίπτωση. Όπως και να έχει πάντως, θα πρέπει πάντα να περιλαμβάνονται στο τελικό tuple προς επιστροφή (η μεταβλητή `params` στην `as_sql`).

Στο τέλος ενώνουμε τα μέρη σε μια SQL εντολή με τη χρήση του `<>` και παρέχουμε όλες τις παραμέτρους για το query (ερώτημα). Αυτό που επιστρέφουμε είναι ένα tuple που περιέχει την παραγόμενη SQL εντολή και τις παραμέτρους, κενές ή όχι. Το tuple που θα επιστραφεί, αναφερόμενοι στην ανωτέρω `as_sql`, είναι το `('"author"."name" <> "%s"', ['Jack'])`.

## Ένα απλό παράδειγμα transformer

Το παραπάνω lookup που φτιάξαμε είναι ωραίο, αλλά σε αρκετές περιπτώσεις θα θέλετε να έχετε τη δυνατότητα να ενώνετε τα lookups μεταξύ τους. Για παράδειγμα, ας υποθέσουμε ότι χτίζουμε μια εφαρμογή όπου θέλουμε να κάνουμε χρήση της μεθόδου `abs()`, που επιστρέφει το απόλυτο ενός αριθμού. Έχουμε ένα μοντέλο με το όνομα `Experiment` το οποίο καταγράφει μια τιμή έναρξης (start), μια τέλους (end) και την αλλαγή (αρχή – τέλος). Θα θέλαμε να βρούμε όλα τα πειράματα (experiments) όπου η αλλαγή ήταν ίση με μια συγκεκριμένη τιμή (`Experiment.objects.filter(change__abs=27)`) ή η αλλαγή ήταν μικρότερη ή ίση μιας συγκεκριμένης τιμής (`Experiment.objects.filter(change__abs__lt=27)`). Εδώ βλέπετε τη σύνδεση μεταξύ των lookups που αναφέραμε πριν (`abs__lt`).

Σημείωση

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

Θα ξεκινήσουμε γράφοντας έναν `AbsoluteValue` transformer (στην ουσία είναι μια δικιά μας κλάση που κληρονομεί από την κλάση `Transform`). Η κλάση αυτή θα χρησιμοποιήσει την SQL συνάρτηση `ABS()` για να μετατρέψει την τιμή πριν την συγκρίνει:

    from django.db.models import Transform

    class AbsoluteValue(Transform):
        lookup_name = 'abs'
        function = 'ABS'

Επόμενο βήμα είναι κάνουμε register την κλάση μας με το πεδίο `IntegerField`:

    from django.db.models import IntegerField
    IntegerField.register_lookup(AbsoluteValue)

Μπορούμε τώρα να τρέξουμε τα queries στα οποία αναφερθήκαμε παραπάνω. Το `Experiment.objects.filter(change__abs=27)` θα παράξει την ακόλουθη SQL εντολή:

    SELECT ... WHERE ABS("experiments"."change") = 27

Χρησιμοποιώντας την `Transform` αντί της `Lookup` μας επιτρέπει να συνδέουμε περαιτέρω lookups στην αλυσίδα. Επομένως το `Experiment.objects.filter(change__abs__lt=27)` θα παράξει την ακόλουθη SQL εντολή:

    SELECT ... WHERE ABS("experiments"."change") < 27

Σημειώστε ότι στην περίπτωση που δεν υπάρχει άλλο lookup στο τέλος, το Django μεταφράζει το `change__abs=27` ως `change__abs__exact=27`.

Για να βρει το Django τον τύπο των lookups που επιτρέπονται μετά το δικό μας (`AbsoluteValue`), κοιτάζει το attribute `output_field` το οποίο δεν χρειάστηκε να το ορίσουμε στο συγκεκριμένο παράδειγμα γιατί έτυχε ο αριθμός που συγκρίνουμε, στο δεξί μέρος, να είναι ακέραιος (27). Αν μπερδευτήκατε, ας το ξεκαθαρίσουμε: Η `Transform` προσφέρει τη μετατροπή ενός πεδίου (πχ του `change`) σε κάποια άλλη τιμή (πχ από -27 που μπορεί να ήταν, σε 27) προτού συγκριθεί με το δεξί μέρος και γίνει το lookup στη βάση δεδομένων. Ο τύπος της νέας αυτής τιμής (πχ 27 αντί -27) δηλώνεται στο `output_field` του δικού μας `AbsoluteValue ``. Δεν το δηλώσαμε γιατί και οι δύο τιμές είναι τύπου int. Αν υποθέσουμε όμως ότι έχουμε πιο περίπλοκα πεδία (πχ καρτεσιανές συντεταγμένες ή μιγαδικούς αριθμούς) τότε ίσως θα θέλαμε να προσδιορίσουμε τον τύπο της νέας τιμής ως ``FloatField` για περαιτέρω lookups. Αυτό μπορεί να γίνει θέτοντας το attribute `` output_field`` ως property της δικής μας κλάσης, ως εξής:

    from django.db.models import FloatField, Transform

    class AbsoluteValue(Transform):
        lookup_name = 'abs'
        function = 'ABS'

        @property
        def output_field(self):
            return FloatField()

Με αυτό τον τρόπο είμαστε σίγουροι ότι το δικό μας transformation θα επιστρέψει μια τιμή τύπου float και τα επόμενα lookups (αν υπάρχουν, πχ το lte στο `abs__lte`) θα “δουν” την τιμή αυτή ως float και όχι ως int όπως ήταν στην αρχή. Ένα άλλο πολύ κοινό παράδειγμα της `Transform` είναι η μετατροπή ενός `DateField` στην τιμή του έτους μόνο. Για παράδειγμα, θα θέλατε να δείτε ποια πειράματα έγιναν το έτος 2016. Θα θέλατε να το γράψετε ως εξής: `Experiment.objects.filter(date__year=2016)`. Το `date` είναι το πεδίο του μοντέλου μας, τύπου `DateField` και το `year` το lookup (που υπάρχει ήδη μέσα στο Django). Το `year` lookup “ξηλώνει” μόνο το έτος από την ημερομηνία και το συγκρίνει με το 2016. Έτσι, μετατρέπεται η ημερομηνία (`DateField`) σε έτος (`IntegerField`) και συγκρίνεται με το δεξί μέρος.

## Γράφοντας ένα πιο αποτελεσματικό `abs__lt` lookup

Όταν χρησιμοποιούμε το παραπάνω `abs` lookup, η παραγόμενη SQL, σε μερικές περιπτώσεις, δεν χρησιμοποιεί αποτελεσματικά τα ευρετήρια (indexes) της βάσης δεδομένων. Πιο συγκεκριμένα, όταν χρησιμοποιούμε το `change__abs__lt=27`, αυτό είναι ισοδύναμο με το `change__gt=-27` AND `change__lt=27`. (Στη περίπτωση του `lte` θα μπορούσαμε να χρησιμοποιήσουμε την εντολή SQL `BETWEEN`).

Θα θέλαμε, επομένως, η παραγόμενη SQL από το `Experiment.objects.filter(change__abs__lt=27)` να δείχνει κάπως έτσι:

    SELECT .. WHERE "experiments"."change" < 27 AND "experiments"."change" > -27

Η υλοποίηση αυτού φαίνεται παρακάτω:

    from django.db.models import Lookup

    class AbsoluteValueLessThan(Lookup):
        lookup_name = 'lt'

        def as_sql(self, compiler, connection):
            lhs, lhs_params = compiler.compile(self.lhs.lhs)
            rhs, rhs_params = self.process_rhs(compiler, connection)
            params = lhs_params + rhs_params + lhs_params + rhs_params
            return '%s < %s AND %s > -%s' % (lhs, rhs, lhs, rhs), params

    AbsoluteValue.register_lookup(AbsoluteValueLessThan)

Υπάρχουν μερικά αξιοσημείωτα πράγματα εδώ. Πρώτα απ’ όλα το `AbsoluteValueLessThan` δεν καλεί τη μέθοδο `process_lhs()`. Αντιθέτως, υπερπηδά τη μετατροπή του `lhs` που γίνεται από την `AbsoluteValue` και χρησιμοποιεί την αρχική τιμή του lhs` (το `self.lhs` είναι η τιμή μετά την μετατροπή ενώ το `self.lhs.lhs` είναι η αρχική τιμή του `change`). Αυτό γίνεται γιατί θέλουμε να έχουμε το `"experiments"."change"` και όχι το `ABS("experiments"."change")`. Είναι ασφαλής η αναφορά μας απ’ ευθείας στο `self.lhs.lhs` γιατί η `Lookup` κλάση `AbsoluteValueLessThan` μπορεί να προσπελασθεί μόνο από την `Transform` κλάση `AbsoluteValue` (λόγω του register στην τελευταία γραμμή), δηλαδή το `lhs` θα είναι πάντα ένα instance της Transform κλάσης `AbsoluteValue`.

Επισημαίνεται επίσης το γεγονός ότι όχι μόνο και τα δύο μέρη (αριστερό και δεξί) χρησιμοποιούνται πολλές φορές μέσα στο SQL query αλλά και οι παράμετροι επίσης (`lhs_params` και `rhs_params`).

Ο λόγος που γίνεται αυτό είναι γιατί, σε περίπτωση που το `self.rhs` είναι κάτι διαφορετικό από έναν ακέραιο (πχ μια αναφορά σε ένα `F()`) δε θα μπορούμε να κάνουμε τις μετατροπές στην Python.

Σημείωση

Στην πραγματικότητα, τα περισσότερα lookups που εμπεριέχουν το `__abs` θα μπορούσαν να υλοποιηθούν με την παραπάνω μέθοδο και στις περισσότερες βάσεις δεδομένων θα μπορέσετε να εκμεταλλευτείτε τη δυνατότητα των ευρετηρίων (indexes) τους. Ωστόσο, στην PostgreSQL ίσως θα θέλατε να προσθέσετε ένα ευρετήριο για το `abs(change)` το οποίο θα επιτρέψει σε αυτά τα queries να είναι πολύ αποτελεσματικά.

## Ένα παράδειγμα ενός διμερούς transformer

Το παράδειγμα της `Transformer` κλάσης `AbsoluteValue` που περιγράψαμε παραπάνω είναι μια μετατροπή η οποία εφαρμόζεται στο αριστερό μέρος του lookup (στο πεδίο `change`). Ίσως να υπάρξουν, μερικές περιπτώσεις όπου θα θέλατε η μετατροπή να εφαρμοστεί και στο αριστερό και στο δεξί μέρος. Για παράδειγμα, αν θέλετε να φιλτράρετε ένα queryset βασιζόμενο στην ισότητα των δύο μερών (αριστερό και δεξί) με την χρήση κάποιας SQL συνάρτησης.

Ας εξετάσουμε την απλή περίπτωση μιας μετατροπής που δεν κάνει διάκριση μεταξύ πεζών και κεφαλαίων γραμμάτων. Αυτή η μετατροπή δεν είναι πολύ χρήσιμη στην πράξη καθώς το Django έχει προεγκατεστημένα αρκετά lookups που δεν κάνουν διακρίσεις μεταξύ πεζών-κεφαλαίων. Παρόλ’ αυτά αποτελεί ένα χρήσιμο παράδειγμα για την επίδειξη των διμερών μετατροπών (bilateral transformations) ανεξαρτήτως της βάση δεδομένων που χρησιμοποιείται.

Ορίζουμε έναν transformer `UpperCase` ο οποίος χρησιμοποιεί την SQL συνάρτηση `UPPER()` για να μετατρέψει τις τιμές πριν την σύγκριση. Θέτουμε το attribute `bilateral = True` για να υποδείξουμε ότι η μετατροπή θα πρέπει να γίνει όχι μόνο στο αριστερό μέρος, `lhs`, αλλά και στο δεξί, `rhs`:

    from django.db.models import Transform

    class UpperCase(Transform):
        lookup_name = 'upper'
        function = 'UPPER'
        bilateral = True

Έπειτα το κάνουμε register με τους ανάλογους τύπους πεδίων:

    from django.db.models import CharField, TextField
    CharField.register_lookup(UpperCase)
    TextField.register_lookup(UpperCase)

Τώρα το queryset `Author.objects.filter(name__upper="doe")` θα παράξει ένα query το οποίο δεν θα κάνει διάκριση μεταξύ πεζών-κεφαλαίων γραμμάτων:

    SELECT ... WHERE UPPER("author"."name") = UPPER('doe')

## Γράφοντας εναλλακτικές υλοποιήσεις για ήδη υπάρχοντα lookups

Κάποιες φορές, μερικές βάσεις δεδομένων απαιτούν έναν διαφορετικό τρόπο γραφής της SQL για την ίδια λειτουργία. Για παράδειγμα, κάποιες βάσεις για να προσδιορίσουν τη λογική “όχι ίσο με”, αντί του συμβόλου `<>` απαιτούν το `!=`. (Στην πραγματικότητα σχεδόν όλες οι βάσεις δεδομένων υποστηρίζουν και τα δύο αυτά σύμβολα, συμπεριλαμβανομένων και όλων των επίσημων βάσεων δεδομένων που υποστηρίζει το Django). Σε αυτό το παράδειγμα θα ξαναγράψουμε μια δικιά μας υλοποίηση, για την MySQL, της λειτουργίας NotEqual.

Μπορούμε να αλλάξουμε τη συμπεριφορά για ένα συγκεκριμένο database backend δημιουργώντας μια subclass της `NotEqual` η οποία δεν υλοποιεί τη μέθοδο `as_sql `` αλλά την ``as_mysql`:

    class MySQLNotEqual(NotEqual):
        def as_mysql(self, compiler, connection):
            lhs, lhs_params = self.process_lhs(compiler, connection)
            rhs, rhs_params = self.process_rhs(compiler, connection)
            params = lhs_params + rhs_params
            return '%s != %s' % (lhs, rhs), params

    Field.register_lookup(MySQLNotEqual)

Μπορούμε, έπειτα, να κάνουμε register την κλάση αυτή με το `Field`. Η κλάση `MySQLNotEqual` αντικαθιστά την κλάση `NotEqual`, μόνο για τις MySQL βάσεις δεδομένων, αφού μοιράζονται το ίδιο `lookup_name`.

Κατά τη διάρκεια μετάφρασης του query, το Django ψάχνει για τις μεθόδους``as_%s % connection.vendor`` και αν δεν βρει κάποια τότε χρησιμοποιεί την `as_sql`. Τα ονόματα των vendor για τις προεγκατεστημένες στο Django database backends είναι τα `sqlite`, `postgresql`, `oracle` και `mysql`.

## Πως προσδιορίζει το Django τα lookups και τα transforms που χρησιμοποιούνται

Σε μερικές περιπτώσεις θα θέλατε να αλλάξετε, δυναμικά, το `Transform` ή το `Lookup` που επιστρέφεται ανάλογα το όνομα που δίνεται στο query παρά να μπείτε στη διαδικασία να φτιάξετε ένα για το καθένα. Θα μπορούσατε, για παράδειγμα, να έχετε ένα πεδίο το οποίο αποθηκεύει συντεταγμένες ή κάποια αυθαίρετη διάσταση και θέλετε να μπορείτε να γράφετε το `.filter(coords__x7=4)` και να σας επιστρέφει τα objects όπου η 7η συντεταγμένη έχει την τιμή 4. Για να το κάνετε αυτό, θα πρέπει να παρακάμψετε (override) την `get_lookup` με κάτι σαν αυτό:

    class CoordinatesField(Field):
        def get_lookup(self, lookup_name):
            if lookup_name.startswith('x'):
                try:
                    dimension = int(lookup_name[1:])
                except ValueError:
                    pass
                else:
                    return get_coordinate_lookup(dimension)
            return super(CoordinatesField, self).get_lookup(lookup_name)

Μετά θα ορίσετε τη μέθοδο `get_coordinate_lookup`, καταλλήλως, για να επιστρέφει μια subclass της `Lookup` η οποία θα χειριστεί την σχετική τιμή της μεταβλητής `dimension`.

Ομοίως, υπάρχει μια μέθοδος για την `Transform` με το όνομα `get_transform()`. Η μέθοδος `get_lookup()` θα πρέπει πάντα να επιστρέφει μια subclass της `Lookup` ενώ η `get_transform()` μια subclass της `Transform`. Είναι σημαντικό να θυμάστε ότι τα objects της κλάσης `Transform` μπορούν να φιλτραριστούν περαιτέρω ενώ τα objects της κλάσης `Lookup` όχι.

Όταν φιλτράρετε και έχει απομείνει μόνο ένα lookup όνομα, το Django ψάξει για κάποιο `Lookup`. Αν υπάρχουν πολλά τότε θα ψάξει για κάποιο `Transform`. Στην περίπτωση που υπάρχει μόνο ένα όνομα και δεν βρεθεί κάποιο `Lookup`, τότε το Django θα ψάξει για ένα `Transform` και μετά το `exact` lookup πάνω σε αυτό το `Transform`. Στο τέλος, το Django, πάντα θα ψάχνει για ένα `Lookup`. Για να ξεκαθαρίσουμε λίγο το τοπίο:

- Το `.filter(myfield__mylookup)` θα καλέσει την μέθοδο `myfield.get_lookup('mylookup')`.

- Το `.filter(myfield__mytransform__mylookup)` θα καλέσει τη μέθοδο `myfield.get_transform('mytransform')` και μετά την `mytransform.get_lookup('mylookup')`.

- Το `.filter(myfield__mytransform)` θα καλέσει πρώτα τη μέθοδο `myfield.get_lookup('mytransform')`, η οποία θα αποτύχει. Έπειτα θα καλέσει την μέθοδο `myfield.get_transform('mytransform')` και τέλος την `mytransform.get_lookup('exact')`.