---
title: "Como escrever pesquisadores customizadas"
version: 4.2
locale: pt-br
source: https://docs.djangoproject.com/pt-br/4.2/howto/custom-lookups/
canonical: https://djangodocs.dev/pt-br/4.2/howto/custom-lookups/
---
# Como escrever pesquisadores customizadas

Django oferece uma grande variedade de: ref:lookups embutidas \<field-lookups\> para filtragem (por exemplo, `exact` e `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 exemplo de filtro

Vamos começar com um pequeno pesquisador customizado. Nós iremos escrever um pesquisador customizado `ne` que irá funcionar de maneira contrária ao `exact`. `Author.objects.filter(name__ne='Jack')` irá ser traduzido para o SQL:

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

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

Há dois passos para fazer isso funcionar. Primeiramente nós precisamos implementar o filtro, então precisamos informar ao Django sobre isso:

```
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 o pesquisador `NotEqual` vamos precisar chamar `register_lookup` na classe de campo que queremos que o pesquisador esteja disponível. Nesse caso, o pesquisador faz sentido para todas as subclasses de `Field`, então vamos registrá-lo diretamente com `Field`:

```
from django.db.models import Field

Field.register_lookup(NotEqual)
```

O registro do filtro também pode ser feito usando um padrão de “decorator”:

```
from django.db.models import Field

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

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 \`\` \_\_\`\`.

Quando então é necessário definir o método `as_sql`. Este recebe um objeto `SQL Compiler`, chamado `compiler`, e a conexão de banco de dados ativa. Os objetos `SQLCompiler` não são documentados, mas a única coisa que precisamos saber sobre eles é que eles tem um método `compile()` o qual retorna uma tupla contendo uma string SQL, e os parâmetros a serem interpolados naquela string. Na maioria dos casos você não precisa usá-lo diretamente e pode ser passá-lo para o `process_lhs()` e `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 [query expression API](/pt-br/4.2/ref/models/lookups/#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 `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 comandos SQL e os parâmetros a serem interpolados dentro destes comandos SQL, assim como nós precisamos para retornar do nosso método `as_sql`. No exemplo acima, `process_lhs` retorna `('"author"."name"', [])` 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.

FInalmente combinamos as partes em uma expressão SQL com `<>`, e fornecemos todos os parâmetros para a consulta. Então retornamos uma tupla contendo a string SQL gerada e os parâmetros.

## Um exemplo de transformador

O 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.

Nos vamos começar escrevendo um transformador `AbsoluteValue`. Isso vai 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 nós podemos rodar as consultas que tínhamos antes. `Experiment.objects.filter(change__abs=27)` irá gerar o seguinte SQL:

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

Ao usar `Transform` em vez de `Lookup` somos capazes de usar outros pesquisadores em combinação depois. `Experiment.objects.filter(change__abs__lt=27)` irá gerar o seguinte SQL:

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

Note que no caso de não haver nenhuma outra lookup especificada, o Django interpreta `change__abs = 27` como `change__abs__exact = 27`.

Isso também permite que o resultado seja usado nas clausálas `ORDER BY` e `DISTINCT ON`. Por exemplo `Experiment.objects.order_by('change__abs')` gera:

```sql
SELECT ... ORDER BY ABS("experiments"."change") ASC
```

E em bancos de dados que tem suporte ao distinct em campos (como o PostgreSQL), `Experiment.objects.distinct('change__abs')` gera:

```sql
SELECT ... DISTINCT ON ABS("experiments"."change")
```

Ao procurar quais lookups são permitidas depois de aplicar o `Transform`, o Django usa o atributo `output_field`. Não precisamos especificar isso aqui, já que não mudou, mas supondo que estivéssemos aplicando `AbsoluteValue` para algum campo que represente um tipo mais complexo (por exemplo um ponto relativo a uma origem ou um número complexo) então talvez querer especificar que o `transform` retorne um tipo `FloatField` para os próximos lookups. Isso pode ser feito adicionando um atributo `output_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 novos filtros como o `abs__lte` se comportem como deveriam para um `FloatField`.

## Escrevendo um filtro `abs__lt` eficiente

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

Então optaríamos por `Experiment.objects.filter(change__abs__lt=27)` para gerar o seguinte SQL:

```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 ao ``self.lhs.lhs``` é tão seguro quanto o `AbsoluteValueLessThan` pode ser acessado diretamente do filtro `AbsoluteValue`, que é `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()` ) não podemos fazer as transformações em Python.

> **Note**
>
> Na verdade, a maioria das filtros com `__abs` poderiam ser implementados 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 na `abs(change)` o que permitiria que essas consultas fossem muito eficientes.

## Um exemplo bilateral de “transforme”

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 insensivelmente para alguma função SQL.

Vamos examinar transformações insensíveis a maiúsculas-minúsculas. Essa transformação não é muito útil na prática já que o Django já contém vários pesquisadores como esse built-in, mas é uma boa demonstração de transformações bilaterais agnóstica ao banco de dados.

Definimos um “transformer” de `UpperCase` que usa a função `UPPER()` do SQL para transformar os valores antes da comparação. Definimos :attr: bilateral = True \<django.db.models.Transform.bilateral\> para indicar que essa transformação deve ser aplicada a ambos `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, a queryset `Author.objects.filter(name__upper="doe")` irá gerar uma consulta insensível a maiúsculas-minúsculas como essa:

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

## Escrever implementações alternativas para filtros 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 do operador NotEqual para o MySQL. 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 apoiadas pelo Django).

Nós podemos mudar o comportamento em um backend específico criado pela subclasse de `NotEqual` com um método `as_mysql`:

```
class MySQLNotEqual(NotEqual):
    def as_mysql(self, compiler, connection, **extra_context):
        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, o Django primeiro procura pelos métodos `as_%s % connection.vendor`, e depois para `as_sql`. Os nomes de fornecedores para os back-ends suportados são `sqlite`, `postgresql`, `oracle`  e `mysql`.

## Como o Django determina quais filtros e “transforms” são usados

Em alguns casos, talvez queira mudar dinamicamente qual `Transform` ou filtro é devolvido com base no nome passado, em vez de corrigí-lo. Como exemplo, você pode ter um campo o qual armazena coordenadas ou uma dimensão arbitrária, e deseja permitir 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().get_lookup(lookup_name)
```

Você deve então definir \`\` get\_coordinate\_lookup\`\` adequadamente para retornar uma subclasse de `Lookup` a qual 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.

Ao filtrar, se houver apenas um nome de filtro 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  filtro `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")`.
