Boas práticas - Django

 ler material sobre models




Antes de chegar ao ponto crucial deste post, há uma questão fundamental que acho que devo responder.


Por que você deve gastar uma boa quantidade de tempo projetando o modelo de dados?

A “elegância” do seu design é diretamente proporcional ao tempo e esforço necessários para adicionar novos recursos e modificar os existentes. Um modelo de dados bem projetado não apenas permite um processo de desenvolvimento tranquilo, mas também torna seu código-fonte mais compreensível, sustentável e ajuda você a seguir o princípio DRY de uma maneira melhor.

Aqui vamos nós:

Torná-los gordos


Existe um ditado famoso entre os desenvolvedores de Django, “modelos gordos e visualizações finas”. Na minha experiência, nunca é uma boa ideia colocar sua lógica de negócios em visualizações. Para o exemplo mais simples, vamos supor que armazenamos os dados do cliente em um modelo:

classe Customer (models.Model):
first_name = models.CharField (max_length = 25)
last_name = models.CharField (max_length = 25)
address = models.CharField (max_length = 255)

Em seu aplicativo, pode haver momentos em que você precisa gerar o endereço de correspondência de um cliente e isso também pode ser entre visualizações. Nesse caso, gerar endereço em cada vista seria uma violação clara do princípio DRY.

Em vez disso, o que você pode fazer é criar uma propriedade na Customerclasse do modelo e usá-la sempre que o endereço do cliente for necessário:

@property
def mailing_address (self):
return f "{self.first_name} {self.last_name}, {self.address}"

Como regra geral, a lógica de negócios que não está relacionada ao processamento de dados de solicitação ou dependente de front end se ajusta melhor ao modelo.

Seja específico com relacionamentos de dados


Use melhor estas palavras-chave: OneToOneField & ManyToManyField.
Para explicar melhor, daria um exemplo para cada um.

Uma relação um para um pode ser definida assim:

class Customer (models.Model):
first_name = models.CharField (max_length = 25)
last_name = models.CharField (max_length = 25)
house_no = models.CharField (max_length = 5)
street_address = models.CharField (max_length = 255)
class Vehicle (models.Model):
make = models.CharField (max_length = 25)
model = models.CharField (max_length = 25)
customer = models.ForeignKey (Customer, unique = True)

Evite usar ForeignKeycom unique=True. Isso pode ser feito de uma forma mais orientada para a estrutura usando OneToOneField, desta forma:

class Customer (models.Model):
first_name = models.CharField (max_length = 25)
last_name = models.CharField (max_length = 25)
house_no = models.CharField (max_length = 5)
street_address = models.CharField (max_length = 255)
class Vehicle (models.Model):
make = models.CharField (max_length = 25)
model = models.CharField (max_length = 25)
customer = models.OneToOneField (Customer)

Tradicionalmente, um relacionamento muitos para muitos entre 2 tabelas é definido usando uma terceira tabela de junção como no exemplo a seguir:

classe Author (models.Model):
first_name = models.CharField (max_length = 25)
last_name = models.CharField (max_length = 25)
class Book (models.Model):
name = models.CharField (max_length = 25)
price = models.FloatField ()
#
Classe da tabela de junção BookAuthor (models.Model):
book = models.ForeignKey (Book)
author = models.ForeignKey (Author)

O Django ManyToManyFieldcria automaticamente uma tabela de junção nos bastidores e fornece um ótimo conjunto de API para recuperar dados relacionados. Ele pode ser usado conforme mostrado no exemplo:

class Book (models.Model):
name = models.CharField (max_length = 25)
price = models.FloatField ()
classe Author (models.Model):
first_name = models.CharField (max_length = 25)
last_name = models.CharField (max_length = 25)
book = models.ManyToManyField (Livro)

Use modelos abstratos


É sempre aconselhável usar modelos abstratos para evitar a repetição de campos que são comuns para várias tabelas, como criado, atualizado e soft_delete. Os modelos abstratos não têm reflexo no banco de dados e não podem ser instanciados.

Você pode usar modelos abstratos como este:

#
Classe de modelo abstrato BaseModel (models.Model):
created = models.DateTimeField (auto_now_add = True)
updated = models.DateTimeField (auto_now = True)
soft_delete = models.BooleanField (default = False)
classe Meta:
abstract = True
'' 'Você não precisa declarar repetidamente os campos criados, atualizados e soft_delete em todos os seus modelos' ''
classe Autor (BaseModel):
first_name = models.CharField (max_length = 25)
last_name = models.CharField (max_length = 25)
book = models.ManyToManyField (Livro)

BooleanField


Não use null=Trueou blank=Truepara BooleanField. É sempre uma boa prática atribuir um valor padrão a eles. Se você pensar que um nullvalor precisa ser atribuído, use o NullBooleanField.

Não use se queryset


Usar if queryset: do somethingé uma má ideia. Em vez disso, use if queryset.exists(): do something. Lembre-se de que os QuerySets são preguiçosos, usá-los como um valor booleano executará uma consulta correspondente no banco de dados.

Nunca faça len (queryset)


Usar lenpara localizar, obter contagem de objetos é uma prática muito pobre. Em vez disso, use queryset.count(). Isso len(ModelName.objects.all())executará primeiro a consulta para recuperar todos os registros da tabela, depois irá convertê-lo em um objeto Python e o comprimento desse objeto será encontrado usando len. O uso de count executará uma COUNT()consulta SQL apropriada no banco de dados, que também será muito mais barata e rápida.

Use gerenciadores personalizados

À medida que um projeto cresce em tamanho, os desenvolvedores tendem a repetir o mesmo trecho de código em lugares diferentes.
Para manter o seu código compatível com o princípio DRY e acomodar a lógica de negócios em modelos, use gerenciadores personalizados. Suponha que você tenha um Post modelo como este:
class Post (BaseModel):
title = models.CharField (max_length = 200)
content = models.TextField ()
active = models.BooleanField (default = False)

Com o exemplo mais simples, digamos que você precise de todas as postagens publicadas, identificadas por:
publicações_posts = modelos.Post.objetoss.filtro (ativo = Verdadeiro)

Você pode escrever um gerenciador personalizado para conseguir a mesma coisa, como este:
classe PublishedManager (models.Manager):
def get_queryset (self):
return super (PublishedManager, self) .get_queryset (). filter (ativo = True)
class Post (models.Model):
......
# Model managers
objects = models.Manager () # O gerenciador padrão.
publicado = PublishedManager () # Nosso gerenciador personalizado.

Agora podemos recuperar postagens publicadas com o gerenciador personalizado da seguinte maneira:
In [2]: Post.published.all ()
Out [2]: <QuerySet [<Post: First post>, <Post: Second post>]>

Do gerente modelo personalizado get_queryset()retorna um objeto QuerySet, assim você pode usar filter(), exclude()e todos os outros métodos do QuerySet sobre ele.

Construir um bom modelo de dados no Django nunca é uma perda de tempo. Ao contrário, ter um modelo claro e sólido torna o design do restante do aplicativo em seu projeto Django uma brisa.

Portanto, não tenha vergonha de reservar um tempo extra para planejar a estrutura do seu modelo. Consulte-o com seus companheiros de equipe. Certifique-se de que seja fácil de entender para todos e capture as necessidades do seu aplicativo. Você vai agradecer a si mesmo no futuro.

Antes de chegar ao ponto crucial deste post, há uma questão fundamental que acho que devo responder.
Por que você deve gastar uma boa quantidade de tempo projetando o modelo de dados?
A “elegância” do seu design é diretamente proporcional ao tempo e esforço necessários para adicionar novos recursos e modificar os existentes. Um modelo de dados bem projetado não apenas permite um processo de desenvolvimento tranquilo, mas também torna seu código-fonte mais compreensível, sustentável e ajuda você a seguir o princípio DRY de uma maneira melhor.

Aqui vamos nós:

Torná-los gordos


Existe um ditado famoso entre os desenvolvedores de Django, “modelos gordos e visualizações finas”. Na minha experiência, nunca é uma boa ideia colocar sua lógica de negócios em visualizações. Para o exemplo mais simples, vamos supor que armazenamos os dados do cliente em um modelo:

classe Customer (models.Model):
first_name = models.CharField (max_length = 25)
last_name = models.CharField (max_length = 25)
address = models.CharField (max_length = 255)

Em seu aplicativo, pode haver momentos em que você precisa gerar o endereço de correspondência de um cliente e isso também pode ser entre visualizações. Nesse caso, gerar endereço em cada vista seria uma violação clara do princípio DRY.
Em vez disso, o que você pode fazer é criar uma propriedade na Customerclasse do modelo e usá-la sempre que o endereço do cliente for necessário:

@property
def mailing_address (self):
return f "{self.first_name} {self.last_name}, {self.address}"

Como regra geral, a lógica de negócios que não está relacionada ao processamento de dados de solicitação ou dependente de front end se ajusta melhor ao modelo.

Seja específico com relacionamentos de dados

Use melhor estas palavras-chave: OneToOneField & ManyToManyField.
Para explicar melhor, daria um exemplo para cada um.

Uma relação um para um pode ser definida assim:

class Customer (models.Model):
first_name = models.CharField (max_length = 25)
last_name = models.CharField (max_length = 25)
house_no = models.CharField (max_length = 5)
street_address = models.CharField (max_length = 255)
class Vehicle (models.Model):
make = models.CharField (max_length = 25)
model = models.CharField (max_length = 25)
customer = models.ForeignKey (Customer, unique = True)

Evite usar ForeignKeycom unique=True. Isso pode ser feito de uma forma mais orientada para a estrutura usando OneToOneField, desta forma:

class Customer (models.Model):
first_name = models.CharField (max_length = 25)
last_name = models.CharField (max_length = 25)
house_no = models.CharField (max_length = 5)
street_address = models.CharField (max_length = 255)
class Vehicle (models.Model):
make = models.CharField (max_length = 25)
model = models.CharField (max_length = 25)
customer = models.OneToOneField (Customer)

Tradicionalmente, um relacionamento muitos para muitos entre 2 tabelas é definido usando uma
terceira tabela de junção como no exemplo a seguir:

classe Author (models.Model):
first_name = models.CharField (max_length = 25)
last_name = models.CharField (max_length = 25)
class Book (models.Model):
name = models.CharField (max_length = 25)
price = models.FloatField ()
#
Classe da tabela de junção BookAuthor (models.Model):
book = models.ForeignKey (Book)
author = models.ForeignKey (Author)

O Django ManyToManyFieldcria automaticamente uma tabela de junção nos bastidores e fornece um ótimo conjunto de API para recuperar dados relacionados. Ele pode ser usado conforme mostrado no exemplo:

class Book (models.Model):
name = models.CharField (max_length = 25)
price = models.FloatField ()
classe Author (models.Model):
first_name = models.CharField (max_length = 25)
last_name = models.CharField (max_length = 25)
book = models.ManyToManyField (Livro)

Use modelos abstratos


É sempre aconselhável usar modelos abstratos para evitar a repetição de campos que são comuns para várias tabelas, como criado, atualizado e soft_delete. Os modelos abstratos não têm reflexo no banco de dados e não podem ser instanciados.
Você pode usar modelos abstratos como este:

#
Classe de modelo abstrato BaseModel (models.Model):
created = models.DateTimeField (auto_now_add = True)
updated = models.DateTimeField (auto_now = True)
soft_delete = models.BooleanField (default = False)
classe Meta:
abstract = True
'' 'Você não precisa declarar repetidamente os campos criados, atualizados e soft_delete em todos os seus modelos' ''
classe Autor (BaseModel):
first_name = models.CharField (max_length = 25)
last_name = models.CharField (max_length = 25)
book = models.ManyToManyField (Livro)

BooleanField


Não use null=Trueou blank=Truepara BooleanField. É sempre uma boa prática atribuir um valor padrão a eles. Se você pensar que um nullvalor precisa ser atribuído, use o NullBooleanField.

Não use se queryset


Usar if queryset: do somethingé uma má ideia. Em vez disso, use if queryset.exists(): do something. Lembre-se de que os QuerySets são preguiçosos, usá-los como um valor booleano executará uma consulta correspondente no banco de dados.

Nunca faça len (queryset)


Usar lenpara localizar, obter contagem de objetos é uma prática muito pobre. Em vez disso, use queryset.count(). Isso len(ModelName.objects.all())executará primeiro a consulta para recuperar todos os registros da tabela, depois irá convertê-lo em um objeto Python e o comprimento desse objeto será encontrado usando len. O uso de count executará uma COUNT()consulta SQL apropriada no banco de dados, que também será muito mais barata e rápida.

Use gerenciadores personalizados


À medida que um projeto cresce em tamanho, os desenvolvedores tendem a repetir o mesmo trecho de código em lugares diferentes.
Para manter o seu código compatível com o princípio DRY e acomodar a lógica de negócios em modelos, use gerenciadores personalizados. Suponha que você tenha um Post modelo como este:

class Post (BaseModel):
title = models.CharField (max_length = 200)
content = models.TextField ()
active = models.BooleanField (default = False)

Com o exemplo mais simples, digamos que você precise de todas as postagens publicadas, identificadas por:

publicações_posts = modelos.Post.objetoss.filtro (ativo = Verdadeiro)

Você pode escrever um gerenciador personalizado para conseguir a mesma coisa, como este:

classe PublishedManager (models.Manager):
def get_queryset (self):
return super (PublishedManager, self) .get_queryset (). filter (ativo = True)
class Post (models.Model):
......
# Model managers
objects = models.Manager () # O gerenciador padrão.
publicado = PublishedManager () # Nosso gerenciador personalizado.

Agora podemos recuperar postagens publicadas com o gerenciador personalizado da seguinte maneira:

In [2]: Post.published.all ()
Out [2]: <QuerySet [<Post: First post>, <Post: Second post>]>

Do gerente modelo personalizado get_queryset()retorna um objeto QuerySet, assim você pode usar filter(), exclude()e todos os outros métodos do QuerySet sobre ele.

Construir um bom modelo de dados no Django nunca é uma perda de tempo. Ao contrário, ter um modelo claro e sólido torna o design do restante do aplicativo em seu projeto Django uma brisa.

Portanto, não tenha vergonha de reservar um tempo extra para planejar a estrutura do seu modelo. Consulte-o com seus companheiros de equipe. Certifique-se de que seja fácil de entender para todos e capture as necessidades do seu aplicativo. Você vai agradecer a si mesmo no futuro.

Ler material



Exemplo prático de teste de velocidade usando as duas formas:
  • Vale ressaltar que a varredura em objetos apresenta comportamento diferente sendo assim melhor utilizar o count() para obter o total de itens.

from timeit import default_timer as timer

test = [x for x in range(1000)]
count = 0

start = timer()
len(test)
end = timer()
print(end - start)

start = timer()
for i in test:
count += 1
end = timer()
print(end - start)

Returns:
2.643069343567298e-06

213.7110354941546e-06


If test = "This is a test string."
Returns:
2.2654880087719696e-06

1.0572277374269745e-05

Ler material


Comentários

Postagens mais visitadas deste blog

Weasyprint

Models - Django