Fazendo consultas¶
Uma vez que você tenha criado seus data models, o Django automaticamente lhe dá uma API de abstração de banco de dados que permite você criar, receber, atualizar e deletar objetos. Este documento explica como usar esta API. Visite a referência do model para detalhes completos de todas opções de model.
Por todo esse guia (e na referência), nós iremos refereir aos seguintes models, que compreendem um aplicação de weblog:
class Blog(models.Model):
name = models.CharField(max_length=100)
tagline = models.TextField()
def __unicode__(self):
return self.name
class Author(models.Model):
name = models.CharField(max_length=50)
email = models.EmailField()
def __unicode__(self):
return self.name
class Entry(models.Model):
blog = models.ForeignKey(Blog)
headline = models.CharField(max_length=255)
body_text = models.TextField()
pub_date = models.DateTimeField()
authors = models.ManyToManyField(Author)
def __unicode__(self):
return self.headline
Criando objetos¶
Para representar dados de uma tabela de banco de dados em objetos Python, o Django usa um sistema intuitivo: Uma classe model representa uma tabela de banco de dados, e uma instância dessa classe representa um dado em particular dentro da tabela.
Para criar um objeto, instâncie-o usando argumentos nomeados para a classe
model, e então chame save()
para salvá-lo no banco de dados.
You import the model class from wherever it lives on the Python path, as you may expect. (We point this out here because previous Django versions required funky model importing.)
Assumimos que os models estão em mysite/blog/models.py
, aqui tem um
exemplo:
>>> from mysite.blog.models import Blog
>>> b = Blog(name='Beatles Blog', tagline='All the latest Beatles news.')
>>> b.save()
Isso executa uma consulta SQL INSERT
por trás das cenas. O Django não
executa nada no banco de dados até que você, explicitamente save()
.
O método save()
não retorna valores.
See also
O save()
recebe algumas opções avançadas não descritas aqui.
Veja a documentação do save()
para detalhes completos.
Para criar um objeto e salvá-lo em um passo veja o método `create()`
.
Salvando mudanças de objetos¶
Para salvar mudanças de um objeto que já existe no banco de dados, use
save()
.
Dado uma instância b5
do Blog
que já está salvo no banco de dados, este
exemplo muda seu nome e o atualiza no banco de dados:
>> b5.name = 'New name'
>> b5.save()
Isso executa uma consulta SQL UPDATE
por trás das cenas. O Django não acessa
o banco de dados até que você, explicitamente, chame save()
.
Salvando campos ForeignKey
e ManyToManyField
¶
Atualizar um campo ForeignKey
funciona exatamente da mesma forma como salvar
um campo normal; simplesmente atribuindo um objeto do tipo certo ao campo em
questão:
>>> cheese_blog = Blog.objects.get(name="Cheddar Talk")
>>> entry.blog = cheese_blog
>>> entry.save()
Atualizar um ManyToManyField
funciona um pouco diferente; usa o método
add()
sobre o campo para adicionar um dado a relação:
>> joe = Author.objects.create(name="Joe")
>> entry.authors.add(joe)
O Django se queixará se você tentar atribuir ou adicionar um objeto do tipo errado.
Recebendo objetos¶
Para receber objetos de seu banco de dados, você constrói uma QuerySet
através do Manager
de sua classe model.
Um QuerySet
representa uma coleção de objetos do banco de dados. Ela poder
nenhum, um ou muitos filtros – critérios que apurem a coleção baseado nos
parâmetros dados. Em termos SQL, um QuerySet
equipara-se a uma consulta
SELECT
, e um filtro é uma clausula limitadora como WHERE
ou LIMIT
.
Você obtem um QuerySet
usando o Manager
de seu model. Cada model tem
pelo menos um Manager
, e ele é chamado objects
por padrão. Acesse-o
diretamente via classe model, desta forma:
>>> Blog.objects
<django.db.models.manager.Manager object at ...>
>>> b = Blog(name='Foo', tagline='Bar')
>>> b.objects
Traceback:
...
AttributeError: "Manager isn't accessible via Blog instances."
Note
O Managers
são acessível somente via classes, ao invés de instâncias,
para reforçar a separação entre operações a nível de tabela e operações a
nível de dado.
O Manager
é a principal fonte de QuerySets
para um model. Ele age como
uma QuerySet
“raiz” que descreve todos os objetos de uma tabela de banco de
dados. Por exemplo, Blog.objects
é o QuerySet
inicial que contém todos
os objetos do Blog
no banco de dados.
Recebendo todos os objetos¶
A forma mais simples de receber objetos da tabela é obtendo todos eles.
Para fazer isso, use o método all()
de um Manager
:
>>> all_entries = Entry.objects.all()
O método all()
retorna um QuerySet` de todos os objetos do banco de dados.
(Se Entry.objects
é um QuerySet
, porquê nós fizemos um
Entry.objects
? Isso é porque Entry.objects
, o QuerySet
raiz, é um
caso especial que não pode ser avaliado. O método all()
retorna um
QuerySet
que pode ser avaliado.)
Recebendo objetos específicos com filtros¶
O QuerySet
raiz fornecido pelo Manager
descreve todos os objetos na
tabela de banco de dados. Geralmente, contudo, você precisará selecionar somente
um conjunto de objetos.
Para criar um subconjunto, você precisar refinar o QuerySet
inicial,
adicionando confições ao filtro. As duas formas mais comuns de refinar um
QuerySet
são:
``filter(**kwargs)``
Retorna um novo ``QuerySet`` contendo objetos que combinam com os
parâmetros fornecidos.
``exclude(**kwargs)``
Retorna um novo ``QuerySet`` contendo objetos que *não* combinam com os
parâmetros fornecidos.
Os parâmetros (**kwargs
na definição de função acima) devem estar no formato
descrito em Campos de pesquisa abaixo.
Por exemplo, para obter um QuerySet
de entradas de blog do ano 2006, use o
filter()
desta forma:
Entry.objects.filter(pub_date__year=2006)
Nós não temos que adicionar um all()
– Entry.objects.all().filter(...)
.
Que continua funcionando, mas você somente precisa de all()
quando deseja
obter todos os objetos do QuerySet
raiz.
Filtros encadeados¶
O resultado de refinar uma QuerySet
é, em si, um QuerySet
, então é
possível encadear refinamentos juntos. Por exemplo:
>>> Entry.objects.filter(
... headline__startswith='What'
... ).exclude(
... pub_date__gte=datetime.now()
... ).filter(
... pub_date__gte=datetime(2005, 1, 1)
... )
Isso recebe o QuerySet
inicial de todas as entradas do banco de dados,
adiciona um filtro, então uma exclusão, e então um outro filtro. O resultado
final é um QuerySet
contendo todas as entradas com um cabeçalho que começa
com “What”, que foi publicado entre Janeiro 1, 2005 e a data atual.
QuerySets filtrados são únicos¶
Cada vez que você refina um QuerySet
, você tem um QuerySet
novo que não
é vinculado ao QuerySet
anterior. Cada refinamento cria um QuerySet
separado e distinto que pode ser armazenado, usado e reusado.
Exemplo:
>> q1 = Entry.objects.filter(headline__startswith="What")
>> q2 = q1.exclude(pub_date__gte=datetime.now())
>> q3 = q1.filter(pub_date__gte=datetime.now())
Este três QuerySet
são separadas. O primeiro é um QuerySet
base contendo
todas as entradas que contenham um cabeçalho começando com “What”. O segundo é
um subconjunto do primeir, com um critério adicional que exclui dados cujo o
pub_date
é maior que agora. O terceiro é um subconjunto do primeiro, com um
critério adicional que seleciona somente os dados cujo pub_date
é maior que
agora. O QuerySet
inicial (q1
) não é afetado pelo processo de
refinamento.
QuerySets são lazy¶
Os QuerySets
are lazy – o ato de criar um QuerySet
não envolve
qualquer atividade de banco de dados. Você pode empilhar filtros juntos ao longo
do dia, e o Django não os executa no banco de dados até que o QuerySet
seja
avaliado. Dê uma olhada nesse exemplo:
>>> q = Entry.objects.filter(headline__startswith="What")
>>> q = q.filter(pub_date__lte=datetime.now())
>>> q = q.exclude(body_text__icontains="food")
>>> print q
Embora isso pareça como três hits no banco de dados, de fato o banco de dados
foi consultado somente na última linha (print q
). Geralmente, os resultados
de um QuerySet
não são buscados do banco de dados até que você “peça” por
eles. Quando você o faz, o QuerySet
é avaliado para acessar o banco de
dados. Para mais detalhes sobre exatamente quando as avaliações tomam seu lugar,
veja Quando QuerySets são avaliados.
Outros métodos do QuerySet¶
Na maior parte do tempo você usará all()
, filter()
e exclude()
quando você precisar pesquisar por objetos no banco de dados. Entretanto, isso
não é tudo que há; veja a Referência de API do QuerySet
para uma lista completa de todos os vários métodos do QuerySet
.
Limitando QuerySets¶
Use a sintaxe de array-slicing do Python para limitar seu QuerySet
com um
certo número de resultados. Este é o equivalente as clausulas SQL LIMIT
e
OFFSET
.
Por exemplo, isso retorna os primeiros 5 objetos (LIMIT 5
):
>>> Entry.objects.all()[:5]
Isso retorna do sexto ao décimo objeto (OFFSET 5 LIMIT 5
):
>>> Entry.objects.all()[5:10]
Indexação negativa (i.e. Entry.objects.all()[-1]
) não é suportada.
Normalmente, dividir um QuerySet
resulta em um novo QuerySet
– e não
executa um nova consulta. Uma exceção é se você usa o parâmetro “step” do
Python na sintaxe do slice. Por exemplo, isso executaria na verdade a consulta
para retornar uma lista dos 10 primeiros objetos a cada segundo:
>>> Entry.objects.all()[:10:2]
Para receber um único objeto ao invés de uma lista
(e.g. SELECT foo FROM bar LIMIT 1
), use um index simples ao invés de um
slice. Por exemplo, isso retorna a primeira Entry
do banco de dados, depois
de ordenar as entradas alfabéticamente pelos cabeçalhos:
>>> Entry.objects.order_by('headline')[0]
Isso é o equivalente a:
>>> Entry.objects.order_by('headline')[0:1].get()
Note, no entanto, que o primeiro desses será lançado IndexError
enquanto o
segundo lançará DoesNotExists
se nenhum objeto combinar com o critério dado.
Veja get()
para mais detalhes.
Campos de pesquisa¶
Campos de pesquisa são como você especifica o cerne de uma clausula WHERE
.
Eles são especificado como argumento nomeados para o método filter()
do
QuerySet
.
Os argumentos básicos de pesquisa tem a forma campo__tipodepesquisa=valor
.
(Isso é um underscore duplo). Por exemplo:
>>> Entry.objects.filter(pub_date__lte='2006-01-01')
é traduzido para o seguinte SQL:
SELECT * FROM blog_entry WHERE pub_date <= '2006-01-01';
Como isso é possível
O Python tem a abilidade de definir funções que aceitam argumentos de nome-valor arbitrários cujo os nomes e valores são avaliados em tempo de execução. Para mais informações, veja Keyword Arguments no tutorial oficial do Python.
Se você passar um argumento inválido, uma função de pesquisa gerará o erro
TypeError
.
A API de banco de dados suporta sobre dois dos tipos de pesquisa; uma referência completa poe ser encontrada em referencia dos campos de pesquisa. Para dar a você um gosto do que está disponível, aqui temos algumas das pesquisas mais comuns que você provavelmente usa:
:lookup:`exact`
Uma combinação "extata". Por exemplo::
>>> Entry.objects.get(headline__exact="Man bites dog")
Podria gerar o SQL ao longo dessas linhas:
.. code-block:: sql
SELECT ... WHERE headline = 'Man bites dog';
Se você não fornecer um tipo de pesquisa -- isto é, se seu argumento não
contém um underscore duplo -- o tipo de pesquisa é assumido como
``exact``
Por exemplo, as duas clausulas seguintes são equivalentes::
>>> Blog.objects.get(id__exact=14) # Forma explicita
>>> Blog.objects.get(id=14) # __exact é implícito
Isso é uma conveniência, pois pesquisas ``exact`` são um caso comum.
:lookup:`iexact`
Uma combinação não sensível a maiúsculas. Então, a consulta::
>>> Blog.objects.get(name__iexact="beatles blog")
Poderia combinar um ``Blog`` entitulado "Beatles Blog", "beatles blog",
ou mesmo "BeAtlEs blOG".
:lookup:`contains`
Sensível a maiúsculas, testa se contém. Por exemplo::
Entry.objects.get(headline__contains='Lennon')
É traduzido para este SQL:
.. code-block:: sql
SELECT ... WHERE headline LIKE '%Lennon%';
Note que combinará com cabeçalhos ``'Today Lennon honored'`` mas não
``'today lennon honored'``.
Há também uma versão não sensível a maiúsculas, :lookup:icontains`.
:lookup:`startswith`, :lookup:`endswith`
Começa com e termina com, respecitivamente. Há também a versão não
sensível a maiúsculas chamada :lookup:`istartswith` e
:lookup:`iendswith`.
De novo, estes são somente arranhões na superfície. Uma referência completa pode ser encontrada na referência os campos de pesquisa.
Pesquisas que abrangem relacionamentos¶
O Django oferece um poderoso e intuitovo meio de “seguir” relacionamentos numa
pesquisa, preocupando-se com os JOIN
s SQL por vocês automaticamente, por
trás das cenas. Para abranger um relacionamento, é só usar o nome dos campos
relacionados atráves dos models, separados por dois underscores, até que você
obtenha os campos que você deseja.
Este exemplo recebe todos os objetos Entry
com um Blog
cujo name
seja 'Beatles Blog'
:
>>> Entry.objects.filter(blog__name__exact='Beatles Blog')
Esta abrangência pode ser tão funda quanto você quiser.
Ela funciona com backwards, também. Para referenciar um relacionamento “reverso”, é só colocar em minúsculo o nome do model.
Este exemplo recebe todos os objetos Blog
que tenham pelo menos um Entry
cujo headline
contenha 'Lennon'
:
>>> Blog.objects.filter(entry__headline__contains='Lennon')
Se você estiver fazendo filtragem através de vários relacionamentos e um dos
models intermediários não tiver um valor que fechar com a condição do filtro, o
Django tratará ela como se estivesse vazia (todos os valores são NULL
), mas
válidos. Tudo isso significa que nenhum erro será gerado. Por exemplo, neste
filtro:
Blog.objects.filter(entry__author__name='Lennon')
(se houvesse um model relacionado Author
), caso não houvesse nenhum
author
associado com uma entrada, ele poderia ser tratado como se também não
tivesse nenhum name
atachado, ao invés de lançar um erro por não ter o
author
. Normalmente este é exatamente isso que você espera que aconteça. O
único caso onde ele pode ser confuso é se você estiver usando isnull
. Deste
modo:
Blog.objects.filter(entry__author__name__isnull=True)
retornará objetos Blog
que possuem um name
vazio no author
e também
aqueles que têm um author
vazio sobre os entry
. Se você não quer esses
últimos objetos, você poderia escrever:
Blog.objects.filter(entry__author__isnull=False,
entry__author__name__isnull=True)
Abrangendo relacionamentos de múltiplos valores¶
Quando você está filtrando um objeto baseado no ManyToManyField
ou numa
ForeignKeyField
reverso, há dois diferentes tipos de filtro que podem ser
interessantes. Considere o relacionamento Blog
/Entry
(Blog
para
Entry
é um relacionamento one-to-many). Nós podemos estar interessados em
encontrar blogs que tenham uma entrada que possui ambos, “Lennon” no título e
que foi publicado em 2008. Ou nós podemos querer encontrar blogs que tenham uma
entrada com “Lennon” no título bem como uma que fora publicada em 2008. Já que
há várias entradas associadas com um único Blog
, ambas consultas são
possíveis e fazem sentido em algumas situações.
O mesmo tipo de situação surge com um ManyToManyField
. Por exemplo, se uma
Entry
tem um ManyToManyField
chamado tags
, nós podemos querer
encontrar entradas likadas às tags chamadas “music” e “bands” ou nós podemos
desejar uma entrada que contenha uma tag com o nome de “musci” e um status de
“public”.
Para lidar com ambas situações, o Django tem um forma consistente de
processar chamadas filter()
e exclude()
. Tudo dentro de um única chamada
filter()
é aplicado simultaneamente para filtrar ítens que combinam com
todos esses requisitos. Sucessivas chamadas filter()
adicionais restringem o
conjunto de objetos, mas para relações com múltiplos valores, se aplicam a
qualquer objeto linkado a primeira chamada filter()
.
Eles podem soar um pouco confusa, então espero que um exemplo dê uma clareada. Para selecionar todos os blogs que contêm entradas tanto com “Lennon” no título e que foram publicados em 2008 (a mesma entrada satisfazendo ambas condições), nós escreveriámos:
Blog.objects.filter(entry__headline__contains='Lennon',
entry__pub_date__year=2008)
Para selecionar todos os blogs que contêm um entrada com “Lennon” no título bem como uma entrada que fora publicada em 2008, nós escrevíamos:
Blog.objects.filter(entry__headline__contains='Lennon').filter(
entry__pub_date__year=2008)
Neste segundo exemplo, o primeiro filtro restringe o queryset para todos os
blogs linkados a esse tipo particular de entrada. O segundo filtro restringe o
conjunto de blogs mais as que também foram linkadas ao segundo tipo de entrada.
As entradas selecionadas no segundo filtro podem ou não, ser as mesmas entradas
do primeiro filtro. Nós estamos filtrando os ítens Blog
com cada declaração
de filtro, não ítens Entry
.
Todo esse comportamento também é aplicado ao exclude()
: todas as condições
numa única declaração exclude()
é aplicada a uma única instância (se estas
condições estiverem falando dessa mesma relação de múltiplos valores). Condições
em chamadas filer()
ou exclude()
subsequentes que referem-se ao mesmo
relacionamento podem terminar filtrando diferentes objetos linkados.
Atalho para pk¶
Por conveniência, o Django fornece um atalho para pesquisas com pk
, que
significa “chave primária”.
No exemplo Blog
model, a chave primária é o campo id
, então estas três
regras são equivalente:
>>> Blog.objects.get(id__exact=14) # Forma explicita
>>> Blog.objects.get(id=14) # __exact é implícito
>>> Blog.objects.get(pk=14) # pk implica em id__exact
O uso de pk
não é limitado a consultas __exact
– qualquer termo de
consulta pode ser combinado com pk
para executar uma consulta sobre a chave
primária de um model:
# Pega as entradas dos blogs com id 1, 4 e 7
>>> Blog.objects.filter(pk__in=[1,4,7])
# Pega todas as entradas do blog com id > 14
>>> Blog.objects.filter(pk__gt=14)
Pesquisas com pk
também podem funcionar através de joins. Por exemplo, estas
três regras são equivalentes:
>>> Entry.objects.filter(blog__id__exact=3) # Froma explicita
>>> Entry.objects.filter(blog__id=3) # __exact é implícito
>>> Entry.objects.filter(blog__pk=3) # __pk implica em __id__exact
Escapando sinais de porcentagem e underscores em consultas LIKE¶
Os campos de pesquisa que equacionam consultas SQL LIKE
(iexact
,
contains
, icontains
, startswith
, istartswith
, endswith
e iendswith
) terão automaticamente escapados os dois caracteres especiais
usados no LIKE
– o sinal de porcentagem e o underscore. (Numa regra
LIKE
, o sinal de porcentagem significa um coringa de multiplos caracteres e
o underscore significa um coringa de um único caractere.)
Isso signifca que coisas devem funcionar intuitivamente, então a abstração não vaza. Por exemplo, para receber todas as entradas que contenham um sinal de porcentagem, é só usar o sinal de porcentagem como qualquer outro caractere:
>>> Entry.objects.filter(headline__contains='%')
O Django se encarrega de escapar para você; o SQL resultante parecerá com algo assim:
SELECT ... WHERE headline LIKE '%\%%';
O mesmo serve para undescores. Ambos, sinais de porcentagem e undersocres, são tratados para você transparentemente.
Cacheamento e QuerySets¶
Cada QuerySet
contém um cache, para minizar o acesso ao banco de dados. É
importante entender como ele funciona, a fim de se escrever um código mais
eficiente.
Num QuerySet
recem criado, o cache está vazio. Na primeira vez que um
QuerySet
for avaliado – e, por isso, uma consulta ao banco de dados
acontece – o Django salva o resultado da consulta num cache de QuerySet
e
retorna os resultado que foram explicitamente requisitados (e.g., o próximo
elemento, se o QuerySet
estiver sendo iterado). Avaliações subsequentes de
um QuerySet
reusam os resultados cacheados.
Mantenha esse comportamento de cache em mente, pois ele pode morder você se você
não usar seus QuerySet
s correntamente. Por exemplo, a seguir serão criados
dois QuerySet
s, eles serão avaliados, e mostrados na tela:
>>> print [e.headline for e in Entry.objects.all()]
>>> print [e.pub_date for e in Entry.objects.all()]
Isso significa que a mesma consulta ao banco de dados será executada duas
vezes, efetivamente dobrando sua carga sobre o banco de dados. Também, há a
possibilidade de duas listas que podem não incluir os mesmos dados do banco,
pois uma Entry
pode ter sido adicionada ou deletada na fração de segundo que
divide as duas requisições.
Para evitar este problema, simplesmente salve o QuerySet
e reuse-o:
>>> queryset = Poll.objects.all()
>>> print [p.headline for p in queryset] # Avalia o query set.
>>> print [p.pub_date for p in queryset] # Re-usa o que está em cache.
Consultas complexas com objetos Q¶
Consultas com palavras chaves – no filter()
, etc. – são mescladas com
“AND”. Se você precisa executar consultas mais complexas (por exemplo, consultas
com OR
), você pode usar objetos Q
.
Um objeto Q
(django.db.models.Q
) é um objeto usado para encapsular uma
coleção de argumentos nomeados. Estes argumentos são especificados assim como
nos “Campos de pesquisa” acima.
Por exemplo, este objeto Q
encapsula uma única consulta LIKE
:
Q(question__startswith='What')
Objetos Q
podem ser combinados usando os operadores &
e |
. Quando um
operador é usado em dois objetos Q
, ele produz um novo objeto Q
.
Por exemplo, esta regra produz um único objeto Q
que representa o “OR” ou
duas consultas "question__startwith"
:
Q(question__startswith='Who') | Q(question__startswith='What')
Este é equivalente a seguinte clausula SQL WHERE
:
WHERE question LIKE 'Who%' OR question LIKE 'What%'
Você pode compor declarações de complexidade arbitrária combinando objetos Q
com os operadores &
e |
e usar o agrupamento paramétrico. Também,
objetos Q
podem ser negados usando o operador ~
, permitindo combinações
entre consultas normais e negadas (NOT
):
Q(question__startswith='Who') | ~Q(pub_date__year=2005)
Cada função de pesquisa que recebe argumentos nomeados (e.g. filter()
,
exclude()
, get()
) podem também ser passados para um ou mais objetos
Q
como um argumento posicional. Se você fornece vários argumentos de objeto
Q
para uma função de pesquisa, os argumentos serão mesclados com “AND”. Por
exemplo:
Poll.objects.get(
Q(question__startswith='Who'),
Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6))
)
... isso se traduz aproximadamente no SQL:
SELECT * from polls WHERE question LIKE 'Who%'
AND (pub_date = '2005-05-02' OR pub_date = '2005-05-06')
Funções de pesquisa podem combinar o uso de objetos Q
e argumentos nomeados.
Todos os argumentos fornecidos para uma função de pesquisa (sejam eles agumentos
ou objetos Q
) são mesclados com “AND”. No entanto, se um objeto Q
é
fornecido, ele deve preceder a definição de qualquer argumento nomeado.
Por exemplo:
Poll.objects.get(
Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6)),
question__startswith='Who')
... poderia ser uma consulta válida, equivalente ao exemplo anterior; mas:
# CONSULTA INVÁLIDA
Poll.objects.get(
question__startswith='Who',
Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6)))
... não seria válida.
See also
O exemplos do OR nos unit tests do Django é mostrado alguns possíveis
usos do Q
.
Comparando objetos¶
Para comparar duas instâncias de model, é só usar o operador de comparação
padrão do Python, dois sinais de igual: ==
. Por trás das cenas, o que é
comparado é a chave primária dos dois models.
Usando o exemplo Entry
acima, as duas declarações a seguir são
equivalentes:
>>> some_entry == other_entry
>>> some_entry.id == other_entry.id
Se uma chave primária de um model não é chamada de id
, não tem problema.
As comparações sempre usaram a chave primária, seja lá como for chamada. Por
exemplo, se uma chave primária de um model é chamada name
, estas duas
declarações são equivalentes:
>>> some_obj == other_obj
>>> some_obj.name == other_obj.name
Deletando objetos¶
O método delete, convenientemente é chamado delete()
. Este método deleta
imediatamente o objeto e não retorna valor. Exemplo:
e.delete()
Você pode também deletar objetos em grupos. Todo QuerySet
tem um método
delete()
, que deleat todos os seus membros.
Por exemplo, isso deleta todos os objetos Entry
com um pub_date
do ano
2005:
Entry.objects.filter(pub_date__year=2005).delete()
Tenha em mente que isso irá, sempre que possível, ser executado puramente em
SQL, sendo assim o método delete()
de instâncias de objeto individuais não
precisaram ser chamadas durante o processo. Se você forneceu um método
delete()
a uma classe model e quer se assegurar de que ele será chamado,
você precisa deletar as instâncias “manualmente” (e.g., iterando sobre o
QuerySet
e chamando o delete()
de cada objeto individualmente) ao invés
de usar um método delete()
de grupo do QuerySet
.
Quando o Django deleta um objeto, ele emula o comportamento de restrições
(CONSTRAINT) do SQL ON DELETE CASCADE
– em outras palavras, quaisquer
objetos que possuam uma chave estrangeira apontando ao objeto que está para ser
deletado, também será deletado com ele. Por exemplo:
b = Blog.objects.get(pk=1)
# Isso irá deletar o Blog e todos os seus objetos Entry.
b.delete()
Note que delete()
é o único método de QuerySet
que não é exposto num
Manager
em si. Este é um mecanismo de segurança para previnir que você
acidentalmente requisite Entry.objects.delete()
, e apague todas as
entradas. Se você deseja deletar todos os objetos, então você deve
explicitamente requisitas uma consulta completa:
Entry.objects.all().delete()
Atualizando vários objetos de uma vez¶
As vezes você quer atribuir um valor em particular a um campo de todos os
objetos de um QuerySet
. Você pode fazer isso com o método update()
.
Por exemplo:
# Atualiza todos os headlines com o pub_date em 2007.
Entry.objects.filter(pub_date__year=2007).update(headline='Everything is the same')
Você somente pode setar compos não relacionados e campos ForeignKey
usando
este método, e o valor que você configurar o campo deve ser um valor nativo do
Python (i.e., você não pode configurar um campo para ser igual a algum outro
campo nesse momento).
Para atualizar campos ForeignKey
, configure o novo valor na nova instância
de model que você deseja apontar. Exemplo:
>>> b = Blog.objects.get(pk=1)
# Muda todos os Entry que pretençam a este Blog.
>>> Entry.objects.all().update(blog=b)
O método update()
é aplicado instantâneamente e não retorna valores (
semelhante ao delte()
). A única restrição do QuerySet
que foi atualizado
é que ele só pode acessar uma tabela do banco de dados, a tabela principal do
model.
Tenha cuidado que o método update()
é convertido diretamente numa declaração
SQL. Ela é uma operação de massa para atualizações direta. Não executa qualquer
método save()
no seus models, ou emite os sinais pre_save
ou
post_save
(que são consequencias da chamada do save()
). Se você deseja
salvar todos os ítens de um QuerySet
assegure-se de que o método save()
seja chamada em cada instância, você não precisa de nenhuma função especial para
lidar com isso. É só iterar sobre eles e chamar o save()
:
for item in my_queryset:
item.save()
Objetos relacionados¶
Quando você define um relacionamento num model (i.e., um ForeignKey
,
OneToOneField
, ou ManyToManyField
), instâncias desse model terão uma API
conveniente para acessar os objetos relacionados.
Usando os models do topo dessa página, por exemplo, um objeto Entry
e
pode obter seus objetos Blog
associados acessando um atributo blog` :
``e.blog
.
(Por trás das cenas, esta funcionalidade é implementada por descriptors Python. Isso realmente não deve importar, mas nós o apontamos aqui só por curiosidade.)
O Django também cria uma API de acessores para o outro lado do
relacionamento – o link de um model relacionado para outro que define o
relacionamento. Por exemplo, um objeto Blog
b
tem acesso a lista de
todos os objetos Entry
relacionados via o atributo entry_set
:
b.entry_set.all()
.
Todos os exemplos nesta seção usam as amostras de model Blog
, Author
e
Entry
definidos no topo dessa página.
Relacionamentos Um-para-muitos (One-to-many)¶
Forward¶
Se um modem tem uma ForeignKey
, instâncias desse model terão acesso ao
objeto relacionado (foreign) via um simples atribuot do model.
Exemplo:
>>> e = Entry.objects.get(id=2)
>>> e.blog # Retorna o objeto Blog relacionado.
Você pode obter um conjunto via um atributo foreing-key. Assim como você pode
esperar, mudanças na chave estrangeira não são salvas no banco de dados até que
você chame save()
.
Exemplo:
>>> e = Entry.objects.get(id=2)
>>> e.blog = some_blog
>>> e.save()
Se um campo ForeignKey
tem null=True
(i.e., ele permite valores
NULL
), você pode atrituir None
a ele. Exemplo:
>>> e = Entry.objects.get(id=2)
>>> e.blog = None
>>> e.save() # "UPDATE blog_entry SET blog_id = NULL ...;"
Remeter acesso a relacionamentos one-to-many é chacheado na primeira vez em que o objeto relacionado é acessado. Subsequentes acessos a chave estrangeira do mesmo objeto são cacheados. Exemplo:
>>> e = Entry.objects.get(id=2)
>>> print e.blog # Consluta o banco de dados para receber o Blog associado.
>>> print e.blog # Não consulta o banco de dados. usa a versão em cache.
Note que o método select_related()
do QuerySet
recursivamente prepopula
o cache de todos os relacionamentos one-to-many de antemão. Exemplo:
>>> e = Entry.objects.select_related().get(id=2)
>>> print e.blog # Não consulta o banco de dados; usa a versão em cache.
>>> print e.blog # Não consulta o banco de dados; usa a versão em cache.
Seguindo os relacionamentos “backward”¶
Se um model tem uma ForeignKey
, instâncias da chave estrangeira do model
terão acesso ao Manager
que retorna todas as instâncias do primeiro model.
Por padrão, esse Manager
é chamado FOO_set
, onde FOO
é o nome do
model fonte, em minusculo. Esse Manager
retorna QuerySets
, que podem ser
filtrados e manipulados como descrito na seção “Recebendo objetos” acima.
Exemplo:
>>> b = Blog.objects.get(id=1)
>>> b.entry_set.all() # Retorna todas os objetos Entry relacionados ao Blog.
# b.entry_set é um Manager que retorna QuerySets.
>>> b.entry_set.filter(headline__contains='Lennon')
>>> b.entry_set.count()
Você pode sobrescrever o nome FOO_set
através do parâmetro related_name
na definição do ForeignKey
. Por exemplo, se o model Entry
fosse alterado
para blog = ForeignKey(Blog, related_name='entries')
, o código do exemplo
acima ficaria:
>>> b = Blog.objects.get(id=1)
>>> b.entries.all() # Retorna todos os objetos Entry relacionados ao Blog.
# b.entries é um Manager que retorna QuerySets.
>>> b.entries.filter(headline__contains='Lennon')
>>> b.entries.count()
Você não pode acessar um Manager
ForeignKey
de uma classe no sentido
contrário; ele deve ser acessado de uma instância:
>>> Blog.entry_set
Traceback:
...
AttributeError: "Manager must be accessed via instance".
Além dos métodos do QuerySet
definidos em “Recebendo objetos” acima, o
Manager
ForeignKey
possui métodos adicionais utilizados para lidar com
conjuntos de objetos relacionados. Uma sinopse de cada um está abaixo, e
detalhes completos podem ser encontrados na referência de objetos
relacionados.
add(obj1, obj2, ...)
- Adiciona os objetos do model especificados ao conjunto do objeto relacionado.
create(**kwargs)
- Cria um novo objeto, salva-o e coloca-o no conjunto do objeto relacionado. Retorna o novo objeto criado.
remove(obj1, obj2, ...)
- Remove o os objetos model especificados do conjunto do objeto relacinoado.
clear()
- Remove todos os objetos do conjunto do objeto relacionado.
Para atribuir memtros de um conjunto relacionado de uma só vez, é só atribuí-lo de qualquer objeto iterável. O iterável pode conter instâncias de objetos, ou só uma lista de valores de chave primária. Por exemplo:
b = Blog.objects.get(id=1)
b.entry_set = [e1, e2]
Neste exemplo, e1
e e2
pode ser uma instância completa do Entry, ou
valores inteiros de chave primária.
Se o método clear()
estiver disponível, quaisquer objetos pre-existentes
serão removidos do entry_set
antes de todos os objetos no iterável (neste
caso, uma lista) são adicionados ao conjunto. Se o método clear()
não
estiver disponível, todos os objetos no iterável serão adicionados sem remover
quaisquer elementos existentes.
Cada operação “reverse” descrita nesta seção tem um efeito imediato sobre o banco de dados. Toda adição, criação e deleção é imediatamente e automaticamente salva no banco de dados.
Relacionamentos muitos-para-muitos (Many-to-many)¶
Amdas as extremidades de um relacionamento many-to-many obtêem uma API de acesso automática para o outro lado. A API funciona exatamente como o “backward” do relacionamento one-to-many, acima.
A única diferença é na nomeação do atributo: O model que define o
ManyToManyField
usa o nome do atributo do próprio campo, considerando que o
model “reverso” usa o nome do model original em minúscula, mais '_set'
(assim como reverso de relacionamentos one-to-many).
Com um exemplo fica mais fácil entender:
e = Entry.objects.get(id=3)
e.authors.all() # Retorna todos os objetos Author desta Entry.
e.authors.count()
e.authors.filter(name__contains='John')
a = Author.objects.get(id=5)
a.entry_set.all() # Retorna todos os objetos Entry desse Author.
Como ForeignKey
, o ManyToManyField
pode especificar related_name
.
No exemplo acima, se o ManyToManyField
em Entry
tivesse especificado
related_name='entries'
, então cada instância de Author
teria um atributo
entries
ao invés de entry_set
.
Relacionamentos Um-pra-um (One-to-one)¶
Relacionamentos One-to-one são muito similares aos relacionamentos many-to-many.
Se você define uma OneToOneField
no seu model, as
instâncias desse model terão acesso ao objeto relacionado atráves de um simples
atributo de um model.
Por exemplo:
class EntryDetail(models.Model):
entry = models.OneToOneField(Entry)
details = models.TextField()
ed = EntryDetail.objects.get(id=2)
ed.entry # Retorna o objeto Entry relacionado.
A diferença vem nas consultas “reverse”. O model relacionado num relacionamento
one-to-one também tem acesso ao objeto Manager
, mas
esse Manager
representa um único objeto, ao invés de
uma coleção de objetos:
e = Entry.objects.get(id=2)
e.entrydetail # retorna o objeto EntryDetail relacionado
Se nenhum objeto foi atribuído a este relacionamento, o Django lançará uma
exceção DoesNotExist
.
Instâncias podem ser atribuídas ao relacionamento reverso da mesma forma como você poderia atribuir ao relacionamento forward:
e.entrydetail = ed
Como é possível relacionamentos backward?¶
Outro mapeador de objeto relacional requer que você defina relacionamentos em abos os lados. Os desenvolvedores do Django acreditam que isso é uma violação do princípio DRY (Don’t Repeat yourself), então o Django somente requer que você defina o relacionamento de um lado.
Mas como é possível, dado que uma classe model não sabe qual outros classes model estão relacionadas até que essas outras classes sejam carregadas?
A resposta está na configuração INSTALLED_APPS
. A primeira vez em que
qualquer model é carregado, o Django itera sobre todos os models no
INSTALLED_APPS
e cria os relacionamentos backward na memória se
necessário. Essencialmente, uma das funções do INSTALLED_APPS
é dizer
ao Django os domínios dos models.
Consultas sobre objetos relacionados¶
Consultas envolvendo objetos relacionados seguem as mesmas regras de consultas envolvendo campos de valores normais. Quando especificar o valor para combinar numa consulta, você pode usar tanto uma instância de objeto em si, quando o valor da chave primária do objeto.
Por exemplo, se você tem um objeto Blog b
com id=5
, as três consultas
seguintes seriam idênticas:
Entry.objects.filter(blog=b) # Consulta usando intância de objeto
Entry.objects.filter(blog=b.id) # Consulta usando id da instância
Entry.objects.filter(blog=5) # Consulta usando id diretamente
Voltando ao SQL puro¶
Se você se encontrou precisando escrever uma consulta SQL que é mais complexa para o mapeador de banco de dados do Django, você pode voltar ao modo de consultas em SQL puro.
A forma preferida para fazer isso é dando ao model, métodos personalizados ou métodos personalizados ao manager, que executam consultas. Embora não há nada no Django que obrigue consultas de banco de dados estarem na camada de model, esta abordagem mantem toda a lógica de acesso a dados num único lugar, o que é mais inteligente para organização de código. Para instruções, veja Performing raw SQL queries.
Finalmente, é importante notar que a camada de banco de dados do Django é meramente uma interface para o seu banco de dados. Você pode acessar seu banco por outras ferramentas, linguagens de programação ou frameworkds de banco de dados; não há nada específico do Django sobre o seu banco de dados.