Code Style#
Code style guidelines for Construbot contributions.
Python Style#
Follow PEP 8#
Code formatting:
4 spaces for indentation (no tabs)
Max line length: 100 characters (not strict 79)
2 blank lines between top-level functions/classes
1 blank line between methods
Use Black:
black .
Use isort:
isort .
Naming Conventions#
# 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
Type Hints#
Encouraged but not required:
def get_contracts(company: Company) -> QuerySet[Contrato]:
return Contrato.objects.filter(company=company)
Docstrings#
Use Google style:
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
"""
...
Django Style#
Model Best Practices#
Always include company:
class MyModel(models.Model):
company = models.ForeignKey(Company, on_delete=models.CASCADE)
Use verbose_name:
class Contrato(models.Model):
folio = models.CharField(
max_length=100,
verbose_name="Reference Number",
help_text="Unique identifier for this contract"
)
Meta options:
class Meta:
ordering = ['-created_at']
unique_together = ('company', 'folio')
verbose_name_plural = "Contracts"
View Best Practices#
Use class-based views:
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
)
Always scope to company:
# GOOD
contracts = Contrato.objects.filter(company=request.user.active_company)
# BAD
contracts = Contrato.objects.all() # Leaks data!
Query Optimization#
Use select_related:
# Avoid N+1 queries
contracts = Contrato.objects.select_related(
'contraparte',
'sitio'
)
Use prefetch_related:
users = User.objects.prefetch_related('companies')
Security#
Never trust user input:
# Use Django forms for validation
form = ContractForm(request.POST)
if form.is_valid():
contract = form.save()
Use get_object_or_404:
contract = get_object_or_404(Contrato, id=contract_id)
Check permissions:
if not request.user.nivel_acceso.nivel >= 3:
raise PermissionDenied()
Testing Style#
Clear test names:
def test_contract_creation_with_valid_data():
pass
def test_contract_creation_fails_with_duplicate_folio():
pass
Use setUp for common data:
class ContractTestCase(TestCase):
def setUp(self):
self.company = Company.objects.create(...)
def test_something(self):
contract = Contrato.objects.create(company=self.company)
One assertion per test (ideally):
def test_contract_folio_is_uppercase(self):
contract = Contrato.objects.create(folio="abc")
self.assertEqual(contract.folio, "ABC")
Git Commit Messages#
Format:
Short summary (50 chars max)
More detailed explanation if needed. Wrap at 72 characters.
- List of changes
- Another change
Good examples:
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.
Bad examples:
Fixed stuff
WIP
asdf
Updated code
Common Patterns#
Factory pattern for tests:
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)
Context manager for transactions:
from django.db import transaction
with transaction.atomic():
contract = Contrato.objects.create(...)
estimate = Estimate.objects.create(contract=contract, ...)
Queryset methods:
class ContratoQuerySet(models.QuerySet):
def for_company(self, company):
return self.filter(company=company)
def active(self):
return self.filter(status='active')
Tools#
Linting:
# pylint
pylint construbot/
# flake8
flake8 construbot/
Formatting:
# Black (code formatter)
black .
# isort (import sorting)
isort .
Type checking (optional):
mypy construbot/
Pre-commit Hooks#
Recommended .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
See Also#
Getting Started - Development setup
Testing - Testing guide