Desenvolvimento Web com Django

Regis do Python

slides.com/regissantos/desenvolvimento-web-com-django

Criando uma página simples

cat << EOF > index.html
<h1 style="font-size: 180px;">Minha landpage</h1>
EOF
python -m http.server

Frameworks de CSS

  • Bootstrap
  • TailwindCSS
  • Bulma
  • PicoCSS

Uma página com framework de CSS

<!DOCTYPE html>
<html lang="en" data-theme="light">
<head>
  <meta charset="utf-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no">
  <link rel="shortcut icon" href="https://www.djangoproject.com/favicon.ico">
  <title>Landpage</title>

  <!-- PicoCSS -->
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@picocss/pico@2/css/pico.min.css" />
</head>
<body>
  <h1 style="font-size: 180px;">Minha landpage</h1>
</body>
</html>

Banco de dados

SQLite

import sqlite3

# Conectar ao banco
conexao = sqlite3.connect('loja.db')

# Criar um cursor para executar comandos SQL
cursor = conexao.cursor()

# Criar tabela de produtos
cursor.execute('''
    CREATE TABLE IF NOT EXISTS produtos (
        id INTEGER PRIMARY KEY,
        titulo TEXT NOT NULL,
        preco REAL NOT NULL
    )
''')

... continuação

# Inserir alguns produtos
produtos = [
    ("Notebook", 2500.99),
    ("Mouse wireless", 32.90),
    ("Monitor 24 pol", 650.00)
]

cursor.executemany(
    "INSERT INTO produtos (titulo, preco) VALUES (?, ?)", produtos
)

... continuação

# Buscar todos os produtos
cursor.execute("SELECT * FROM produtos")
resultados = cursor.fetchall()

print("Produtos cadastrados:")
for produto in resultados:
    print(f"ID: {produto[0]} | Título: {produto[1]} | Preço: R$ {produto[2]:.2f}")

# Confirmar as alterações e fechar conexão
conexao.commit()
conexao.close()

PostgreSQL

pip install psycopg2-binary
import psycopg2

try:
    # Conectar ao banco PostgreSQL
    conexao = psycopg2.connect(
        host="localhost",
        database="loja",
        user="meu_usuario",
        password="minha_senha",
        port="5432"
    )
    # resto é igual...

except psycopg2.Error as e:
    print(f"Erro ao conectar ao PostgreSQL: {e}")

finally:
    if conexao:
        cursor.close()
        conexao.close()

MySQL

import mysql.connector

try:
    # Conectar ao banco MySQL
    conexao = mysql.connector.connect(
        host="localhost",
        database="loja",
        user="meu_usuario",
        password="minha_senha",
        port="3306"
    )

    cursor = conexao.cursor()

    # ...

except mysql.connector.Error as e:
    print(f"Erro ao conectar ao MySQL: {e}")

finally:
    if conexao:
        cursor.close()
        conexao.close()

Diferenças

  • Biblioteca: mysql.connector ao invés de psycopg2
  • Porta padrão: 3306 (MySQL) vs 5432 (PostgreSQL)
  • Exceção: mysql.connector.Error vs psycopg2.Error

Criando o projeto

django-admin startproject apps .

cd apps/

python manage.py startapp core

python ../manage.py startapp core
python ../manage.py startapp produto

cd ..

Settings

from pathlib import Path
from decouple import config

SECRET_KEY = config('SECRET_KEY')


INSTALLED_APPS = [
    # ...
    'django_extensions',
    'apps.core',
    'apps.produto',
]

Conexão com banco de dados

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql',
        'NAME': config('POSTGRES_DB', 'dicas_de_django_db'),
        'USER': config('POSTGRES_USER', 'postgres'),
        'PASSWORD': config('POSTGRES_PASSWORD', 'postgres'),
        'HOST': config('DB_HOST', 'localhost'),
        'PORT': config('DB_PORT', 5432, cast=int),
    }
}

Um pequeno ajuste

# apps/core/apps.py
from django.apps import AppConfig


class CoreConfig(AppConfig):
    default_auto_field = 'django.db.models.BigAutoField'
    name = 'apps.core'  # <---
# apps/produto/apps.py
from django.apps import AppConfig


class ProdutoConfig(AppConfig):
    default_auto_field = 'django.db.models.BigAutoField'
    name = 'apps.produto'  # <---

Migrações

python manage.py migrate

Operations to perform:
  Apply all migrations: admin, auth, contenttypes, sessions
Running migrations:
  Applying contenttypes.0001_initial... OK
  Applying auth.0001_initial... OK
  Applying admin.0001_initial... OK
  Applying admin.0002_logentry_remove_auto_add... OK
  Applying admin.0003_logentry_add_action_flag_choices... OK
  Applying contenttypes.0002_remove_content_type_name... OK
  Applying auth.0002_alter_permission_name_max_length... OK
  Applying auth.0003_alter_user_email_max_length... OK
  Applying auth.0004_alter_user_username_opts... OK
  Applying auth.0005_alter_user_last_login_null... OK
  Applying auth.0006_require_contenttypes_0002... OK
  Applying auth.0007_alter_validators_add_error_messages... OK
  Applying auth.0008_alter_user_username_max_length... OK
  Applying auth.0009_alter_user_last_name_max_length... OK
  Applying auth.0010_alter_group_name_max_length... OK
  Applying auth.0011_update_proxy_permissions... OK
  Applying auth.0012_alter_user_first_name_max_length... OK
  Applying sessions.0001_initial... OK

Criando um super usuário

python manage.py createsuperuser
Usuário (leave blank to use 'regis'): admin
Endereço de email: 
Password: 
Password (again): 

ORM

python manage.py shell

python manage.py shell_plus  # django_extensions
>>> users = User.objects.all()
>>> users
<QuerySet [<User: admin>]>
>>> for user in users:
...     print(user.id, user.username, user.password)
... 
1 admin pbkdf2_sha256$1000000$yaXKSdSTV6Yjsyewdr7v3f$rfjNshlqwgxSvXJ3cgwaJMNAeLj1AgsJyjW3EEr93wQ=
>>> 

Trocando a senha

>>> user = User.objects.get(username='admin')
>>> user
<User: admin>
>>> user.set_password('teste')
>>> user.save()
>>> user.password
'pbkdf2_sha256$1000000$ybu4NGoCe1MMutgAGr7oSW$NBlsqR3ICe+unfW1OSgMbHQ82MWCpsnKPhUrMoY3968='

Criando um modelo

# produto/models.py
from django.db import models


class Produto(models.Model):
    titulo = models.CharField('título', max_length=100)
    descricao = models.TextField(
        'descrição',
        null=True,
        blank=True
    )
    sku = models.CharField(max_length=8, unique=True)
    preco = models.DecimalField(
        'preço',
        max_digits=9,
        decimal_places=2
    )

    class Meta:
        ordering = ('titulo',)
        verbose_name = 'produto'
        verbose_name_plural = 'produtos'

    def __str__(self):
        return f'{self.sku} - {self.titulo}'

Rodando as migrações

(.venv):/live$ python manage.py makemigrations  # <--
Migrations for 'produto':
  apps/produto/migrations/0001_initial.py
    + Create model Produto
(.venv):/live$ python manage.py migrate  # <--
Operations to perform:
  Apply all migrations: admin, auth, contenttypes, produto, sessions
Running migrations:
  No migrations to apply.

Painel de Admin

# produto/admin.py
from django.contrib import admin

from .models import Produto


@admin.register(Produto)
class ProdutoAdmin(admin.ModelAdmin):
    list_display = ('titulo', 'sku', 'preco')
    search_fields = ('sku', 'titulo', 'descricao')

Criando views

# core/views.py
from django.shortcuts import render


def index(request):
    template_name = 'index.html'
    return render(request, template_name)
# produto/views.py
from django.shortcuts import render

from .models import Produto


def produto_list(request):
    template_name = 'produto/produto_list.html'
    object_list = Produto.objects.all()
    context = {'object_list': object_list}
    return render(request, template_name, context)

Criando urls

# core/urls.py
from django.urls import path

from apps.core import views as v

app_name = 'core'


urlpatterns = [
    path('', v.index, name='index'),
]
# produto/urls.py
from django.urls import path

from apps.produto import views as v

app_name = 'produto'


urlpatterns = [
    path('', v.produto_list, name='produto_list'),
]
# urls.py
from django.contrib import admin
from django.urls import include, path

urlpatterns = [
    path('', include('apps.core.urls', namespace='core')),
    path('produto/', include('apps.produto.urls', namespace='produto')),
    path('admin/', admin.site.urls),
]

Criando um template

<!-- base.html -->
<!DOCTYPE html>
<html lang="en" data-theme="light">
<head>
  <meta charset="utf-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no">
  <link rel="shortcut icon" href="https://www.djangoproject.com/favicon.ico">
  <title>Landpage</title>

  <!-- PicoCSS -->
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@picocss/pico@2/css/pico.min.css" />
</head>
<body class="container-fluid">
  {% include "includes/menu.html" %}

  {% block content %}{% endblock content %}
</body>
</html>
<!-- menu.html -->
<ul>
  <li>
    <a href="{% url 'produto:produto_list' %}">Produtos</a>
  </li>
</ul>
<!-- index.html -->
{% extends "base.html" %}

{% block content %}
  <h1>Minha landpage</h1>
{% endblock content %}
<!-- produto_list.html -->
{% extends "base.html" %}

{% block content %}
  <h1>Produtos</h1>

  <table>
    <thead>
      <tr>
        <th>Título</th>
        <th>SKU</th>
        <th>Preço</th>
      </tr>
    </thead>
    <tbody>
      {% for object in object_list %}
        <tr>
          <td>{{ object.titulo }}</td>
          <td>{{ object.sku }}</td>
          <td>{{ object.preco }}</td>
        </tr>
      {% endfor %}
    </tbody>
  </table>
{% endblock content %}

MTV

Sites

djangoproject.com

django-ninja.dev

django-rest-framework.org

dicas-de-django.com.br

Livros

Django 3 by Example

Antonio Melé

Django Admin Cookbook

books.agiliq.com/projects/django-admin-cookbook/en/latest/

Two Scoops of Django 3.x

Atualmente: Django 5.2.3

Django Ninja

Meu Canal

Regis do Python

Desenvolvimento Web com Django

By Regis Santos

Desenvolvimento Web com Django

  • 44