Estilo de código#

Pautas de estilo de código para contribuciones de Construbot.

Estilo pitón#

Siga PEP 8#

Formato de código:

  • 4 espacios para sangría (sin pestañas)

  • Longitud máxima de línea: 100 caracteres (no estricto 79)

  • 2 líneas en blanco entre funciones/clases de nivel superior

  • 1 línea en blanco entre métodos

Usar negro:

black .

Usar isorción:

isort .

Convenciones de nomenclatura#

# Variables and functions: snake_case
user_count = 10
def calculate_total():
    pass

# Classes: PascalCase
class ContractManager:
    pass

# Constants: UPPER_CASE
MAX_CONTRACTS = 100

# Private: _leading_underscore
def _internal_helper():
    pass

Consejos de tipo#

Se recomienda pero no es obligatorio:

def get_contracts(company: Company) -> QuerySet[Contrato]:
    return Contrato.objects.filter(company=company)

cadenas de documentos#

Usa el estilo de Google:

def create_contract(company, folio, amount):
    """Create a new contract.

    Args:
        company (Company): The company for this contract
        folio (str): Reference number
        amount (Decimal): Contract amount

    Returns:
        Contrato: The created contract

    Raises:
        ValidationError: If folio already exists
    """
    ...

Estilo Django#

Modelo de mejores prácticas#

Incluye siempre compañía:

class MyModel(models.Model):
    company = models.ForeignKey(Company, on_delete=models.CASCADE)

Usar nombre_detallado:

class Contrato(models.Model):
    folio = models.CharField(
        max_length=100,
        verbose_name="Reference Number",
        help_text="Unique identifier for this contract"
    )

Metaopciones:

class Meta:
    ordering = ['-created_at']
    unique_together = ('company', 'folio')
    verbose_name_plural = "Contracts"

Ver mejores prácticas#

Usar vistas basadas en clases:

from django.views.generic import ListView

class ContractListView(LoginRequiredMixin, ListView):
    model = Contrato
    template_name = 'contracts/list.html'

    def get_queryset(self):
        return Contrato.objects.filter(
            company=self.request.user.active_company
        )

Siempre alcance a la empresa:

# GOOD
contracts = Contrato.objects.filter(company=request.user.active_company)

# BAD
contracts = Contrato.objects.all()  # Leaks data!

Optimización de consultas#

Usar select_ related:

# Avoid N+1 queries
contracts = Contrato.objects.select_related(
    'contraparte',
    'sitio'
)

Usar prefetch_relacionado:

users = User.objects.prefetch_related('companies')

Seguridad#

Nunca confíes en la entrada del usuario:

# Use Django forms for validation
form = ContractForm(request.POST)
if form.is_valid():
    contract = form.save()

Utilice get_object_or_404:

contract = get_object_or_404(Contrato, id=contract_id)

Verificar permisos:

if not request.user.nivel_acceso.nivel >= 3:
    raise PermissionDenied()

Estilo de prueba#

Borrar nombres de pruebas:

def test_contract_creation_with_valid_data():
    pass

def test_contract_creation_fails_with_duplicate_folio():
    pass

Utilice la configuración para datos comunes:

class ContractTestCase(TestCase):
    def setUp(self):
        self.company = Company.objects.create(...)

    def test_something(self):
        contract = Contrato.objects.create(company=self.company)

Una afirmación por prueba (idealmente):

def test_contract_folio_is_uppercase(self):
    contract = Contrato.objects.create(folio="abc")
    self.assertEqual(contract.folio, "ABC")

Mensajes de confirmación de Git#

Formato:

Short summary (50 chars max)

More detailed explanation if needed. Wrap at 72 characters.

- List of changes
- Another change

Buenos ejemplos:

Add hierarchical contract support

Implement django-treebeard for parent/child contract relationships.
Includes migration, model updates, and tree query methods.

Fix: Prevent data leak in contract list view

Ensure contracts are filtered by active_company before display.

Malos ejemplos:

Fixed stuff
WIP
asdf
Updated code

Patrones comunes#

Patrón de fábrica para pruebas:

class ContractFactory:
    @staticmethod
    def create(**kwargs):
        defaults = {
            'company': CompanyFactory.create(),
            'folio': 'C-001',
            'monto': Decimal('1000000.00'),
        }
        defaults.update(kwargs)
        return Contrato.objects.create(**defaults)

Administrador de contexto para transacciones:

from django.db import transaction

with transaction.atomic():
    contract = Contrato.objects.create(...)
    estimate = Estimate.objects.create(contract=contract, ...)

Métodos de conjunto de consultas:

class ContratoQuerySet(models.QuerySet):
    def for_company(self, company):
        return self.filter(company=company)

    def active(self):
        return self.filter(status='active')

Herramientas#

Pelusa:

# pylint
pylint construbot/

# flake8
flake8 construbot/

Formato:

# Black (code formatter)
black .

# isort (import sorting)
isort .

Comprobación de tipo (opcional):

mypy construbot/

Ganchos de compromiso previo#

Recomendado .pre-commit-config.yaml:

repos:
  - repo: https://github.com/psf/black
    rev: 22.10.0
    hooks:
      - id: black

  - repo: https://github.com/pycqa/isort
    rev: 5.10.1
    hooks:
      - id: isort

  - repo: https://github.com/pycqa/flake8
    rev: 5.0.4
    hooks:
      - id: flake8

Ver también#