Executando verificação de segurança...
3

[PARTE-1][TUTORIAL][DEPLOY][DJANGO] - Enviando para deploy

Orientações

Para o deploy usarei minha VPS/Domínio comprados no site da HOSTINGER , para quem quiser me ajudar comprando com o meu cumpom HOSTINGER - REFERÊNCIA, caso você compra sua VPS e seu domínio, ou qualquer outro produto dentro do site, utilizando meu código, vai me ajudar a receber uma graninha a mais.

Na hostinger eles oferecem o aplicativo para deploy de varias aplicações, como django, rusty, node entre outras, usando o OpenLiteSpeed como gerênciador.
Muitas vezes tentei fazer deploy com NGINX, porém é muito trabalhoso.

Usaremos também POSTGRESSQL, caso queira usar SQLITE apenas pule as etapas indicadas.

Início

Escolha um diretório e vamos começar, usarei o GitBash para começar o projeto.

$ mkdir django-project
$ cd django-project
$ nano .env
    #ADICIONAR AO ARQUIVO E SALVAR
    SECRET_KEY=!r8!9ja-3jl*_@5f*3uz4#byytvets90tj9g_9w(t$!_^7!l3j
    DEBUG=True
    POSTGRES_NAME=django_project_db
    POSTGRES_USER=django_project_user
    POSTGRES_PASSWORD=!r8!9ja-3jl*_@5f*3uz4#byytvets90tj9g_9w(t$!_^7!l3j
    POSTGRES_HOST=localhost
    POSTGRES_PORT=5432
    
$ python -m virtualenv venv
$ source venv/Scripts/activate
$ pip install django python-dotenv whitenoise psycopg2-binary Pillow

Criando o projeto, eu sempre crio primeiro a pasta no caso django-project e depois dentro eu uso o django-admin com um "." na frente para criar no mesmo diretório

$ django-admin startproject core .
$ mkdir apps
$ cd apps
$ django-admin startapp accounts
$ cd accounts
$ nano urls.py
    #ADICIONAR AO ARQUIVO E SALVAR
    from django.urls import static

    urlpatterns = [

    ]

Sempre tive dúvida de como utilizar os apps do django em uma pasta especifica, é simples, no arquivo "apps.py", adicione a frente do name='accounts', o "apps." ou o nome da pasta desejada.

from django.apps import AppConfig


class AccountsConfig(AppConfig):
    default_auto_field = 'django.db.models.BigAutoField'
    name = 'apps.accounts'

Em settings, vamos fazer uma jogada, segue as pastas em forma de árvore.
TREE MAKER

.
└── django-project/
    ├── apps/
    │   └── accounts
    ├── core/
    │   └── settings/
    │       ├── __init__.py
    │       ├── base.py
    │       ├── local.py
    │       └── production.py
    ├── templates
    ├── static/
    │   ├── css
    │   ├── js
    │   └── images
    ├── media
    ├── public/
    │   ├── static
    │   └── media
    ├── .env
    └── .gitignore

Settings

Para que funcione o settings precisamos alterar alguns arquivos do django (WSGI e manage)

manage.py

import os
import sys
from core.settings import base

def main():
    """Run administrative tasks."""
    if base.DEBUG:
        os.environ.setdefault('DJANGO_SETTINGS_MODULE',
                              'core.settings.local')
    else:
        os.environ.setdefault('DJANGO_SETTINGS_MODULE',
                              'core.settings.production')
    try:
        from django.core.management import execute_from_command_line
    except ImportError as exc:
        raise ImportError(
            "Couldn't import Django. Are you sure it's installed and "
            "available on your PYTHONPATH environment variable? Did you "
            "forget to activate a virtual environment?"
        ) from exc
    execute_from_command_line(sys.argv)


if __name__ == '__main__':
    main()

wsgi.py

import os
from core.settings import base


from django.core.wsgi import get_wsgi_application

if base.DEBUG:
    os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'core.settings.local')
else:
    os.environ.setdefault('DJANGO_SETTINGS_MODULE',
                          'core.settings.production')

application = get_wsgi_application()

base.py

  • Aqui vou configurar também o whitenoise, para servir os staticfiles em produção na pasta public/static e media e também o BASE_DIR , muito importante.
...
import os
from dotenv import load_dotenv
load_dotenv()

#O PADRÃO É VIR COM 2 PARENTS , PORÉM VAMOS COLOCAR MAIS UM
BASE_DIR = Path(__file__).resolve().parent.parent.parent
...
SECRET_KEY = os.environ.get('SECRET_KEY')
DEBUG = os.environ.get('DEBUG') == 'True'

ALLOWED_HOSTS = ['*']

#OPICIONAL CASO QUEIRA USAR O CODESPACE PARA EDITAR SEU PROJETO
if 'CODESPACE_NAME' in os.environ:
    codespace_name = os.getenv("CODESPACE_NAME")
    codespace_domain = os.getenv("GITHUB_CODESPACES_PORT_FORWARDING_DOMAIN")
    CSRF_TRUSTED_ORIGINS = [
        f'https://{codespace_name}-8000.{codespace_domain}']

X_FRAME_OPTIONS = "ALLOW-FROM preview.app.github.dev"

#ADIÇÃO DO APP DE ACCOUNTS
INSTALLED_APPS = [
    ...,
    "apps.accounts",
]

#CONFIGURAÇÃO DO WHITENOISE
MIDDLEWARE = [
    # ...
    "django.middleware.security.SecurityMiddleware",
    "whitenoise.middleware.WhiteNoiseMiddleware",
    # ...
]

STATICFILES_STORAGE = "whitenoise.storage.CompressedManifestStaticFilesStorage"

TEMPLATES = [
    {
        ...
        'DIRS': [os.path.join(BASE_DIR, 'templates')],
        ...
    },
]

LANGUAGE_CODE = 'pt-br'

TIME_ZONE = 'America/Sao_Paulo'
#PARA SERVIR OS ARQUIVOS STATICOS, VAMOS USAR AS CONFIGURAÇÕES A SEGUIR
STATICFILES_DIRS = [
    os.path.join(BASE_DIR, 'static'),
]

STATIC_URL = '/public/static/'
STATIC_ROOT = os.path.join(BASE_DIR, 'public/static')

MEDIA_URL = '/public/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'public/media')

#MODELO DE AUTENTICAÇÃO EDITADO
AUTH_USER_MODEL = 'accounts.Account'

local.py

  • No local, vamos utilizar o sqlite para facilitar as configurações, pode retirar o DATABASES do base.
from .base import *

DEBUG = True

ALLOWED_HOSTS = [
    '*',
]

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': BASE_DIR / 'db.sqlite3',
    }
}

production.py

  • As configurações são necessárias para melhor segurança da sua aplicação.
from .base import *

DEBUG = False
#COLOCAR SEU HOST AQUI
ALLOWED_HOSTS = [
    '*'
]

#SEGURANÇA
if os.environ.get("DEBUG") == False:
    # SECURITY WARNING: don't run with debug turned on in production!
    SECURE_SSL_REDIRECT = True

    SESSION_COOKIE_SECURE = True
    CSRF_COOKIE_SECURE = True

    SECURE_HSTS_SECONDS = 3600
    SECURE_HSTS_INCLUDE_SUBDOMAINS = True
    SECURE_HSTS_PRELOAD = True

    X_FRAME_OPTIONS = "DENY"


DATABASES = {
    "default": {
        "ENGINE": "django.db.backends.postgresql",
        "NAME": os.environ.get("POSTGRES_NAME"),
        "USER": os.environ.get("POSTGRES_USER"),
        "PASSWORD": os.environ.get("POSTGRES_PASSWORD"),
        "HOST": os.environ.get("POSTGRES_HOST"),
        "PORT": os.environ.get("POSTGRES_PORT"),
    }
}

Configurando os static files

core/urls.py

from django.contrib import admin
from django.urls import path,re_path,include
from django.conf.urls.static import static
from django.conf import settings
from django.views.static import serve

urlpatterns = [
    path('admin/', admin.site.urls),
]

#INCLUDES
urlpatterns += [
    # INCLUDES
    path('', include('apps.accounts.urls')),
]


#STATICFILES
urlpatterns += [
    # STATICMEDIA SERVE
    re_path(r'^public/media/(?P<path>.*)$', serve,
            {'document_root': settings.MEDIA_ROOT}),
    re_path(r'^public/static/(?P<path>.*)$', serve,
            {'document_root': settings.STATIC_ROOT}),
]

urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)

Criando o modelo de accounts

models.py

import uuid

from django.db import models
from django.contrib.auth.models import AbstractUser


def image_path(instance, filename):
    ext = filename.split('.')[-1]
    filename = f'{uuid.uuid4()}.{ext}'
    user= Account.objects.get(email=instance)
    return f"accounts/{user.username}/avatars/{filename}"

class Account(AbstractUser):
    email = models.EmailField(
        max_length=255, unique=True, blank=False, null=False)

    avatar = models.ImageField(
        upload_to=image_path, blank=True, null=True)
    email_notify = models.BooleanField("Notificação por email", default=True)
    slug = models.SlugField(blank=True)

    def save(self, *args, **kwargs):
        if not self.slug:
            self.slug = self.username
        return super().save(*args, **kwargs)

    USERNAME_FIELD = 'email'
    REQUIRED_FIELDS = ['username',]

    def __str__(self):
        return self.email

admin.py

from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from django.utils.html import format_html
from apps.accounts.models import Account


# Register your models here.


@admin.register(Account)
class AccountAdmin(UserAdmin):
    def thumbnail(self, object):
        return format_html(
            "<img src='{}' width='30' height='30' style='border-radius:50%;'/>"
            .format(object.avatar.url))

    model = Account
    list_display = ('id', 'email', 'username', 'last_login',
                    'date_joined', 'is_active')
    list_filter = ('email', 'username', 'last_login',
                   'date_joined', 'is_active')
    readonly_fields = ('last_login', 'date_joined', "slug")

    fieldsets = (
        ('Configurações', {
         "fields": ("avatar", "email", "password", "email_notify")}),
        ("Permissões", {"fields": ("is_superuser", "is_staff",
                                    "is_active", "groups", "user_permissions")}),
        ("Datas importantes", {"fields": ("date_joined", "last_login")}),
        ("Imutáveis", {"fields": ("slug",)})

    )
    add_fieldsets = (
        (None, {
            "classes": ("wide",),
            "fields": (
                "email", "password1", "password2", "is_staff",
                "is_active", "groups", "user_permissions"
            )}
         ),
    )

    search_fields = ('email', 'first_name', 'last_name', 'username')
    ordering = ('-date_joined',)

Iniciando as migrações e criação do superuser

Em sua pasta inicial django-project

$ python manage.py makemigrations
$ python manage.py migrate
$ python manage.py createsuperuser
$ python manage.py runserver

Usando o navegador, acessar http://127.0.0.1:8000/admin/ , verificar se tudo está funcionando.

Após a criação da autenticação de usuários, podemos seguir adiante.

Vamos dar continuidade em um próximo post, deixa sua mensagem pra gente continuar.

PRÓXIMO >

Carregando publicação patrocinada...
1
3