Ir para o conteúdo

Truques e Dicas

Esta parte é dedicada à organização de código dentro da aplicação.

Os exemplos estão mais focados no Esmerald já que o autor é o mesmo, mas novamente, pode fazer o mesmo na sua framework preferida.

Colocar a sua conecção num local centralizado

Provavelmente isto é o que gostaria de fazer na sua aplicação, uma vez que não quer declarar repetidamente as mesmas variáveis.

A principal razão para isto é o facto de que cada vez que se declara um registo, na verdade está a criar um novo objeto e isso não é ideal se precisar de aceder aos documentos utilizados no registo principal, certo?

Colocar os detalhes da conecçâo dentro de um ficheiro de configurações global

Esta é provavelmente a forma mais fácil de colocar os detalhes da conecção, especialmente para o Esmerald, já que possui uma maneira simples e fácil de acessar as configurações em qualquer parte do código.

Algo simples como isto:

"""
Generated by 'esmerald createproject'
"""
from functools import cached_property
from typing import Optional

from esmerald.conf.enums import EnvironmentType
from esmerald.conf.global_settings import EsmeraldAPISettings

from mongoz import Registry


class AppSettings(EsmeraldAPISettings):
    app_name: str = "My application in production mode."
    environment: Optional[str] = EnvironmentType.PRODUCTION
    secret_key: str = "esmerald-insecure-h35r*b9$+hw-x2hnt5c)vva=!zn$*a7#"  # auto generated

    @cached_property
    def db_connection(self) -> Registry:
        """
        To make sure the registry and the database connection remains the same
        all the time, always use the cached_property.
        """
        database = "mongodb://root:mongoadmin@localhost:27017"
        return Registry(database=database)

Como pode ver, agora tem a db_connection num único local e de fácil acesso em qualquer parte do seu código. No caso do Esmerald:

from esmerald.conf import settings

registry = settings.db_connection

Mas isso é suficiente? Não.

Como mencionado anteriormente, ao atribuir ou criar uma variável, o próprio Python gera um novo objeto com um id diferente, que pode ser diferente a cada vez que precisa importar as configurações nos locais necessários.

Não vamos falar sobre este truque, visto que há muita documentação online e mais adequada para este mesmo propósito.

Como resolvemos este problema? Entra em cena o lru_cache.

A LRU cache

LRU extends significa least recently used.

Uma técnica muito comum que visa ajudar a fazer cache de certas partes de funcionalidade dentro do código e garantir que não cria objetos extras, e é exatamente isso que precisamos.

Usando o exemplo acima, vamos agora criar um novo ficheiro chamado utils.py, onde aplicaremos a técnica lru_cache para a nossa db_connection.

utils.py
from functools import lru_cache

from esmerald.conf import settings


@lru_cache()
def get_db_connection():
    registry = settings.db_connection
    return registry

Isto garantirá que a partir de agora irá usar sempre a mesma conecção e registro dentro da aplicação, importando o get_db_connection() sempre que for necessário.

Exemplo prático

Para este exemplo, teremos a seguinte estrutura (não iremos usar todos os ficheiros). Não iremos criar views visto que este não é o propósito do exemplo.

.
└── myproject
    ├── __init__.py
    ├── apps
       ├── __init__.py
       └── accounts
           ├── __init__.py
           ├── tests.py
           └── v1
               ├── __init__.py
               ├── schemas.py
               ├── urls.py
               └── views.py
    ├── configs
       ├── __init__.py
       ├── development
          ├── __init__.py
          └── settings.py
       ├── settings.py
       └── testing
           ├── __init__.py
           └── settings.py
    ├── main.py
    ├── serve.py
    ├── utils.py
    ├── tests
       ├── __init__.py
       └── test_app.py
    └── urls.py
Esta estrutura é gerada utilizando as directivas Esmerald

As configurações

Como mencionado anteriormente, teremos um ficheiro de configurações com as propriedades de ligação à base de dados montadas.

my_project/configs/settings.py
"""
Generated by 'esmerald createproject'
"""
from functools import cached_property
from typing import Optional

from esmerald.conf.enums import EnvironmentType
from esmerald.conf.global_settings import EsmeraldAPISettings

from mongoz import Registry


class AppSettings(EsmeraldAPISettings):
    app_name: str = "My application in production mode."
    environment: Optional[str] = EnvironmentType.PRODUCTION
    secret_key: str = "esmerald-insecure-h35r*b9$+hw-x2hnt5c)vva=!zn$*a7#"  # auto generated

    @cached_property
    def db_connection(self) -> Registry:
        """
        To make sure the registry and the database connection remains the same
        all the time, always use the cached_property.
        """
        database = "mongodb://root:mongoadmin@localhost:27017"
        return Registry(database=database)

Os utilitários

Agora criamos o utils.py onde aplicamos a técnica LRU.

myproject/utils.py
from functools import lru_cache

from esmerald.conf import settings


@lru_cache()
def get_db_connection():
    registry = settings.db_connection
    return registry

Os documentos

Agora podemos começar a criar os nossos documentos e garantir que os mantemos sempre no mesmo registo.

myproject/apps/accounts/documents.py
from datetime import datetime

from my_project.utils import get_db_connection

import mongoz

registry = get_db_connection()


class BaseDocument(mongoz.Document):
    class Meta:
        abstract = True
        registry = registry
        database = "my_db"


class User(BaseDocument):
    """
    Base document for a user
    """

    first_name: str = mongoz.String(max_length=150)
    last_name: str = mongoz.String(max_length=150)
    username: str = mongoz.String(max_length=150, unique=True)
    email: str = mongoz.Email(max_length=120, unique=True)
    password: str = mongoz.String(max_length=128)
    last_login: datetime = mongoz.DateTime(null=True)
    is_active: bool = mongoz.Boolean(default=True)
    is_staff: bool = mongoz.Boolean(default=False)
    is_superuser: bool = mongoz.Boolean(default=False)

Aqui aplicamos a herança para torná-lo mais limpo e legível caso queiramos ainda mais documentos.

Como também pode notar, estamos a importar o get_db_connection() previamente criado. Agora é o que usaremos em todos os lugares.

Notas

O exemplo acima mostra como pode aproveitar local centralizado para gerir suas conecções e usá-las em toda a aplicação, mantendo o código sempre limpo, não redundante e bonito.

Este exemplo pode ser aplicado a qualquer uma das frameworks preferidas e pode usar tantas técnicas diferentes quanto achar adequado para o seu propósito.

Mongoz é independente de qualquer framework.