Sistema web completo desenvolvido em Django + SQLite para cadastro, publicação e candidatura em concursos públicos, com pagamento integrado via Mercado Pago (PIX e Boleto) e recuperação de senha por e-mail e WhatsApp.
- Visão Geral
- Screenshots
- Funcionalidades
- Tecnologias
- Estrutura do Projeto
- Requisitos
- Instalação
- Configuração do .env
- Rodando o Projeto
- Rodando com Docker
- Criando o Superusuário
- Painel Administrativo
- Rotas do Sistema
- Modelos de Dados
- Integrações Externas
- Deploy em Produção
- Perguntas Frequentes
Todas as telas foram capturadas nas versões Desktop e Mobile.
| 🖥️ Desktop | 📱 Mobile |
|---|---|
|
|
| 🖥️ Desktop | 📱 Mobile |
|---|---|
|
|
| 🖥️ Desktop | 📱 Mobile |
|---|---|
|
|
| 🖥️ Desktop | 📱 Mobile |
|---|---|
|
|
| 🖥️ Desktop | 📱 Mobile |
|---|---|
|
|
| 🖥️ Desktop | 📱 Mobile |
|---|---|
|
|
| 🖥️ Desktop | 📱 Mobile |
|---|---|
|
|
| 🖥️ Desktop |
|---|
|
| 🖥️ Desktop | 📱 Mobile |
|---|---|
|
|
| 🖥️ Desktop | 📱 Mobile |
|---|---|
|
|
| 🖥️ Desktop | 📱 Mobile |
|---|---|
|
|
| 🖥️ Desktop | 📱 Mobile |
|---|---|
|
|
| 🖥️ Desktop | 📱 Mobile |
|---|---|
|
|
| Login Admin | Admin Logado |
|---|---|
|
|
O ConcursosBR é uma plataforma que conecta candidatos a empresas/órgãos que publicam concursos públicos. O candidato cria seu perfil completo, navega pelos concursos abertos com filtros por estado e cidade, se candidata e paga a taxa de inscrição diretamente pela plataforma.
Candidato → Cadastro → Login → Busca Concursos → Se Candidata → Paga (PIX ou Boleto)
Empresa → Cadastro → Login → Cria Concurso → Publica Editais → Acompanha Inscrições
- Cadastro com foto, CPF, RG + órgão emissor, nome completo, nome da mãe e do pai, renda mensal, data de nascimento
- Até 2 telefones (celular e/ou fixo), cada um com indicação de WhatsApp
- Login por e-mail e senha
- Recuperação de senha via e-mail ou WhatsApp
- Busca e filtro de concursos por estado, cidade e palavra-chave
- Candidatura em concursos abertos com escolha de cargo
- Pagamento da taxa por PIX (QR Code + copia e cola) ou Boleto Bancário
- Acompanhamento de candidaturas com número de inscrição único
- Cadastro com razão social, CNPJ, responsável
- Dashboard com todos os concursos cadastrados
- Criação e edição de concursos com múltiplos cargos (vagas, salário, PCD)
- Publicação de editais em PDF com suporte a retificações
- Controle de status do concurso: Rascunho → Aberto → Encerrado / Suspenso
- Painel admin completo (
/admin/) com gestão de todos os modelos - Ações em lote: publicar e encerrar concursos
- Visualização de pagamentos e status de candidaturas
| Tecnologia | Versão | Uso |
|---|---|---|
| Python | 3.10+ | Linguagem base |
| Django | 6.x | Framework web |
| SQLite | — | Banco de dados |
| Bootstrap | 5.3 | Interface |
| Bootstrap Icons | 1.11 | Ícones |
| django-crispy-forms | 2.x | Formulários |
| crispy-bootstrap5 | — | Tema Bootstrap 5 |
| django-localflavor | 4.x | Validação BR |
| Pillow | 10.x | Upload de fotos |
| python-decouple | 3.x | Variáveis de ambiente |
| Mercado Pago SDK | 2.x | Pagamentos PIX/Boleto |
| Twilio | 9.x | WhatsApp API |
| Celery + Redis | 5.x / — | Tarefas assíncronas (opcional) |
sistema concurso/
│
├── manage.py # Ponto de entrada Django
├── requirements.txt # Dependências Python
├── .env # Variáveis de ambiente (não versionar)
├── .env.example # Modelo do .env
├── .gitignore
├── iniciar.bat # Script de inicialização (Windows)
│
├── core/ # Configuração central do projeto
│ ├── __init__.py
│ ├── celery.py # Configuração Celery
│ ├── urls.py # URLs raiz
│ ├── wsgi.py
│ └── settings/
│ ├── base.py # Configurações compartilhadas
│ ├── development.py # Desenvolvimento (DEBUG=True)
│ └── production.py # Produção (HTTPS, segurança)
│
├── apps/
│ ├── accounts/ # Autenticação e usuários
│ │ ├── models.py # Usuario, TokenRedefinicaoSenha
│ │ ├── views.py # Login, logout, reset de senha
│ │ ├── forms.py
│ │ ├── backends.py # Autenticação por e-mail
│ │ ├── urls.py
│ │ └── admin.py
│ │
│ ├── candidatos/ # Perfil do candidato
│ │ ├── models.py # Candidato, Telefone
│ │ ├── views.py # Cadastro, edição de perfil
│ │ ├── forms.py # Formulários + formset de telefones
│ │ ├── urls.py
│ │ └── admin.py
│ │
│ ├── empresas/ # Perfil da empresa
│ │ ├── models.py # Empresa
│ │ ├── views.py # Cadastro, dashboard
│ │ ├── forms.py
│ │ ├── urls.py
│ │ └── admin.py
│ │
│ ├── concursos/ # Concursos, cargos e editais
│ │ ├── models.py # Concurso, Cargo, Edital
│ │ ├── views.py # CRUD de concursos, upload de edital
│ │ ├── forms.py # Formulários + formset de cargos + filtro
│ │ ├── urls.py
│ │ └── admin.py
│ │
│ ├── candidaturas/ # Inscrições dos candidatos
│ │ ├── models.py # Candidatura
│ │ ├── views.py # Candidatar, listar candidaturas
│ │ ├── forms.py
│ │ ├── urls.py
│ │ └── admin.py
│ │
│ └── pagamentos/ # Pagamentos via Mercado Pago
│ ├── models.py # Pagamento
│ ├── views.py # Checkout, confirmação, webhook
│ ├── urls.py
│ └── admin.py
│
├── utils/
│ ├── validators.py # Validação de CPF e CNPJ
│ ├── whatsapp.py # Envio via Twilio WhatsApp
│ └── mercadopago_client.py # Criação de PIX e Boleto
│
├── templates/
│ ├── base.html # Layout base com navbar e footer
│ ├── 404.html / 500.html # Páginas de erro
│ ├── registration/ # Login
│ ├── accounts/ # Reset de senha
│ ├── candidatos/ # Cadastro e perfil
│ ├── empresas/ # Dashboard e formulário de concurso
│ ├── concursos/ # Lista e detalhe
│ ├── candidaturas/ # Confirmação e lista
│ └── pagamentos/ # Checkout PIX/Boleto e confirmação
│
├── static/
│ ├── css/style.css # Estilos customizados
│ └── js/main.js # Máscaras CPF/CNPJ/telefone, preview foto
│
└── media/
├── fotos_candidatos/ # Fotos de perfil
└── editais/ # PDFs dos editais
- Python 3.10 ou superior
- pip
- Windows, Linux ou macOS
Para funcionalidades opcionais:
- Redis (tarefas assíncronas com Celery)
- Conta Mercado Pago (pagamentos PIX e Boleto)
- Conta Twilio (reset de senha via WhatsApp)
- Conta de e-mail com SMTP (reset de senha por e-mail)
Se tiver o Git:
git clone <url-do-repositorio> sistema-concurso
cd sistema-concursoOu apenas extraia o ZIP na pasta desejada e abra o terminal nela.
Windows:
python -m venv .venv
.venv\Scripts\activateLinux / macOS:
python3 -m venv .venv
source .venv/bin/activateApós ativar, o terminal mostrará
(.venv)no início da linha.
pip install -r requirements.txtPara habilitar pagamentos (Mercado Pago):
pip install mercadopago==2.2.3Para habilitar WhatsApp (Twilio):
pip install twilio==9.3.2Para habilitar tarefas assíncronas (Celery):
pip install celery==5.4.0 redis==5.1.1Copie o arquivo de exemplo:
Windows:
copy .env.example .envLinux / macOS:
cp .env.example .envEdite o .env com suas configurações (veja a seção Configuração do .env).
python manage.py migratepython manage.py createsuperuserInforme e-mail e senha quando solicitado. Ou rode o comando abaixo substituindo os dados:
python manage.py shell -c "
from django.contrib.auth import get_user_model
User = get_user_model()
User.objects.create_superuser(email='[email protected]', password='suasenha')
print('Superusuário criado!')
"Windows (duplo clique ou terminal):
iniciar.batOu manualmente:
python manage.py runserverAcesse: http://localhost:8000
O arquivo .env fica na raiz do projeto e contém todas as variáveis sensíveis. Nunca versione este arquivo.
# ─── OBRIGATÓRIO ──────────────────────────────────────────────
SECRET_KEY=sua-chave-secreta-unica-aqui
DEBUG=True
ALLOWED_HOSTS=localhost,127.0.0.1
# ─── E-MAIL (para reset de senha por e-mail) ──────────────────
# Em desenvolvimento, deixe DEBUG=True e os e-mails aparecem no console.
# Para Gmail: ative "Senhas de App" em myaccount.google.com/security
EMAIL_HOST=smtp.gmail.com
EMAIL_PORT=587
EMAIL_USE_TLS=True
EMAIL_HOST_USER=[email protected]
EMAIL_HOST_PASSWORD=xxxx-xxxx-xxxx-xxxx # senha de app, NÃO sua senha normal
# ─── MERCADO PAGO (pagamentos PIX e Boleto) ───────────────────
# Obtenha em: https://www.mercadopago.com.br/developers/panel
MP_ACCESS_TOKEN=APP_USR-000...
MP_PUBLIC_KEY=APP_USR-000...
MP_WEBHOOK_SECRET=sua-chave-webhook
# ─── TWILIO (reset de senha via WhatsApp) ─────────────────────
# Obtenha em: https://www.twilio.com/console
TWILIO_ACCOUNT_SID=ACxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
TWILIO_AUTH_TOKEN=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
TWILIO_WHATSAPP_FROM=whatsapp:+14155238886
# ─── URL BASE DO SISTEMA ──────────────────────────────────────
# Em produção: https://seudominio.com.br
BASE_URL=http://localhost:8000
# ─── CELERY / REDIS (opcional) ────────────────────────────────
CELERY_BROKER_URL=redis://localhost:6379/0python -c "from django.core.management.utils import get_random_secret_key; print(get_random_secret_key())"# Ativar ambiente virtual
.venv\Scripts\activate # Windows
source .venv/bin/activate # Linux/macOS
# Iniciar servidor
python manage.py runserverServidor disponível em http://localhost:8000
python manage.py runserver 8080python manage.py checkpython manage.py makemigrations
python manage.py migrateO projeto inclui dois arquivos Docker Compose: um para desenvolvimento e outro para produção.
- Docker Desktop instalado e em execução.
# Clonar o repositório (se ainda não fez)
git clone https://github.com/brunnodev50/django-concursos-br.git
cd django-concursos-br
# Copiar o arquivo de variáveis de ambiente
copy .env.example .env # Windows
cp .env.example .env # Linux/macOS
# Subir os containers (web + redis)
docker-compose -f docker-compose.dev.yml upO servidor estará disponível em http://localhost:8000
Para rodar em segundo plano (detached):
docker-compose -f docker-compose.dev.yml up -d# Subir os containers em segundo plano (web + redis + celery)
docker-compose up -dO servidor estará disponível em http://localhost:8000
# Desenvolvimento
docker-compose -f docker-compose.dev.yml exec web python manage.py createsuperuser
# Produção
docker-compose exec web python manage.py createsuperuser# Desenvolvimento
docker-compose -f docker-compose.dev.yml exec web python manage.py migrate
# Produção
docker-compose exec web python manage.py migrate# Parar e remover containers
docker-compose down
# Parar sem remover (mantém dados)
docker-compose stopAs variáveis são lidas do arquivo .env na raiz do projeto. Certifique-se de configurar ao menos:
| Variável | Descrição |
|---|---|
SECRET_KEY |
Chave secreta do Django |
DEBUG |
True para dev, False para prod |
ALLOWED_HOSTS |
Domínios permitidos (ex: localhost,127.0.0.1) |
O superusuário tem acesso total ao painel administrativo.
python manage.py createsuperuserPreencha:
Email: [email protected]
Password: ********
Password (again): ********
python manage.py shell -c "
from django.contrib.auth import get_user_model
User = get_user_model()
if not User.objects.filter(email='[email protected]').exists():
User.objects.create_superuser(email='[email protected]', password='suasenha')
print('Criado!')
else:
print('Já existe.')
"Acesse http://localhost:8000/admin/ com as credenciais do superusuário.
| Seção | Ações |
|---|---|
| Usuários | Listar, criar, bloquear usuários |
| Candidatos | Ver perfis completos, telefones |
| Empresas | Gerenciar empresas cadastradas |
| Concursos | Publicar, encerrar, suspender concursos em lote |
| Cargos | Gerenciar cargos inline no concurso |
| Editais | Ver e baixar PDFs publicados |
| Candidaturas | Ver todas as inscrições e status |
| Pagamentos | Ver pagamentos, status, IDs do Mercado Pago |
| Tokens de Senha | Ver tokens de redefinição usados/expirados |
No admin de Concursos, selecione vários registros e use:
- Publicar concursos selecionados → status muda para "Aberto"
- Encerrar concursos selecionados → status muda para "Encerrado"
| Método | URL | Descrição |
|---|---|---|
| GET | / |
Redireciona para /concursos/ |
| GET | /concursos/ |
Lista de concursos abertos com filtros |
| GET | /concursos/<id>/ |
Detalhe do concurso |
| GET | /accounts/login/ |
Login |
| GET | /accounts/redefinir-senha/solicitar/ |
Solicitar reset de senha |
| GET | /accounts/redefinir-senha/<token>/ |
Formulário de nova senha |
| GET | /candidatos/cadastro/ |
Cadastro de candidato |
| GET | /empresas/cadastro/ |
Cadastro de empresa |
| Método | URL | Descrição |
|---|---|---|
| GET/POST | /candidatos/perfil/ |
Editar perfil |
| GET/POST | /candidaturas/concurso/<id>/candidatar/ |
Se inscrever em um concurso |
| GET | /candidaturas/minhas/ |
Listar minhas candidaturas |
| GET/POST | /pagamentos/checkout/<id>/ |
Pagamento da taxa |
| GET | /pagamentos/confirmacao/<id>/ |
Confirmação PIX / Boleto |
| Método | URL | Descrição |
|---|---|---|
| GET | /empresas/dashboard/ |
Dashboard da empresa |
| GET/POST | /concursos/criar/ |
Criar novo concurso |
| GET/POST | /concursos/<id>/editar/ |
Editar concurso |
| GET/POST | /concursos/<id>/edital/ |
Publicar edital PDF |
| Método | URL | Descrição |
|---|---|---|
| POST | /pagamentos/webhook/mercadopago/ |
Notificação de pagamento (Mercado Pago) |
| Campo | Tipo | Descrição |
|---|---|---|
| id | UUID | Chave primária |
| EmailField | Login único | |
| tipo | CharField | candidato, empresa ou admin |
| is_active | BooleanField | Conta ativa |
| criado_em | DateTimeField | Data de cadastro |
| Campo | Tipo | Descrição |
|---|---|---|
| usuario | OneToOne → Usuario | Vínculo com a conta |
| foto | ImageField | Foto de perfil |
| nome_completo | CharField | Nome completo |
| cpf | CharField | CPF (validado) |
| rg | CharField | Número do RG |
| orgao_emissor | CharField | Ex: SSP/SP |
| nome_mae | CharField | Nome da mãe |
| nome_pai | CharField | Nome do pai |
| renda_mensal | DecimalField | Renda em R$ |
| data_nascimento | DateField | Data de nascimento |
| Campo | Tipo | Descrição |
|---|---|---|
| candidato | FK → Candidato | Vínculo |
| numero | CharField | Número com máscara |
| tipo | CharField | celular ou fixo |
| tem_whatsapp | BooleanField | Possui WhatsApp? |
| Campo | Tipo | Descrição |
|---|---|---|
| usuario | OneToOne → Usuario | Vínculo |
| razao_social | CharField | Razão social |
| nome_fantasia | CharField | Nome fantasia |
| cnpj | CharField | CNPJ (validado) |
| responsavel | CharField | Nome do responsável |
| telefone | CharField | Telefone de contato |
| Campo | Tipo | Descrição |
|---|---|---|
| empresa | FK → Empresa | Empresa responsável |
| titulo | CharField | Título do concurso |
| orgao | CharField | Órgão realizador |
| estado | CharField | UF (sigla) |
| cidade | CharField | Cidade |
| vagas_total | IntegerField | Total de vagas |
| taxa_inscricao | DecimalField | Valor da taxa em R$ |
| data_abertura | DateField | Início das inscrições |
| data_fechamento | DateField | Fim das inscrições |
| data_prova | DateField | Data da prova |
| status | CharField | rascunho, aberto, encerrado, suspenso |
| Campo | Tipo | Descrição |
|---|---|---|
| concurso | FK → Concurso | Vínculo |
| nome | CharField | Nome do cargo |
| nivel_escolaridade | CharField | Nível exigido |
| salario | DecimalField | Salário em R$ |
| vagas | IntegerField | Número de vagas |
| vagas_pcd | IntegerField | Vagas para PCD |
| Campo | Tipo | Descrição |
|---|---|---|
| concurso | FK → Concurso | Vínculo |
| titulo | CharField | Título do edital |
| arquivo | FileField | PDF do edital |
| data_publicacao | DateField | Data da publicação |
| retificacao | BooleanField | É retificação? |
| Campo | Tipo | Descrição |
|---|---|---|
| candidato | FK → Candidato | Candidato inscrito |
| concurso | FK → Concurso | Concurso |
| cargo | FK → Cargo | Cargo escolhido |
| status | CharField | pendente, aguardando_pagamento, paga, cancelada |
| numero_inscricao | CharField | Número único gerado automaticamente |
| Campo | Tipo | Descrição |
|---|---|---|
| candidatura | OneToOne → Candidatura | Vínculo |
| metodo | CharField | pix ou boleto |
| valor | DecimalField | Valor pago |
| status | CharField | pendente, pago, cancelado, expirado |
| mp_payment_id | CharField | ID do Mercado Pago |
| pix_qr_code | TextField | QR Code em base64 |
| pix_qr_code_texto | TextField | Código Pix copia e cola |
| boleto_url | URLField | Link do boleto PDF |
| boleto_barcode | CharField | Código de barras |
| pago_em | DateTimeField | Data/hora do pagamento |
- Crie uma conta em mercadopago.com.br/developers
- Acesse Suas integrações → Credenciais
- Copie o Access Token e Public Key de produção
- Cole no
.env - Configure o Webhook no painel do Mercado Pago:
- URL:
https://seudominio.com.br/pagamentos/webhook/mercadopago/ - Evento:
payment
- URL:
- Copie a chave secreta do webhook para
MP_WEBHOOK_SECRETno.env
Em desenvolvimento, use as credenciais de teste (Sandbox) para não cobrar de verdade.
- Crie uma conta em twilio.com
- Acesse o Console e copie Account SID e Auth Token
- Ative o WhatsApp Sandbox (gratuito para testes)
- Cole as credenciais no
.env - O candidato precisa ter um telefone com WhatsApp cadastrado no perfil
Para usar o Gmail:
- Acesse myaccount.google.com/security
- Ative Verificação em duas etapas
- Vá em Senhas de App e gere uma senha para "Outro (nome personalizado)"
- Use essa senha (16 caracteres) em
EMAIL_HOST_PASSWORDno.env
Em desenvolvimento (
DEBUG=True), os e-mails são exibidos no console do terminal automaticamente, sem precisar configurar SMTP.
DEBUG=False
SECRET_KEY=chave-muito-longa-e-aleatoria
ALLOWED_HOSTS=seudominio.com.br,www.seudominio.com.br
BASE_URL=https://seudominio.com.brexport DJANGO_SETTINGS_MODULE=core.settings.productionpython manage.py collectstatic --noinputpip install gunicorn
gunicorn core.wsgi:application --bind 0.0.0.0:8000 --workers 3server {
listen 80;
server_name seudominio.com.br;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
server_name seudominio.com.br;
ssl_certificate /etc/letsencrypt/live/seudominio.com.br/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/seudominio.com.br/privkey.pem;
location /static/ {
alias /caminho/para/staticfiles/;
}
location /media/ {
alias /caminho/para/media/;
}
location / {
proxy_pass http://127.0.0.1:8000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}Para produção, migre de SQLite para PostgreSQL:
pip install psycopg2-binaryEm core/settings/production.py:
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql',
'NAME': 'concursosdb',
'USER': 'usuario',
'PASSWORD': 'senha',
'HOST': 'localhost',
'PORT': '5432',
}
}O sistema funciona sem Mercado Pago configurado? Sim. Os imports são carregados apenas quando o pagamento é acionado. O restante do sistema funciona normalmente.
O sistema funciona sem Twilio configurado? Sim. O reset por WhatsApp exibirá erro apenas se o candidato escolher essa opção sem as credenciais configuradas. O reset por e-mail continuará funcionando.
Como mudar a porta do servidor?
python manage.py runserver 8080Como redefinir o banco de dados do zero?
# Apagar o banco
del db.sqlite3 # Windows
rm db.sqlite3 # Linux/macOS
# Recriar
python manage.py migrate
python manage.py createsuperuserComo habilitar o Celery para tarefas em segundo plano?
# Em um terminal separado (Redis deve estar rodando)
celery -A core worker -l infoOs arquivos de mídia (fotos, editais) são servidos em produção?
Não pelo Django. Configure o Nginx para servir /media/ diretamente (veja a configuração de Nginx acima).
Este projeto foi desenvolvido para uso interno/educacional. Adapte conforme necessário.
Desenvolvido com Django — Python Web Framework








































