- Python 3 ou superior - Conferir a versão:
python --version
- PIP - Conferir a versão:
pip --version
- Pasta de Trabalho - Criar Pasta:
mkdir master-week
cd master-week
mkdir django-do-zero
cd django-do-zero
- Virtual Environment - Criar Ambiente Virtual:
python -m venv venv
- Virtual Environment - Ativar Ambiente Virtual:
source venv/bin/activate
- PIP - Conferir a versão:
pip --version
- Django - Instalar o Django em Ambiente Virtual (Recomendado):
pip install django
- Django 5 ou superior - Conferir a versão:
django-admin --version
Criar o projeto com Django.
django-admin startproject admin .
- Alterar Linguagem e TimeZone do Django para Português do Brasil
code admin/settings.py
# Internationalization
# https://docs.djangoproject.com/en/5.1/topics/i18n/
# LANGUAGE_CODE = 'en-us'
LANGUAGE_CODE = 'pt-br'
# TIME_ZONE = 'UTC'
TIME_ZONE = 'America/Sao_Paulo'
USE_I18N = True
USE_TZ = True
Executar as migrations para criar as tabelas.
python manage.py migrate
Criar o super usuário.
python manage.py createsuperuser
Username (leave blank to use 'cesar'): admin
Email address: [email protected]
Password: 123456A#
Password (again): 123456A#
Rodar o projeto.
python manage.py runserver
Acessar a página padrão criada com Django.
http://127.0.0.1:8000
Acessar o sistema administrativo padrão do Django.
http://127.0.0.1:8000/admin
Usuário: admin
Senha: 123456A#
pip freeze > requirements.txt
pip install -r requirements.txt
- Virtual Environment - Desativar Ambiente Virtual:
deactivate
- MySQL 8 ou superior - Conferir a versão:
mysql --version
- MySQLClient - Instalar o Cliente MySQL:
pip install mysqlclient
Alterar no arquivo "settings.py" as credenciais do banco de dados.
# DATABASES = {
# 'default': {
# 'ENGINE': 'django.db.backends.sqlite3',
# 'NAME': BASE_DIR / 'db.sqlite3',
# }
# }
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'HOST': '127.0.0.1',
'NAME': 'nome-do-banco-de-dados',
'USER': 'usuario-do-banco-de-dados',
'PASSWORD': 'senha-do-usuario-do-banco-de-dados',
'PORT': '3306',
}
}
Executar as migrations para criar as tabelas.
python manage.py migrate
Rodar o projeto.
python manage.py runserver
Acessar a página padrão criada com Django.
http://127.0.0.1:8000
Criar o super usuário.
python manage.py createsuperuser
Username (leave blank to use 'cesar'): admin
Email address: [email protected]
Password: 123456A#
Password (again): 123456A#
Acessar o sistema administrativo padrão do Django.
http://127.0.0.1:8000/admin
Usuário: admin
Senha: 123456A#
Instalar o Django.
pip install Django
Criar o projeto com Django.
django-admin startproject admin .
Rodar o projeto.
python manage.py runserver
Acessar a página padrão criada com Django.
http://127.0.0.1:8000
Instalar o conector MySQL.
pip install mysqlclient
Criar a base de dados.
CREATE DATABASE celke CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
GRANT ALL PRIVILEGES ON `celke`.* TO 'celke'@'%'; ALTER USER 'celke'@'%' ;
Alterar no arquivo "settings.py" as credenciais do banco de dados.
code admin/settings/py
# DATABASES = {
# 'default': {
# 'ENGINE': 'django.db.backends.sqlite3',
# 'NAME': BASE_DIR / 'db.sqlite3',
# }
# }
'ENGINE': 'django.db.backends.mysql',
'HOST': '127.0.0.1',
'PORT': '3306'
'NAME': 'celke',
'USER': 'celke',
'PASSWORD': '123456A#',
Executar as migrations para criar as tabelas.
python manage.py migrate
Criar o super usuário.
python manage.py createsuperuser
Username (leave blank to use 'cesar'): admin
Email address: [email protected]
Password: 123456A#
Password (again): 123456A#
Acessar o sistema administrativo padrão do Django.
http://127.0.0.1:8000/admin
Inicializar um novo repositorio GIT.
git init
Adicionar todos os arquivos modificados na área de preparação.
git add .
Commit registra alterações nos arquivos adicionados na área de preparação.
git commit -m "Base do projeto"
Verificar em qual branch está.
git branch
Renomear a branch atual no GIT para main.
git branch -M main
Adicionar um repositório remoto ao repositório local.
git remote add origin https://github.com/celkecursos/master-week-python-e-django.git
Enviar os commits locais para um repositório remoto.
git push -u origin main
Remover o arquivo do cache do GIT.
git rm --cached db.sqlite3
Remover o diretório do cache do GIT e venv.
git rm --cached -r admin/__pycache__/
git rm --cached -r venv/
Criar Novo App
python manage.py startapp travelpackages
Incluir App no arquivo admin/settings.py
code admin/settings.py
# Application definition
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'travelpackages',
]
Instalar Biblioteca para Trabalhar com ImageField
pip install pillow
Editar arquivo travelpackages/models.py
code travelpackages/models.py
from django.db import models
import os
# Create your models here.
class TravelPackage(models.Model):
name = models.CharField(max_length=255, verbose_name="Nome")
original_price = models.DecimalField(max_digits=10, decimal_places=2, verbose_name="Preço Original")
discounted_price = models.DecimalField(max_digits=10, decimal_places=2, verbose_name="Preço com Desconto")
image = models.ImageField(upload_to="travelpackages", verbose_name="Imagem")
created_at = models.DateTimeField(auto_now_add=True, verbose_name="Criado em")
updated_at = models.DateTimeField(auto_now=True, verbose_name="Editado em")
# Sobrescrever o Método save no Modelo
def save(self, *args, **kwargs):
# Verificar se o objeto já existe no banco de dados
if self.pk:
# Buscar o objeto atual no banco de dados
old_image = TravelPackage.objects.get(pk=self.pk).image
# Comparar se a imagem foi alterada
if old_image and old_image != self.image:
# Remover a imagem antiga do sistema de arquivos
if os.path.isfile(old_image.path):
os.remove(old_image.path)
# Salvar novo registro
super().save(*args, **kwargs)
# Sobrescrever o Método delete do Django para Remover a Imagem
def delete(self, *args, **kwargs):
# Remover a imagem associada ao objeto
if self.image and os.path.isfile(self.image.path):
os.remove(self.image.path)
# Excluir o registro
super().delete(*args, **kwargs)
def __str__(self):
return self.name
class Meta:
db_table = "travelpackages"
verbose_name = "pacote de viagem"
verbose_name_plural = "pacotes de viagens"
Editar arquivo admin/settings.py
code admin/settings.py
from pathlib import Path
import os
...
# Diretório base para arquivos
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
Criar Migrações do Aplicativo
python manage.py makemigrations travelpackages --name travelpackages
Migrations for 'travelpackages':
travelpackages/migrations/0001_travelpackages.py
+ Create model TravelPackage
Executar as migrations para criar as tabelas.
python manage.py migrate
Operations to perform:
Apply all migrations: admin, auth, contenttypes, sessions, travelpackages
Running migrations:
Applying travelpackages.0001_travelpackages... OK
Registrar o models do App TravelPackage
code travelpackages/admin.py
from django.contrib import admin
from .models import TravelPackage
@admin.register(TravelPackage)
class TravelPackageAdmin(admin.ModelAdmin):
list_display = ('id', 'name', 'original_price', 'discounted_price')
Registrar o models do App TravelPackage
code travelpackages/apps.py
from django.apps import AppConfig
class TravelpackagesConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'travelpackages'
verbose_name = 'pacote de viagem'
Alterar Django para acessar view home
mkdir travelpackages/templates
mkdir travelpackages/templates/travelpackages
code travelpackages/templates/travelpackages/home.html
<!DOCTYPE html>
<html lang="pt-BR">
<head>
<title>Celke</title>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
</head>
<body>
<h3>Home</h3>
</body>
</html>
code admin/urls.py
"""
URL configuration for admin project.
The `urlpatterns` list routes URLs to views.
For more information please see:
https://docs.djangoproject.com/en/5.1/topics/http/urls/
Examples:
Function views
1. Add an import:
from my_app import views
2. Add a URL to urlpatterns:
path('', views.home, name='home')
Class-based views
1. Add an import:
from other_app.views import Home
2. Add a URL to urlpatterns:
path('', Home.as_view(), name='home')
Including another URLconf
1. Import the include() function:
from django.urls import include, path
2. Add a URL to urlpatterns:
path('blog/', include('blog.urls'))
"""
from django.contrib import admin
from django.urls import path, include
from django.conf import settings
from django.conf.urls.static import static
urlpatterns = [
path('admin/', admin.site.urls),
path('', include('travelpackages.urls')),
]
# Adicionar suporte para servir arquivos de mídia durante desenvolvimento
if settings.DEBUG:
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
code travelpackages/urls.py
from django.urls import path
from . import views
urlpatterns = [
path('', views.home, name='home')
]
code travelpackages/views.py
from django.shortcuts import render
from .models import TravelPackage
# Create your views here.
def home(request):
packages = TravelPackage.objects.all()
return render(request, 'travelpackages/home.html', {'packages': packages})
Instalar FrameWork CSS Bootstrap diretamente no projeto Django
pip install django-bootstrap-v5
Installing collected packages: soupsieve, django, beautifulsoup4, django-bootstrap-v5
Attempting uninstall: django
Found existing installation: Django 5.1.4
Uninstalling Django-5.1.4:
Successfully uninstalled Django-5.1.4
Successfully installed beautifulsoup4-4.12.3 django-4.2.17 django-bootstrap-v5-1.0.11 soupsieve-2.6
code admin/settings.py
# Application definition
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'travelpackages',
'bootstrap5',
]
code travelpackages/templates/travelpackages/base.html
{# load the tag library #}
{% load bootstrap5 %}
<!DOCTYPE html>
<html lang="pt-BR">
<head>
<title>Celke</title>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
{# load Bootstrap CSS #}
{% bootstrap_css %}
</head>
<body>
{% block content %}
{% endblock content %}
{# load Bootstrap JS #}
{% bootstrap_javascript %}
</body>
</html>
code travelpackages/templates/travelpackages/home.html
{% extends "travelpackages/base.html" %}
{% block content %}
<div class="container mt-5">
<h4>Pacotes de Viagens</h4>
</div>
{% endblock content %}
Criar/Editar arquivos Templates para carregar/utilizar FrameWork CSS Bootstrap
code travelpackages/templates/travelpackages/base.html
{# load the tag library #}
{% load bootstrap5 %}
<!DOCTYPE html>
<html lang="pt-BR">
<head>
<title>Celke</title>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
{# load Bootstrap CSS #}
{% bootstrap_css %}
</head>
<body>
{% block content %}
{% endblock content %}
{# load Bootstrap JS #}
{% bootstrap_javascript %}
</body>
</html>
code travelpackages/templates/travelpackages/home.html
{% extends "travelpackages/base.html" %}
{% block content %}
<div class="container mt-5">
<h4>Pacotes de Viagens</h4>
<div class="row">
{% for package in packages %}
<div class="col-md-4 mb-4">
<div class="card mb-4 shadow">
{% if package.image %}
<img
src="{{ package.image.url }}"
alt="{{ package.name }}"
class="card-img-top"
/>
{% endif %}
</div>
</div>
{% endfor %}
</div>
</div>
{% endblock content %}
Criar Página Black Friday e Refatorar Templates
code travelpackages/urls.py
from django.urls import path
from . import views
urlpatterns = [
path('', views.home, name='home'),
path('black-friday', views.black_friday, name='black_friday'),
]
code travelpackages/views.py
from django.shortcuts import render
from .models import TravelPackage
# Create your views here.
def home(request):
packages = TravelPackage.objects.all().order_by('-id')
return render(request, 'travelpackages/home.html', {'packages': packages})
def black_friday(request):
packages = TravelPackage.objects.all().order_by('discounted_price')
return render(request, 'travelpackages/black_friday.html', {'packages': packages})
code travelpackages/templates/travelpackages/black_friday.html
{% extends "travelpackages/base.html" %}
{% block content %}
<div class="container mt-5">
<h4>Pacotes de Viagens</h4>
<div class="row">
{% for package in packages %}
<div class="col-md-4 mb-4">
<div class="card mb-4 shadow">
{% if package.image %}
<img
src="{{ package.image.url }}"
alt="{{ package.name }}"
class="card-img-top"
/>
<div class="card-body text-center">
<h5 class="card-title">{{ package.name}}</h5>
<p class="card-text">
<span class="text-muted"><del><small>
R$ {{ package.original_price }}
</small></del></span>
<span class="text-success">
R$ {{ package.discounted_price }}
</span>
</p>
<a href="#" class="btn btn-primary btn-sm">
Mais Detalhes
</a>
</div>
{% endif %}
</div>
</div>
{% endfor %}
</div>
</div>
{% endblock content %}
code travelpackages/templates/travelpackages/base.html
{# load the tag library #}
{% load bootstrap5 %}
<!DOCTYPE html>
<html lang="pt-BR" class="h-100">
<head>
<title>Celke</title>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
{# load Bootstrap CSS #}
{% bootstrap_css %}
</head>
<body class="d-flex flex-column h-100">
<nav class="navbar navbar-expand-lg navbar-dark bg-primary">
<div class="container">
<a class="navbar-brand" href="#">
Celke
</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarSupportedContent">
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
<li class="nav-item">
<a class="nav-link active" href="{% url 'home' %}">
Home
</a>
</li>
<li class="nav-item">
<a class="nav-link active" href="{% url 'black_friday' %}">
Black Friday
</a>
</li>
</ul>
<div class="col-md-3 text-end">
<a
target="_blank"
href="{% url 'admin:index' %}"
class="btn btn-outline-light me-2"
>
LogIn
</a>
</div>
</div>
</div>
</nav>
{% block content %}
{% endblock content %}
<footer class="footer mt-auto py-3 bg-light">
<div class="container text-center">
<span class="text-muted">
Copyright © Celke {% now "Y" %}
</span>
</div>
</footer>
{# load Bootstrap JS #}
{% bootstrap_javascript %}
</body>
</html>
code travelpackages/templates/travelpackages/home.html
{% extends "travelpackages/base.html" %}
{% block content %}
<div class="container mt-5">
<h4>Pacotes de Viagens</h4>
<div class="row">
{% for package in packages %}
<div class="col-md-4 mb-4">
<div class="card mb-4 shadow">
{% if package.image %}
<img
src="{{ package.image.url }}"
alt="{{ package.name }}"
class="card-img-top"
/>
<div class="card-body text-center">
<h5 class="card-title">{{ package.name}}</h5>
<p class="card-text">
<span class="text-muted"><del><small>
R$ {{ package.original_price }}
</small></del></span>
<span class="text-success">
R$ {{ package.discounted_price }}
</span>
</p>
<a href="#" class="btn btn-primary btn-sm">
Mais Detalhes
</a>
</div>
{% endif %}
</div>
</div>
{% endfor %}
</div>
</div>
{% endblock content %}
Incluir no Banco de Dados a coluna descrição (descrition)
code travelpackages/models.py
from django.db import models
import os
# Create your models here.
class TravelPackage(models.Model):
name = models.CharField(max_length=255, verbose_name="Nome")
original_price = models.DecimalField(max_digits=10, decimal_places=2, verbose_name="Preço Original")
discounted_price = models.DecimalField(max_digits=10, decimal_places=2, verbose_name="Preço com Desconto")
image = models.ImageField(upload_to="travelpackages", verbose_name="Imagem")
description = models.TextField(verbose_name="Descrição", blank=True, null=True)
created_at = models.DateTimeField(auto_now_add=True, verbose_name="Criado em")
updated_at = models.DateTimeField(auto_now=True, verbose_name="Editado em")
# Sobrescrever o Método save no Modelo
def save(self, *args, **kwargs):
# Verificar se o objeto já existe no banco de dados
if self.pk:
# Buscar o objeto atual no banco de dados
old_image = TravelPackage.objects.get(pk=self.pk).image
# Comparar se a imagem foi alterada
if old_image and old_image != self.image:
# Remover a imagem antiga do sistema de arquivos
if os.path.isfile(old_image.path):
os.remove(old_image.path)
# Salvar novo registro
super().save(*args, **kwargs)
# Sobrescrever o Método delete do Django para Remover a Imagem
def delete(self, *args, **kwargs):
# Remover a imagem associada ao objeto
if self.image and os.path.isfile(self.image.path):
os.remove(self.image.path)
# Excluir o registro
super().delete(*args, **kwargs)
def __str__(self):
return self.name
class Meta:
db_table = "travelpackages"
verbose_name = "pacote de viagem"
verbose_name_plural = "pacotes de viagens"
Criar Migrações com Alteração no Banco de Dados e Executar Migrações
python manage.py makemigrations
Migrations for 'travelpackages':
travelpackages/migrations/0002_alter_travelpackage_options_and_more.py
- Change Meta options on travelpackage
- Add field description to travelpackage
python manage.py migrate
Operations to perform:
Apply all migrations: admin, auth, contenttypes, sessions, travelpackages
Running migrations:
Applying travelpackages.0002_alter_travelpackage_options_and_more... OK
Instalar no projeto Django um Editor WYSIWYG (What You See Is What You Get)
pip install django-tinymce
Incluir no projeto Django o Editor TinyMCE
code admin/settings.py
# Application definition
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'travelpackages',
'bootstrap5',
'tinymce',
]
code travelpackages/admin.py
from django.contrib import admin
from tinymce.widgets import TinyMCE
from django.db import models
from .models import TravelPackage
# Register your models here.
@admin.register(TravelPackage)
class TravelPackageAdmin(admin.ModelAdmin):
list_display = ('id', 'name', 'original_price', 'discounted_price')
formfield_overrides = {
models.TextField: {'widget': TinyMCE()}
}
Criar página detalhe do pacote
code travelpackages/views.py
from django.shortcuts import render, get_object_or_404
from .models import TravelPackage
# Create your views here.
def home(request):
packages = TravelPackage.objects.all().order_by('-id')
return render(request, 'travelpackages/home.html', {'packages': packages})
def black_friday(request):
packages = TravelPackage.objects.all().order_by('discounted_price')
return render(request, 'travelpackages/black_friday.html', {'packages': packages})
def package_detail(request, id):
package = get_object_or_404(TravelPackage, id=id)
return render(request, 'travelpackages/package_detail.html', {'package': package})
code travelpackages/urls.py
from django.urls import path
from . import views
urlpatterns = [
path('', views.home, name='home'),
path('black-friday', views.black_friday, name='black_friday'),
path('package/<int:id>/', views.package_detail, name='package_detail'),
]
code travelpackages/templates/base.html
{# load the tag library #}
{% load bootstrap5 %}
<!DOCTYPE html>
<html lang="pt-BR" class="h-100">
<head>
<title>Celke</title>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
{# load Bootstrap CSS #}
{% bootstrap_css %}
<style>
.list-travel-package {
display: flex;
flex-direction: row;
align-items: center;
margin-bottom: 10px;
}
.title-list-travel-package {
color: #324688;
font-optical-sizing: 1.5rem;
font-weight: bold;
}
.title-bar-list-travel-package {
margin-right: 10px;
width: 5px;
height: 45px;
background-color: #0661a7;
}
</style>
</head>
<body class="d-flex flex-column h-100">
<nav class="navbar navbar-expand-lg navbar-dark bg-primary">
<div class="container">
<a class="navbar-brand" href="#">
Celke
</a>
<button
class="navbar-toggler"
type="button"
data-bs-toggle="collapse"
data-bs-target="#navbarSupportedContent"
aria-controls="navbarSupportedContent"
aria-expanded="false"
aria-label="Toggle navigation"
>
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarSupportedContent">
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
<li class="nav-item">
<a class="nav-link active" href="{% url 'home' %}">
Home
</a>
</li>
<li class="nav-item">
<a class="nav-link active" href="{% url 'black_friday' %}">
Black Friday
</a>
</li>
</ul>
<div class="col-md-3 text-end">
<a
target="_blank"
href="{% url 'admin:index' %}"
class="btn btn-outline-light me-2"
>
LogIn
</a>
</div>
</div>
</div>
</nav>
{% block content %}
{% endblock content %}
<footer class="footer mt-auto py-3 bg-light">
<div class="container text-center">
<span class="text-muted">
Copyright © Celke {% now "Y" %}
</span>
</div>
</footer>
{# load Bootstrap JS #}
{% bootstrap_javascript %}
</body>
</html>
code travelpackages/templates/home.html
{% extends "travelpackages/base.html" %}
{% block content %}
<div class="container mt-5">
<div class="mb-2 pt-1 pb-4 list-travel-package">
<div class="title-bar-list-travel-package"></div>
<h4 class="title-list-travel-package">Pacotes de Viagens</h4>
</div>
<div class="row">
{% for package in packages %}
<div class="col-md-4 mb-4">
<div class="card mb-4 border-light shadow">
{% if package.image %}
<img
src="{{ package.image.url }}"
alt="{{ package.name }}"
class="card-img-top"
/>
<div class="card-body text-center">
<h5 class="card-title">{{ package.name}}</h5>
<p class="card-text">
<span class="text-muted"><del><small>
R$ {{ package.original_price }}
</small></del></span>
<span class="text-success">
R$ {{ package.discounted_price }}
</span>
</p>
<a
href="{% url 'package_detail' id=package.id %}"
class="btn btn-primary btn-sm"
>
Mais Detalhes
</a>
</div>
{% endif %}
</div>
</div>
{% endfor %}
</div>
</div>
{% endblock content %}
code travelpackages/templates/black_friday.html
{% extends "travelpackages/base.html" %}
{% block content %}
<div class="container mt-5">
<div class="mb-2 pt-1 pb-4 list-travel-package">
<div class="title-bar-list-travel-package"></div>
<h4 class="title-list-travel-package">Black Friday Prorrogada</h4>
</div>
<div class="row">
{% for package in packages %}
<div class="col-md-4 mb-4">
<div class="card mb-4 border-light shadow">
{% if package.image %}
<img
src="{{ package.image.url }}"
alt="{{ package.name }}"
class="card-img-top"
/>
<div class="card-body text-center">
<h5 class="card-title">{{ package.name}}</h5>
<p class="card-text">
<span class="text-muted"><del><small>
R$ {{ package.original_price }}
</small></del></span>
<span class="text-success">
R$ {{ package.discounted_price }}
</span>
</p>
<a
href="{% url 'package_detail' id=package.id %}"
class="btn btn-primary btn-sm"
>
Mais Detalhes
</a>
</div>
{% endif %}
</div>
</div>
{% endfor %}
</div>
</div>
{% endblock content %}
code travelpackages/templates/package-detail.html
{% extends "travelpackages/base.html" %}
{% block content %}
<div class="container mt-5">
<div class="mb-2 pt-1 pb-4 list-travel-package">
<div class="title-bar-list-travel-package"></div>
<h4 class="title-list-travel-package">Black Friday Prorrogada</h4>
</div>
<div class="row">
{% for package in packages %}
<div class="col-md-4 mb-4">
<div class="card mb-4 border-light shadow">
{% if package.image %}
<img
src="{{ package.image.url }}"
alt="{{ package.name }}"
class="card-img-top"
/>
<div class="card-body text-center">
<h5 class="card-title">{{ package.name}}</h5>
<p class="card-text">
<span class="text-muted"><del><small>
R$ {{ package.original_price }}
</small></del></span>
<span class="text-success">
R$ {{ package.discounted_price }}
</span>
</p>
<a
href="{% url 'package_detail' id=package.id %}"
class="btn btn-primary btn-sm"
>
Mais Detalhes
</a>
</div>
{% endif %}
</div>
</div>
{% endfor %}
</div>
</div>
{% endblock content %}