---
title: "Pesquisas personalizadas"
version: 1.9
locale: pt-br
source: https://docs.djangoproject.com/pt-br/1.9/howto/custom-lookups/
canonical: https://djangodocs.dev/pt-br/1.9/howto/custom-lookups/
---
# Pesquisas personalizadas

Django oferece uma grande variedade de: ref: pesquisas embutidos \<field-lookups\> \`Para filtragem (por exemplo, e exact\` icontains\`\`). Esta documentação explica como escrever lookups personalizadas e como alterar o funcionamento de lookups existentes. Para as referências da API de lookups, consulte o: doc: / ref / models / lookups.

## Um simples exemple de pesquisa

Vamos iniciar com uma simples pesquisa personalizada. Escreveremos uma simples pesquisa `ne` que funciona em frente ao `exato`. Author.objects.filter(name\_\_ne=’Jack’) traduzindo para SQL.

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

Este SQL é independente, por isso, não precisa se preocupar com os bancos de dados diferentes.

Há duas etapas para fazer este trabalho. Em primeiro lugar precisamos implementar a “lookup”, então precisamos dizer ao Django sobre ele. A implementação é bastante simples

```
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
```

Para registrar a lookup `NotEqual` precisamos somente chamar `register_lookup` no campo da classe que queremos que a lookup esteja disponível. Neste caso, a lookup faz sentido em todos os  `Field` da sub-classe, então registramos isso com \`\` Field\`\` diretamente

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

O registro da “lookup” também pode ser feito usando um pattern decorador

```
from django.db.models.fields import Field

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

> **Changed in Django 1.8**
>
> foi adicionado a capacidade de usar o pattern decorator.

Agora podemos usar \`\` foo\_\_ne\`\` para qualquer campo \`\` foo\`\`. Tenha certeza de registrar antes de tentar criar qualquer “querysets” que o use. É possível colocar a implementação no \`\` models.py\`\`, ou registar a lookup no método `ready()` do \`\` AppConfig\`\`.

Olhando mais perto da implementação, o primeiro atributo obrigatório é \`\` lookup\_name\`\`. Isso permite que o ORM entenda como interpretar \`\` name\_\_ne\`\` e use \`\` NotEqual\`\` para gerar o SQL. Por convenção, estes nomes são sempre em strings caixa baixa contendo apenas letras, mas a única exigência é que não contenha a string \`\` \_\_\`\`.

We then need to define the `as_sql` method. This takes a `SQLCompiler`
object, called `compiler`,  and the active database connection.
`SQLCompiler` objects are not documented, but the only thing we need to know
about them is that they have a `compile()` method which returns a tuple
containing an SQL string, and the parameters to be interpolated into that
string. In most cases, you don’t need to use it directly and can pass it on to
`process_lhs()` and `process_rhs()`.

A `Lookup` trabalha em dois valores, e `lhs` e  `rhs`, entendendo como left-hand side e right-hand side. O lado esquerdo é geralmente uma referência de campo, mas pode ser qualquer coisa que implemente :ref: query expression API \<query-expression\>. O lado direito é o valor dado pelo usuário. No exemplo ```Author.objects.filter (name__ne = 'Jack') ``, o lado esquerdo é uma referência para o campo ``name``` campo do modelo `Author`, e \`\` ‘Jack ‘ \`\` é o lado direito.

Nós chamamos e `process_lhs` e  `process_rhs` para convertê-los para os valores que precisamos para SQL usando o objeto `compiler` descrito antes. Esses métodos retornam tuplas contendo alguns SQL e os parâmetros a serem interpolados dentro deste SQL, assim como nós precisamos para retornar do nosso método `as_sql`. No exemplo acima, `process_lhs` retorna ```( "autor". "Nome" ', []) `` e ``process_rhs``` retorna `( "% s"', [ 'Jack' ])`. Neste exemplo não havia parâmetros para o lado esquerdo, mas isso depende do objeto que temos, portanto ainda precisamos incluí-los nos parâmetros que retornamos.

Finally we combine the parts into an SQL expression with `<>`, and supply all
the parameters for the query. We then return a tuple containing the generated
SQL string and the parameters.

## Um exemplo simples transformador

A lookup personalizado acima é boa, mas em alguns casos você queira ser capaz de lookups encadeadas. Por exemplo, vamos supor que estamos construindo uma aplicação onde queremos fazer uso do operador `abs()`. Temos um modelo `Experiment` que registra um valor inicial, o valor final e a mudança (início - fim). Gostaríamos de encontrar todos os experimentos em que a alteração foi igual a uma certa quantidade ( `Experiment.objects.filter (change__abs = 27)`), ou onde não exceda um certo montante ( `Experiment.objects.filter (change__abs__lt = 27)`).

> **Note**
>
> Este exemplo é um pouco artificial, mas demonstra muito bem a gama de funcionalidades que é possível de uma maneira independente em um backend de banco de dados., e sem duplicar funcionalidades já existentes no Django.

Vamos começar por escrever um tradutor `AbsoluteValue`. Isto irá usar a função SQL `ABS ()` para transformar o valor antes da comparação:

```
from django.db.models import Transform

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

Em seguida, vamos registrá-lo para \`\` IntegerField\`\`

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

agora podemos executar as queries que tínhamos antes. Experiment.objects.filter (change\_\_abs = 27) \`\` irá gerar o seguinte SQL

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

Ao usar \`\` Transform\`\` ao invés de `Lookup` isso significa que somos capazes de encadear lookups umas as outras. Então `Experiment.objects.filter (change__abs__lt = 27)` irá gerar o seguinte SQL

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

Note-se que no caso não há nenhuma outra lookup especificada, o Django interpreta `change__abs = 27` como `change__abs__exact = 27`.

Quando olhamos par quais lookups são permitidas depois de ter aplicado o  `Trnasform`, Django usa o atibuto òutput\_field\`. Não é necessário especificar isso aqui, já que não muda, más supondo que fosse aplicado ```AbsoluteValue``para algum campo que represente um tipo mais complexo (por exemplo um ponto reativo a uma origem ou um número complexo) então talvez quereríamos especificar  que retornasse um tipo ``FloatField``` para próximas lookups. Isso pode ser feito adicionando um atributo òutput\_field\`\`ao transform:

```
from django.db.models import FloatField, Transform

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

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

Isso garante que novas pesquisas como `abs__lte` se comportem como fariam para um `FloatField`.

## Escrevendo uma lookup `abs__lt` eficiente

Quando usado a lookup `abs` escrita acima, o SQL gerado não irá usar índices  de maneira eficiente em alguns casos. Em particular, quando usado `change__abs__lt=27`, é equivalente a `change__gt=-27` e `change__lt=27`. (Para o caso `lte` podemos usar o SQL `BETWEEN`).

Então nós gostaríamos `Experiment.objects.filter (change__abs__lt=27)` para gerar o seguinte SQL:

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

A implementação é:

```
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)
```

Há algumas coisas notáveis ​​acontecendo. Em primeiro lugar, `AbsoluteValueLessThan` não está chamando `process_lhs ()`. Em vez disso ele ignora a transformação do `lhs` feito por `AbsoluteValue` e usa o `lhs` original. Ou seja, queremos pegar \`\` “experiments”.”change” \`\` não ```ABS( "experiments". "change") ``. Referindo-se diretamente a ``self.lhs.lhs``` é tão seguro quanto `AbsoluteValueLessThan`  pode ser acessado diretamente da lookup `AbsoluteValue`, que é a `lhs` é sempre uma instância de `AbsoluteValue`.

Note também que como ambos os lados são usados ​​várias vezes na consulta os parâmetros precisam conter `lhs_params` e `rhs_params` várias vezes.

A consulta final faz a inversão ( `27` para \`\` -27\`\`) diretamente no banco de dados. A razão para isso é que, se o \`\` self.rhs\`\` é algo mais do que um valor inteiro simples (por exemplo, uma referência `F()` ) que não podemos fazer as transformações em Python.

> **Note**
>
> Na verdade, a maioria das lookups com `__abs` poderias ser implementadas como consultas de intervalos como esta, e na maioria dos backends de banco de dados, é provável que seja mais sensato fazê-lo já que podería fazer uso dos índices. No entanto, com PostgreSQL você pode querer adicionar um índice em na `abs(change)` o que permitiria que essas consultas serem muito eficientes.

## Um exemplo bilateral transformador

O exemplo `AbsoluteValue` discutido anteriormente é uma transformação que se aplica para o lado esquerdo da lookup. Pode haver alguns casos onde você quer que a transformação seja aplicada tanto para o lado esquerdo quanto para o lado direito. Por exemplo, se você quiser filtrar um queryset com base em uma igualdade de lado esquerdo e direito inperseptível para alguma função SQL.

Vamos examinar o exemplo simples de transformação case-insensitive aqui. Essa transformação não é muito útil na prática, como Django já vem com um monte de pesquisas de maiúsculas e minúsculas embutidos, mas será uma boa demonstração de transformações bilaterais de uma forma agnóstica ao banco de dados.

Definimos um transformador `UpperCase` que usa a função SQL `UPPER()` para transformar os valores antes de comparação. Definimos :attr: bilateral = True \<django.db.models.Transform.bilateral\> para indicar que essa transformação deverá aplicar-se a `lhs` e `rhs`:

```
from django.db.models import Transform

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

Em seguida, registre

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

Agora, o queryset `Author.objects.filter(name__upper = "doe")` irá gerar uma query case-insensitive como esta:

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

## Escrever implementações alternativas para pesquisas existentes

Às vezes, diferentes fornecedores de banco de dados requerem diferentes SQL para a mesma operação. Para este exemplo, vamos reescrever uma implementação personalizada para o MySQL para o operador NotEqual. Em vez de `<>` usaremos o operador `!=`. (Note que, na realidade, quase todos os bancos de dados suportam ambos, incluindo todas as bases de dados oficiais apoiados por Django).

Nós podemos mudar o comportamento em um backend específico, criando uma subclasse de `NotEqual` com um método `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)
```

Podemos então registrá-lo com `Field`. Ele toma o lugar da classe `NotEqual` original já que tem o mesmo `lookup_name`.

Ao compilar uma consulta, Django primeiro procura pelos métodos `as_%s % connection.vendor`, e depois para `as_sql`. Os nomes de fornecedores para os back-ends suportador são  `sqlite`, `postgresql`, `oracle`  e `mysql`.

## Como o Django determina as lookups e transformações que são usados

Em alguns casos, talvez queira mudar dinamicamente qual `Transform` ou `Lookup` é devolvido com base no nome passado, em vez de corrigi-lo. Como exemplo, é possível ter um campo que armazena coordenadas ou uma dimensão arbitrária, e deseja permitir que uma sintaxe como `.filter (coords__x7=4)` para retornar os objetos onde a 7ª coordenada tem valor 4. Para fazer isso, substitua `get_lookup` com algo como:

```
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)
```

Defina então o \`\` get\_coordinate\_lookup\`\` adequadamente para retornar uma subclasse `Lookup` que lida com o valor relevante da `dimension`.

Existe um método de nome semelhante chamado ```get_transform() ``. `` Get_lookup () `` deve sempre retornar uma subclasse de ``Lookup```, e `get_transform()` uma subclasse de  `Transform`. É importante lembrar que objetos do tipo `Transform` podem ainda ser filtrados, e objetos do tipo `Lookup` não podem.

Ao filtrar, se houver apenas um nome lookup remanescente a ser resolvido, olhamos para um `Lookup`. Se houver vários nomes, ele irá procurar por um `Transform`. Em uma situação onde há apenas um nome e um `Lookup` não é encontrado, procuramos por um `Transform` e então o  lookup `exact` naquele `Transform`. Todas as sequências de chamadas sempre terminam com um `Lookup`. Para esclarecer:

- `.filter(myfield__mylookup)` irá chamar myfield.get\_lookup(‘mylookup’)\`.
- `.filter(myfield__mytransform__mylookup)` irá chamar `myfield.get_transform('mytransform')`, e  em seguida `mytransform.get_lookup('mylookup')`.
- `.filter(myfield__mytransform)` irá chamar primeiro `myfield.get_lookup( 'mytransform')`, o qual irá falhar, por isso vai retornar e chamar `myfield.get_transform('mytransform')` e em seguida, `mytransform.get_lookup( "exact")`.
