Ir para o conteúdo

Django: Como realizar consultas SQL com o ORM (Object Relational Mapping)

Esse post não é um tutorial ou algo do gênero, estou apenas centralizando e comparando alguns exemplos de consultas SQL no Django ORM (Object Relational Mapping).

Note

Desde já é importante notar que os código apresentados aqui são apenas para exemplo, dessa forma altere os mesmos conforme as suas necessidades!

Modelo

Os exemplos contidos nessa página foram criados com base no seguinte modelo:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
from django.core.validators import MaxValueValidator

from django.db import models


class Autor(models.Model):
    nome = models.CharField(max_length=30)
    sobrenome = models.CharField(max_length=30)
    idade = models.PositiveSmallIntegerField(validators=[MaxValueValidator(110)])


class Livro(models.Model):
    titulo = models.CharField(max_length=100)
    autor = models.ForeignKey(Autor, on_delete=models.CASCADE)

Criar as migrações:

1
python manage.py makemigrations

Executar as migrações:

1
python manage.py migrate

Para visualizar o código SQL que foi gerado ao executar o comando migrate é utilizado o comando sqlmigrate, a sua sintaxe é:

1
python manage.py sqlmigrate NomeDoApp NomeDaMigration

No meu caso:

1
python manage.py sqlmigrate orm 0001

Como resultado temos:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
BEGIN;
--
-- Create model Autor
--
CREATE TABLE "orm_autor" (
    "id" integer NOT NULL PRIMARY KEY AUTOINCREMENT,
    "nome" varchar(30) NOT NULL,
    "sobrenome" varchar(30) NOT NULL,
    "idade" smallint unsigned NOT NULL CHECK ("idade" >= 0),
);
--
-- Create model Livro
--
CREATE TABLE "orm_livro" (
    "id" integer NOT NULL PRIMARY KEY AUTOINCREMENT,
    "titulo" varchar(100) NOT NULL,
    "autor_id" integer NOT NULL REFERENCES "orm_autor" ("id") DEFERRABLE INITIALLY DEFERRED,
);
CREATE INDEX "orm_livro_autor_id_2d6a4a38" ON "orm_livro" ("autor_id");
COMMIT;

Note

Observe que o ORM do Django gera o nome da tabela a partir do nome do app + nome da classe.

Esse comportamento pode ser alterado utilizando-se uma classe meta, contudo dei preferencia por manter o padrão.

Para ativar o shell interativo do Django (caso seja necessário):

1
python manage.py shell

Código

Na maior parte das situações tentei escrever o código SQL e o que seria equivalente no ORM.

Por padrão o Django utiliza SQLite, com isso testei a maior parte dos comando no DB Browser for SQLite.

Perceba que podem haver erros 😅 e que essas querys podem mudar ou serem implementadas de outras formas dependêndo do banco de dados que se está utilizando.

Caso encontre algum erro ou queira adicionar alguma query entre em contato 😉.

Inserindo dados

sqlite-sql:

1
INSERT INTO orm_autor (nome, sobrenome, idade) VALUES ('renato', 'cruz', '36');

Django ORM:

1
Autor.objects.create(nome='renato', sobrenome='cruz', idade=36)

Consultando todos os dados

sqlite-sql:

1
SELECT * FROM orm_autor;

Django ORM:

1
Autor.objects.all()

O comando acima retorna uma lista então podemos:

1
2
3
4
5
6
autores = Autor.objects.all()

for autor in autores:
    print(autor.nome)
    print(autor.sobrenome)
    print(autor.idade)

Consultar colunas especificas.

sqlite-sql:

1
SELECT nome, sobrenome FROM orm_autor;

Django ORM:

1
Autor.objects.only('nome', 'sobrenome')

Consulta ignorando linhas duplicadas.

sqlite-sql:

1
SELECT DISTINCT nome, sobrenome FROM orm_autor;

Django ORM:

1
Autor.objects.values('nome', 'sobrenome').distinct()

Consulta com limite de resultados.

sqlite-sql:

1
SELECT * FROM orm_autor LIMIT 10;

Django ORM:

1
Autor.objects.all()[:10]

Consulta paginada.

sqlite-sql:

1
SELECT * FROM orm_autor LIMIT 5 OFFSET 5;

Django ORM:

1
Autor.objects.all()[5:10]

Consulta com filtro.

sqlite-sql:

1
SELECT * FROM orm_autor WHERE id = 1;

Django ORM:

1
Autor.objects.filter(pk=1)

Note

Particularmente gosto de utilizar pk ao invés de id, isso porque o Python tem um método interno chamado id().


Consulta utilizando comparação.

sqlite-sql:

1
2
3
4
5
SELECT * FROM orm_autor WHERE idade > 20;
SELECT * FROM orm_autor WHERE idade >= 20;
SELECT * FROM orm_autor WHERE idade < 20;
SELECT * FROM orm_autor WHERE idade <= 20;
SELECT * FROM orm_autor WHERE idade != 20;

Django ORM:

1
2
3
4
5
Autor.objects.filter(idade__gt=20)
Autor.objects.filter(idade__gte=20)
Autor.objects.filter(idade__lt=20)
Autor.objects.filter(idade__lte=20)
Autor.objects.exclude(idade=20)

Onde:

  • gt: Maior que.
  • gte: Maior ou igual que.
  • lt: Menor que.
  • lte: Menor ou igual que.
  • exclude(): Qualquer resultado diferente do especificado.

Consultar um intervalo (BETWEEN).

sqlite-sql:

1
SELECT * FROM orm_autor WHERE idade BETWEEN 36 AND 40;

Django ORM:

1
Autor.objects.filter(idade__range=(36, 40))

Consulta buscando um padrão.

sqlite-sql:

1
2
3
4
5
6
SELECT * FROM orm_autor WHERE nome LIKE 'Renato%';
SELECT * FROM orm_autor WHERE nome ILIKE 'Renato%';
SELECT * FROM orm_autor WHERE nome LIKE '%Renato';
SELECT * FROM orm_autor WHERE nome ILIKE '%Renato';
SELECT * FROM orm_autor WHERE nome LIKE '%Renato%';
SELECT * FROM orm_autor WHERE nome ILIKE '%Renato%';

Django ORM:

1
2
3
4
5
6
Autor.objects.filter(nome__startswith='Renato')
Autor.objects.filter(nome__istartswith='Renato')
Autor.objects.filter(nome__endswith='Renato')
Autor.objects.filter(nome__iendswith='Renato')
Autor.objects.filter(nome__contains='Renato')
Autor.objects.filter(nome__icontains='Renato')

Onde:

  • startswith: Começa com (Case-sensitive - Maiúscula e Minúsculas fazem diferença).
  • istartswith: Começa com (Case-insensitive - Maiúscula e Minúsculas não fazem diferença).
  • endswith: Terminal com (Case-sensitive).
  • iendswith: Terminal com (Case-insensitive).
  • contains: Contém (Case-sensitive).
  • icontains: Contém (Case-insensitive).

Note

SQLite não suporta case-sensitive.


Consultar se um resultado está em uma lista (IN).

sqlite-sql:

1
SELECT id FROM orm_autor WHERE id in (1, 2);

Django ORM:

1
Autor.objects.filter(id__in=[1, 2])

Consulta com AND.

sqlite-sql:

1
SELECT * FROM orm_autor WHERE nome='renato' AND idade > **OBS**: 28;

Django ORM:

1
Autor.objects.filter(nome='renato', idade__gt=28)

Consulta com OR.

sqlite-sql:

1
SELECT * FROM orm_autor WHERE nome='renato' OR idade > **OBS**: 28;

Django ORM:

1
2
3
from django.db.models import Q

Autor.objects.filter(Q(nome='renato') | Q(idade__gt=28))

Consulta com NOT.

sqlite-sql:

1
SELECT * FROM orm_autor WHERE NOT nome='renato';

Django ORM:

1
Autor.objects.exclude(nome='renato')

Consulta com Null.

sqlite-sql:

1
SELECT * FROM orm_autor WHERE idade is NULL;

Django ORM:

1
Autor.objects.filter(idade__isnull=True)

Outra forma:

1
Autor.objects.filter(idade=None)

Consulta com Not Null.

sqlite-sql:

1
SELECT * FROM orm_autor WHERE idade is NOT NULL;

Django ORM:

1
Autor.objects.filter(idade__isnull=False)

Outra forma:

1
Autor.objects.exclude(idade=None)

Consulta ordenada (Crescente).

sqlite-sql:

1
SELECT * FROM orm_autor order by idade ASC;

Django ORM:

1
Autor.objects.order_by('idade')

Outra forma:

1
Autor.objects.order_by('idade').asc()

Consulta ordenada (Decrescente).

sqlite-sql:

1
SELECT * FROM orm_autor order by idade DESC;

Django ORM:

1
Autor.objects.order_by('-idade')

Outra forma:

1
Autor.objects.order_by('idade').desc()

Atualizando uma linha.

sqlite-sql:

1
UPDATE orm_autor SET idade = 20 WHERE id = 1;

Django ORM:

1
2
3
autor = Autor.objects.get(pk=1)
autor.idade = 20
autor.save()

Outra forma:

1
Autor.objects.filter(pk=1).update(idade=20)

Atualizando múltiplas linhas.

sqlite-sql:

1
UPDATE orm_autor SET idade = idade * 1.5;

Django ORM:

1
2
3
from django.db.models import F

Autor.objects.update(idade=F('idade') * 1.5)

Exemplo com filter():

1
2
3
for autor in Autor.objects.filter(idade=36):
    autor.idade = 30
    autor.save()

Apagando todos os dados.

sqlite-sql:

1
DELETE FROM orm_autor;

Django ORM:

1
Autor.objects.all().delete()

Apagando linhas especificas

sqlite-sql:

1
DELETE FROM orm_autor WHERE idade < 20;

Django ORM:

1
Autor.objects.filter(idade__lt=20).delete()

Apagando um registro

sqlite-sql:

1
DELETE FROM orm_autor WHERE id = 1;

Django ORM:

1
Autor.objects.get(pk=1).delete()

Funções de agregação.

Funções de agregação são funções SQL que permitem executar uma operação aritmética nos valores de uma coluna.

MIN()

sqlite-sql:

1
SELECT MIN(idade) FROM orm_autor;

Django ORM:

1
2
3
from django.db.models import Min

Autor.objects.all().aggregate(Min('idade'))

MAX()

sqlite-sql:

1
SELECT MAX(idade) FROM orm_autor;

Django ORM:

1
2
3
from django.db.models import Max

Autor.objects.all().aggregate(Max('idade'))

AVG()

sqlite-sql:

1
SELECT AVG(idade) FROM orm_autor;

Django ORM:

1
2
3
from django.db.models import Avg

Autor.objects.all().aggregate(Avg('idade'))

SUM()

sqlite-sql:

1
SELECT SUM(idade) FROM orm_autor;

Django ORM:

1
2
3
from django.db.models import Sum

Autor.objects.all().aggregate(Sum('idade'))

COUNT()

sqlite-sql:

1
SELECT COUNT(*) FROM orm_autor;

Django ORM:

1
Autor.objects.count()

Outro exemplo:

sqlite-sql:

1
SELECT COUNT(*) FROM orm_autor WHERE idade = 60;

Django ORM:

1
Autor.objects.filter(idade=60).count()

Consultas com GROUP BY.

Exemplo retorna quantas vezes um mesmo nome se repete.

sqlite-sql:

1
SELECT nome, COUNT(*) as count FROM orm_autor GROUP BY nome;

Django ORM:

1
2
3
from django.db.models import Count

Autor.objects.values('nome').annotate(count=Count('nome'))

Consultas com HAVING.

O HAVING determina uma condição de busca para um grupo ou um conjunto de registros.

Serão exibidos os nomes que possuam mais de 1 ocorrência.

sqlite-sql:

1
SELECT nome, COUNT('nome') as count FROM orm_autor GROUP BY nome HAVING count > **OBS**: 1;

Django ORM:

1
2
3
from django.db.models import Count

Autor.objects.values('nome').annotate(count=Count('nome')).filter(count__gt=1)

Tabelas com chave estrangeira (Many-to-one)

Criando livro através do objeto autor:

Django ORM:

1
2
3
4
novo_autor = Autor(nome='rafaela', sobrenome='da silva', idade=20)
novo_autor.save()

novo_livro = novo_autor.livro_set.create(titulo='livro da rafaela.')

Nota

Poderia ter consultado um autor existente ao invés de se criar um novo.


Criando livro através do objeto livro

Django ORM:

1
2
3
4
5
novo_autor = Autor(nome='gisele', sobrenome='fonseca', idade=28)
novo_autor.save()

novo_livro = Livro(titulo='livro da gisele.', autor=novo_autor)
novo_livro.save()

Note

Poderia ter consultado um autor existente ao invés de se criar um novo.


Extra

Outros ORM (Object-Relational Mapping - Mapeamento Objeto-Relacional) para Python:

Algumas ferramentas uteis para gestão de banco de dados: