Bienvenidos a Cyber Coffee Blog

Creado para la comunidad de programadores de habla hispana

Cyber Coffe es un blog de programación dedicado a tutoriales y ejemplos de proyectos usando varios lenguajes, frameworks y herramientas, explicando estos de manera fácil y objetiva desde como crearlos hasta el uso de GIT/GitHub y su despliegue

Empezar ahora!
arrow-back

TO DO List Django

Código Completo

"Antes que nada quisiera aclarar que esto es solo por aportar mi pedacito en apoyar a las personas de habla hispana que están iniciando en este mundo por lo que cada vez que los invite a ver la explicación detallada en mis videos no es necesario dar un like ni subscribirse si no lo desean es solo porque creo que con palabras y ejemplos pueden llegar a entender mucho mejor"

En este proyecto vamos a desarollar un ToDo List utilizando el patrón de arquitectura de software Modelo-Vista-Template (MVT) o como bien se conoce Modelo-Vista-Controlador(MVC).

CONTENIDO:

  • Instalación

  • Modelos

  • Admin Site

  • Views

  • Templates

  • Forms

  • Urls

  • Archivos Estáticos(Static)

  • Preview

  • IMPORTANTE: Todo el código se encuentra disponible en GitHub por lo que debajo de cada foto de cada archivo que estemos modificando se encontrará el link para obtener el código en GitHub, aunque si eres principiante te recomendaría escribirlo entero tu mismo

    Instalación y Configuración

    Para ello primero vamos a asegurarnos de tener instalado Python en nuestra PC o Laptop, luego vamos a crear nuestro entorno de desarrollo para el proyecto de la siguiente manera:

  • Nos ubicamos en nuestra carpeta raíz del proyecto y ejecutamos el comando:
  • python -m venv nombre-entorno

  • Con esto ya debe crearse nuestro entorno y ahora procedemos a activarlo con el siguiente comando:
  • nombre_entorno\scripts\activate

  • Luego de ejecutar esto deberias ver tu consola de la siguiente manera:
  • El siguiente paso sera instalar django en nuestro entorno y para ellos ejecutaremos el siguiente comando:
  • pip install django

    "Por favor asegurarnos de que en todo momento tengamos nuestro entorno activado"

  • Ahora vamos a iniciar nuestro proyecto de django con el comando
  • django-admin-startproject ToDo .

    Es importante luego del nombre que deseamos darle a nuestro proyecto colocar espacio+punto " ." ya que con esto nos aseguramos de que el proyecto se cree dentro de la carpeta que designamos para este en lugar de crearse otra adicional.

  • Para verificar que nuestro proyecto ha sido creado con exito ejecutamos el siguiente comando:
  • python manage.py runserver

    "Esto permitira correr nuestro servidor de desarrollo local y si copiamos la url donde se encuentra corriendo el servidor que por defecto es http://localhost:8000 y la insertamos en nuestro navegador tendremos esto:"

  • Ya tenemos nuestro entorno creado y activo, tenemos instalado Django y nuestro proyecto creado; Ahora procederemos a crear nuestra app la cual gestionará nuestras tareas para ello ejecutamos el siguiente comando:
  • python manage.py startapp task

    "Es importante que sepan que muchas de las cosas que voy a tratar se las daré en inglés excepto las explicaciones, con el fin de que se familiaricen con el idioma debido a su gran importancia en el mundo profesional"

  • En este punto nuestro directorio debería verse algo así:
  • Luego necesitaremos ubicar nuestro settings.py dentro de la carpeta de nuestro proyecto y dirigirnos hacia las INSTALLED_APPS ahí incluiremos la aplicación tarea que hemos creado y asi quedará registrada:
  • INSTALLED_APPS = [
    .....
    'django.contrib.staticfiles',
    'task',
    ]

    Modelos

    Es necesario aclarar que ya deben tener conocimientos mínimos de Django ya que solo estaré explicando todas las partes necesarias especificamente para este proyecto. Para explicación mas detallada sobre la parte de los modelos en Django te invito a visitar el video sobre Models de mi canal de YouTube:

  • Si se fijaron luego de correr nuestro servidor local se creó un archivo llamado db.sqlite3 en nuestro directorio raíz el cual sera nuestra base de datos local o de desarrollo
  • Teniendo en cuenta lo antes visto ahora vamos a crear nuestro modelo para la app de tarea
  • Primero ubicamos nuestro archivo models.py y dentro de él agregamos los siguientes campos los cuales nos permitirán crear las columnas dentro de nuestra tabla tarea en la base de datos para luego guardar la información necesaria
  • 
    from django.db import models
    
    class Task(models.Model):
        title = models.CharField(max_length = 50, null = False)
        body = models.TextField(max_length= 200, null =True, blank = True)
         
        objects = models.Manager() # The default manager.
    
        class Meta:
            verbose_name = 'Task'
            verbose_name_plural = 'Tasks'
            db_table = 'Task'
    
        def __str__(self):
            return self.title
                
    Ver Explicación
  • Luego de crear nuestro modelo necesitamos registrar esta nueva tabla en nuestra base de datos y para ello ejecutamos el comando:
  • python manage.py makemigrations

    Con esto se nos creara una carpeta dentro de nuestra app, llamada migrations en la cual estará guardado todos los cambios en la estructura de nuestro modelo, es importante aclarar que cada vez que se realice un cambio en nuestro modelo debemos crear la migracion con el comando antes mencionado

  • Ya creamos nuestras migraciones pero aun no existen en nuestra base de datos para ello vamos a ejecutar el comando:
  • python manage.py migrate

    Con esto nuestro modelo con sus respectivos campos quedaran registrados en la base de datos. Y para comprobar esto podemos instalar la aplicación DB Browser para poder visualizar nuestra base de datos sqlite3 y comprobar que se creo nuestra tabla con exito o bien si estamos usando Vs Code instalar la extension SQLite Viewer y solo necesitan hacer click en el archivo .sqlite3 para abrirlo con esta extensión

    La razón de que este salga con letra mayúscula en la base de datos es porque antes en nuestro modelo estuvimos personalizando los metadatos de este a través de su clase META al igual que el método string al final del modelo que nos permite definir cuales son los campos que queremos mostrar en la vista previa del panel de administración de django. Pero como dije antes sería mucho mas fácil para ustedes entender esto con palabras por lo que pueden ver la explicación completa de lo que estuvimos haciendo acá: Link del Video

    ADMIN SITE

    Ahora vamos a interactuar un poco con nuestro modelo ya creado y para ello utilizaremos el Admin Site de django

  • Antes debemos registrar nuestro modelo en el sitio de administracion para que este aparezca por lo que vamos a nuestro admin.py dentro de nuestra app tarea y dentro de el escribimos el siguiente código:
  • 
    from django.contrib import admin
    from .models import Task
    
    admin.site.register(Task)
                
    Ver Explicación

    Debemos fijarnos bien en las importaciones que estoy mostrando en el código ya que evidentemente antes de usar una clase que esta contenida dentro de otro archivo debemos importarlo

  • Luego de esto nuestro modelo ya aparecerá en nuestro panel de administración
  • Para visitarlo volvemos a levantar nuestro servidor de desarrollo con el comando antes mencionado para esto y visitar nuevamente la url con nuestro navegador pero con la particularidad de que agregamos admin/ a nuestra ruta:
  • http://localhost:8000/admin/

    Como resultado tendremos esto y es que antes debemos crear nuestro super usuario y para ello cancelamos nuestro servidor local con "Ctrl+C", si al igual que yo no les gusta ver la consola muy cargada pueden limpiarla escribiendo "cls" en Windows o "Ctrl+L" en Linux

  • Dicho esto vamos a crear nuestro super usuario y lo haremos con el comando:
  • python manage.py createsuperuser

  • Nos saldrá esto y solo ponemos el usuario, email y la contraseña que deseamos, es importante aclarar que cuando escriban la contraseña no la podrán visualizar porque es un mecanismo de seguridad que utiliza Django por defecto. Ya podemos iniciar sesión en nuestro sitio de administración con las credenciales que hemos creado
  • Volvemos a correr el servidor y entramos a la URL de admin y como pueden ver ya está nuestro modelo acá
  • Podemos crear nuevas tareas, eliminarlas y actualizarlas dentro de nuestro Admin Site
  • VIEWS

  • Ahora debemos manejar la lógica para tratar la información de nuestra base de datos y mostrala en nuestro plantilla html (Template) par ello nos dirigimos a nuestro archivo views.py dentro de nuestra app tarea e introducimos el siguiente código:
  • 
    from django.shortcuts import render, redirect
    from .models import Task
    from .forms import TaskForm
    from django.db.models import Q
    
    def home(request):
        """View function for home page of site."""
        # Render the HTML template index.html with the data in the context variable
        data = Task.objects.all()
        return render(request, 'all.html', context={'tasks':data})
    
    
    def add(request):
        form = TaskForm()
        if request.method == "POST":
            form = TaskForm(request.POST)
            if form.is_valid():
                title=request.POST['title']
                body=request.POST['body']
                new_task = Task(title=title, body=body)
                new_task.save()
                return redirect('home')
        return render(request, 'add.html', {"form":form})
    
    
    def search(request, name):
        task = Task.objects.filter(Q(title__icontains=name) | Q(body__icontains=name))
        if len(task) >= 0:
            context = {"tasks":task}
            return render(request, 'all.html',context=context)
        else:
            context= {}
            return render(request, 'all.html', context=context)
    
    
    def delete(request, pk):
        Task.objects.filter(id=pk).delete()
        return redirect("home")
    
    
    def detail(request, id):
        task = Task.objects.get(id=id)
        context= {"task":task}
        return render(request, 'detail.html', context=context)
                
    Ver Explicación

    Para la explicación detallada de que hace cada linea de este código te invito a que veas el video en mi canal de YouTube sobre las Views: Link del Video

    TEMPLATES

  • Te estarás preguntando donde estan las plantillas que debo renderizar para mostrar la información de mi base de datos, pues aqui estan.
  • Para esto dentro de nuestra app tarea necesitaremos crear una carpeta llamada templates y dentro de ella colocamos nuestras plantillas html.
  • Primeramente como buena práctica y siempre lo recomiendo mucho es hacer nuestro base.html o como deseen llamarlo, basicamente es la plantilla por la cual se van a regir las demás, por ejemplo si se han fijado instagram en casi todas las vistas de su app mantiene las barras superior e inferior de navegacion, nosotros usaremos una lógica similar para esta app y es necesario aclarar que esto depende mucho de tus necesidades y el resultado que quieras lograr con tus vistas.
  • Dentro de nuestra carpeta templates vamos a crear 5 archivos html: base.html, topbar.html, all.html, add.html y detail.html
  • base.html

    
    {% load static %}
    
    <link rel="stylesheet" href="{% static 'css/styles.css' %}">
    <body>
        <div class="main-content">
            {% include 'topbar.html' %}
            
            {% block content %}
                
            {% endblock %}
                
        </div>
    <script src="{% static 'scripts/main.js' %}"></script>
    </body>
                
    Ver Explicación
  • Creamos la plantilla que será nuestra barra superior la cual esta incluida en nuestro base.html ya que estara en todas las vistas de nuestra app
  • topbar.html

    
    {% load static %}
    <script src="https://code.jquery.com/jquery-3.3.1.js"></script>
    
    <div class="top bar">
        <a style="margin-left:20px;" href="/"><h1>PaDespue</h1></a>
        <div class="actions">
            <input type="search" placeholder="Search Task" id="search">
            <a href="#" id="go" onclick="searchTask()">Go</a>
            <a id="add" href="{% url 'add' %}"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32"><defs><style>.cls-1{fill:none;stroke:#000;stroke-linecap:round;stroke-linejoin:round;stroke-width:2px;}</style></defs><title/><g id="plus"><line class="cls-1" x1="16" x2="16" y1="7" y2="25"/><line class="cls-1" x1="7" x2="25" y1="16" y2="16"/></g></svg></a>
        </div>
    </div>
                
    Ver Explicación
  • Luego creamos la plantilla donde podemos visualizar todas las tareas que se encuentran en nuestra base de datos
  • all.html

    
    {% extends "base.html" %}
    {% load static %}
    
    {% block content %}
    <div class="box" id="box">
        {% for task in tasks %}
            <div style="height: 20%;" class="task bar">
                <p style="margin-left:20px;">{{task.title}}: {{task.body|truncatechars:20}}</p>
                <div class="action-task bar">
                    <a class="ver" href="{% url 'detail' task.id %}">See More</a>
                    <a class="delete" href="{% url 'delete' task.id %}"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" height="80%" style="enable-background:new 0 0 512 512;" version="1.1" viewBox="0 0 512 512" width="80%" xml:space="preserve"><style type="text/css">
                        </style><g class="st2" id="cross"><g class="st0"><line class="st1" x1="112.5" x2="401" y1="112.5" y2="401"/><line class="st1" x1="401" x2="112.5" y1="112.5" y2="401"/></g></g><g id="cross_copy"><path d="M268.064,256.75l138.593-138.593c3.124-3.124,3.124-8.189,0-11.313c-3.125-3.124-8.189-3.124-11.314,0L256.75,245.436   L118.157,106.843c-3.124-3.124-8.189-3.124-11.313,0c-3.125,3.124-3.125,8.189,0,11.313L245.436,256.75L106.843,395.343   c-3.125,3.125-3.125,8.189,0,11.314c1.562,1.562,3.609,2.343,5.657,2.343s4.095-0.781,5.657-2.343L256.75,268.064l138.593,138.593   c1.563,1.562,3.609,2.343,5.657,2.343s4.095-0.781,5.657-2.343c3.124-3.125,3.124-8.189,0-11.314L268.064,256.75z"/></g></svg></a>
                </div>
            </div>
            <br>
        {% endfor %}
    </div>
    {% endblock %}
                
    Ver Explicación
  • Además crearemos la plantilla para visualizar especificamente la tarea que deseemos seleccionar
  • detail.html

    
    {% extends "base.html" %}
    {% load static %}
    
    {% block content %}
    <div class="box" style="align-items: flex-start;">
    <p style="margin-bottom: 0;color: rgb(189, 161, 240); margin-left: 20px;">{{task.title}}:</p>
    <br>
    <p style="margin-top: 0;color: #fff; font-size: 24px; word-wrap: break-word; margin-left: 20px;">{{task.body}}</p>
    </div>
    {% endblock %}
                
    Ver Explicación
  • En este punto ya no estaremos usando el panel de administración de django por lo que para crear nuestras tareas necesitaremos un formulario y una plantilla para mostrarlo y asi poder crearlas desde el FrontEnd de nuestra app, por lo que necesitamos nuestro archivo html y ademas un formulario
  • add.html

    
    {% extends "base.html" %}
    {% load static %}
    
    {% block content %}
    <div class="box">
        <form class="form" method="post">
            {% csrf_token %}
            {% for field in form %}
            <div class="form-group">
                <label for="{{ field.id_for_label }}">{{ field.label }}</label>
                {{ field }}
            </div>
            {% endfor %}
            <button type="submit">Add</button>
        </form>
    </div>
    {% endblock %}
                
    Ver Explicación

    FORMS

  • Para la plantilla de agregar nuestras tareas habiamos dicho anteriormente que necesitabamos un formulario y te lo muestro a continuación
  • Primeramente debemos ir a nuestra carpeta de tarea y crearemos dentro un archivo llamado forms.py, dentro de él introduciremos el siguiente código
  • 
    from django import forms
    
    class TaskForm(forms.Form):
        title = forms.CharField(max_length=50, label='Title', required=True)
        body = forms.CharField(widget=forms.Textarea(attrs={"rows":"5"}),label='Body')
                
    Ver Explicación

    Detalles acerca de los Forms.py de Django ver video: Link del Video

    URLS

    Ahora te estarás preguntando como hacer que nuestras funciones creadas en views.py se ejecuten y si eres observador debes tener una idea ya que algo llamado {% url '' %} dentro de los templates te llamo la atención; y si debemos definir nuestras urls o rutas para que cuando el navegador visite cada una de estas se ejcute la función que tiene asociada dicha ruta

  • Para ello crearemos nuestro archivo urls.py dentro de nuestra carpeta de la app tarea y escribiremos el siguiente código
  • 
    from django.urls import path
    from .views import *
    
    urlpatterns = [
        path('', home, name='home'),
        path('add/', add, name='add'),
        path('search/<str:name>/', search, name='search'),
        path('search//', home, name='search_empty'),
        path('delete/<int:pk>/', delete, name='delete'),
        path('detail/<int:id>/', detail, name='detail'),
    ]
                
    Ver Explicación
  • Ahora debemos irnos a nuestro archivo urls.py dentro de nuestro proyecto principal ToDo e incluir estas urls que acabamos de crear en la app tarea para esto necesitamos importar la función include y lo haremos de la siguiente manera
  • 
    from django.contrib import admin
    from django.urls import path, include
    
    urlpatterns = [
        path('admin/', admin.site.urls),
        path('',include('task.urls')),
    ]
                
    Ver Explicación
  • Finalmente al visitar la pagina principal de nuestro proyecto estaran registradas las urls para manejar la logica de nuestras tareas Visita mi video para una mejor explicación de las urls en django: Link del Video
  • STATIC FILES(ARCHIVOS ESTATICOS)

    Los archivos estáticos en nuestro proyecto no es mas que las imágenes, estilos y scripts que necesitamos para cada una de nuestras apps

    "Nota:" En este proyecto no usamos imágenes porque no fueron necesarias ya que utilizamos elementos svg en nuestro html

  • Dentro de nuestra app tarea vamos a crear una carpeta llamada static dentro de la cual crearemos 2 carpetas mas llamadas(css y scripts)
  • Dentro de scripts creamos nuestro archivo main.js con el siguiente codigo Java Script
  • 
    function searchTask() {
        var searchInput = document.getElementById('search');
        var searchText = searchInput.value;
        window.location.href = '/search/' + searchText + '/';
    }
                
    Ver Explicación
  • Ahora ya tenemos todo listo pero lo habras encontrado un poco feo no?
  • No te preocupes ahora necesitamos estilar nuestras plantillas HTML con CSS y dentro de nuestra carpeta css crearemos el archivo styles.css dentro del cual introduciremos el código que les voy a dejar acá
  • 
    /*Scrollbar Styles*/
    .box::-webkit-scrollbar {
        width: 5px;
    }
    .box::-webkit-scrollbar-track {
        background-color: transparent;
    }
    .box::-webkit-scrollbar-thumb {
        background-color: rgb(210, 210, 216);
        border: none;
        border-radius: 20px;
    }
    
    /*Global Styles*/
    body{
        background-color: #4e497a;
        height: 100vh;
        display: flex;
        justify-content: center;
        align-items: center;
        margin: 0;
    }
    
    h1{
        font-family: 'Plus Jakarta Display', sans-serif;
        color: #a095f9;
        font-size: 3em;
    }
    
    h3{
        font-family: 'Plus Jakarta Display',sans-serif;
        font-size: 3em;
        color: #fff;
    }
    
    p{
        font-family: 'Roboto Regular';
        font-size: 2em;
        color: #fff;
    }
    
    a{
        height: 100%;
        text-decoration: none;
        color: #fff;
        text-align: center;
        display: flex;
        flex-direction: column;
        justify-content: center;
        font-family: 'Roboto Regular';
        font-size: 20px;
    }
    
    button{
        margin: 0 0 20px 0;
        background-color: #33304f;
        border: none;
        border-radius: 5px;
        width: 120px;
        height: 40px;
        color: #fff;
    }
    
    button:hover{
        color: black;
        background-color: rgb(231, 144, 231);
        box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.3);
        cursor: pointer;
    }
    
    /*Generic Style for Horizontal Bars*/
    .bar{
        display: flex;
        flex-direction: row;
        align-items: center;
        justify-content: space-between;
        width: 100%;
        height: 15%;
    }
    
    .form{
        width: 100%;
        height: 100%;
        display: flex;
        flex-direction: column;
        justify-content: space-around;
        align-items: center;
        padding-top: 20px;
    }
    
    .form-group{
        margin:0;
        display: flex;
        flex-direction: column;
        justify-content: flex-start;
        width: 60%;
    }
    
    .form label{
        font-size: 18px;
        color: rgb(18, 32, 46);
    }
    
    input, textarea, select {
        margin-bottom: 10px;
        padding: 10px;
        border-radius: 3px;
        border: none;
        font-family: 'Roboto Regular', sans-serif;
        font-size: 18px;
    }
    
    input:focus{
        outline: none;
    }
    
    svg{
        width: 100%;
        height: 100%;
    }
    
    
    /*Specific Styles for our classes*/
    
    .main-content{
        padding: 30px;
        width:80%;
        height: 70%;
        display: flex;
        flex-direction: column;
        justify-content: center;
        align-items: center;
        box-shadow: 0 0 10px rgba(0,0,0,0.3);
    }
    
    .actions{
        display: flex;
        flex-direction: row;
        justify-content: start;
        align-items: center;
        width: 50%;
        height: 100%;
    }
    
    #search{
        margin-top: 10px;
        width: 55%;
        height: 50%;
    }
    
    #go{
        width: 20%;
        height: 50%;
        margin-left: 30px;
        border-radius: 5px;
        background-color: #33304f;
    }
    
    #go:hover{
        box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.3);
    }
    
    #add {
        width: 20%;
        height: 80%;
        display: flex;
        flex-direction: row;
        justify-content: flex-end;
        align-items: center;
    }
    
    .box{
        display: flex;
        flex-direction: column;
        align-items: center;
        justify-content: flex-start;
        width: 100%;
        height: 70%;
        margin-top: 20px;
        scroll-behavior: smooth;
        overflow-y: scroll;
        text-wrap: wrap;
    }
    
    .action-task{
        width: 20%;
        height: 100%;
    }
    
    .delete {
        margin-right: 10px;
        width: 30%;
        height: 100%;
    }
    
    .delete svg:hover{
        fill: red;
    }
    
    .ver{
        width: 70%;
    }
    
    .ver:hover{
        color: rgb(28, 44, 59);
    }
    
                    
    Ver Explicación
  • Explicación de la funcionalidad del archivo main.js y la manera de darle estilos a cada elemento de las plantillas en mi video: Link del video
  • Ahora necesitaremos correr nuestro servidor local de la manera que ya aprendimos y pues visitar el sitio en el navegador y ya podemos utilizar nuestra aplicación de gestionar tareas y no se preocupen por si se les cierra el navegador ya que estas están guardadas en nuestra base de datos por lo que siempre que no las eliminen estas se conservarán intactas y se mostrarán en nuestra aplicación.

    PREVIEW

    arrow-back

    E-Comerce Content

    arrow-back

    Aplicación de Chat

    Código Completo

    En este proyecto estaremos haciendo un chat en tiempo real usando django-channels para el control de websockets por parte del servidor y daphne como web server para que nuestro servidor corra correctamente nuestra app de tipo asíncrona ya que el servidor de django solo corre las apps de tipo wsgi.

    ASGI - Asyncronus Service Gateway Interface WSGI - Web Service Gateway Interface

    Recomendación: Este tutorial es nivel avanzado en Django por favor ver tutoriales anteriores si aún no tienen suficiente habilidad y conocimiento sobre el Framework.

    Dato del Autor: Cada uno de los pasos de este tutorial y su explicación a fondo esta disponible en mi canal de YouTube
    Link del video

    Requisitos para este proyecto:

  • Nivel intermedio django
  • Conocimientos de Java Script
  • Dominio de Python
  • CONTENIDO:

  • Instalación

  • Configuración ASGI

  • Urls

  • Views

  • Forms

  • Routing

  • Consumers

  • Templates

  • ARCHIVOS ESTATICOS

  • Preview

  • INSTALACION

    Primeramente ya debemos tener nuestro entorno de desarrollo listo con django instalado y nuestro proyecto creado al cual le daremos el nombre de ChatApp

    Vamos a crear además nuestra app que va a tener el nombre de chat

    Nuestro directorio en este punto debería verse de la siguiente manera

  • Primero debemos instalar django channels y daphne en nuestro entorno de desarrollo y para ello ejecutamos los comandos:
  • pip install channels

    pip install daphne

  • Luego debemos ubicarnos en nuestras INSTALLED_APPS en el settings.py de nuestro proyecto y colocar channels y daphne y en lugar de colocar nuestra app chat lo haremos de la siguiente manera:
  • INSTALLED_APPS = [
    'chat.apps.ChatConfig',
    'daphne',
    .....
    'django.contrib.staticfiles',
    'channels',
    ]

    Es muy importante que ChatConfig se encuentre de primero en nuestra lista y daphne justo debajo

    ASGI CONFIGURACION

  • Luego debemos configurar nuestro archivo asgi.py dentro de nuestro proyecto y lo haremos de la siguiente manera:
  • Realizamos las siguientes importaciones:
  • 
    from channels.auth import AuthMiddlewareStack
    from channels.routing import URLRouter,
    ProtocolTypeRouter
    from chat.routing import websocket_urlpatterns
                
  • Ahora personalizamos un poco nuestra variable application usando lo que estuvimos importando:
  • 
    application = ProtocolTypeRouter(
        {
            'http':get_asgi_application,
            'websocket':AuthMiddlewareStack(
            URLRouter(
                websocket_urlpatterns
                )
            )
        }
    )
                

    Importante: El editor de texto que usen puede marcarles como un error la importación de websocket_urlpatterns proveniente de nuestra app chat ya que aun no hemos creado nuestro archivo routing.py pero no se preocupen porque mas adelante lo estaremos creando.

  • Ahora en el archivo settings.py dentro de nuestro proyecto ChatApp colocamos lo siguiente:
  • 
    
    ASGI_APPLICATION = 'ChatApp.asgi.application'
    
    CHANNEL_LAYERS = {
        "default": {
            "BACKEND": "channels.layers.InMemoryChannelLayer"
        }
    }
                

    Lo que estamos haciendo acá es asignandole a la variable ASGI_APPLICATION de Django el contenido de la configuración que antes hicimos en asgi.py. Además en el diccionario CHANNEL_LAYERS estamos definiendo las capas de canales para nuestro websocket y el comportamiento asincrónico.

    URLS

  • Ahora debemos manejar las rutas de nuestro proyecto y lo haremos creando un archivo llamado urls.py dentro de nuestra app chat con el siguiente código:
  • 
    from django.urls import path
    from views import *
    from django.contrib.auth.views import LogoutView
    
    urlpatterns = [
        path('', user_login, name='login'),
        path('54465488fsdfsd/', home, name='chat-page'),
        path('auth/logout/', LogoutView.as_view(), name='logout-user')
    ]
                
  • Como pudiste ver estamos utilizando una clase llamada LogoutView del archivo views de la librería auth que contiene django pero ahora nos hace falta definir la variable LOGOUT_REDIRECT_URL que es a la url que nos va a redirigir cuando cerremos sesión en nuestra aplicación
  • Para hacer esto vamos a nuestro settings.py y definimos lo siguiente:
  • 
    LOGOUT_REDIRECT_URL = "login"
                
  • Luego necesitamos incluir las urls de nuestra app en el patrón de rutas principal de nuestro proyecto y para ellos vamos a nuestro archivo urls.py de nuestro proyecto ChatApp y nos aseguramos que quede de la siguiente manera:
  • 
    from django.contrib import admin
    from django.urls import path, include
    
    urlpatterns = [
        path('admin/', admin.site.urls),
        path('', include('chat.urls'))
    ]
                

    VIEWS

  • Ahora necesitaremos nuestra vista para la autenticación de usuarios y para enviar los datos necesarios de estos a nuestros templates
  • Para ello vamos al archivo views.py dentro de nuestra app chat y le introducimos el siguiente código:
  • 
    from django.shortcuts import render
    from .forms import FormLogin
    from django.contrib.auth import authenticate, login
    
    def user_login(request):
        if request.method == 'POST':
            form = FormLogin(request.POST)
            if form.is_valid():
                username = form.cleaned_data['username']
                password = form.cleaned_data['password']
                user = authenticate(username=username, password=password)
                if user is not None:
                    login(request, user)
                    return redirect('chat-page')
    
        else:
            form = FormLogin()
        return render(request, 'login.html', {'form': form})
    
    def home(request):
        context={}
        return render(request, 'chatPage.html', context=context)
                
    Ver Explicación

    Forms

  • Luego creamos nuestro formulario para capturar las credenciales de nuestros usuarios y validarlas
  • Para esto crearemos un archivo llamado forms.py dentro de nuestra app chat con el siguiente código:
  • 
    from django import forms
    
    class FormLogin(forms.Form):
        username = forms.CharField(max_length=100, label="Username")
        password = forms.CharField(label="Password", widget=forms.PasswordInput)
                
    Ver Explicación

    ROUTING

    Ya estuvimos hablando antes sobre el archivo routing del cual estábamos importando la lista websocket_urlpatterns en nuestro asgi.py

  • Primero que nada este archivo definirá las rutas de nuestra app en su forma asíncorna y en este caso está definiendo nuestras rutas para websocket
  • Ahora vamos a crear un archivo con el nombre de routing.py dentro de nuestra app chat y le introducimos el siguiente código:
  • 
    from django.urls import path
    from .consumers import ChatConsumer
    
    websocket_urlpatterns = [
        path('', ChatConsumer.as_asgi()),
    ]
                
    Ver Explicación

    CONSUMERS

    Si te habrás dado cuenta la ruta de websocket definida dentro del archivo routing.py tenía asociada una clase llamada ChatConsumer dicha clase será la encargada de manejar toda nuestra lógica para el chat por parte del servidor

  • Vamos a crear nuestro archivo consumers.py dentro de nuestra app chat
  • La estructura de este estará compuesta por una Clase controladora llamada ChatConsumer y dentro de ella varias funciones asíncronas
  • 
    import json
    from channels.generic.websocket import AsyncWebsocketConsumer
    
    
    class ChatConsumer(AsyncWebsocketConsumer):
    	async def connect(self):
    		self.roomGroupName = "Together"
    		await self.channel_layer.group_add(
    			self.roomGroupName ,
    			self.channel_name
    		)
    		await self.accept()
    		
    	async def disconnect(self , close_code):
    		await self.channel_layer.group_discard(
    			self.roomGroupName , 
    			self.channel_name 
    		)
    		
    	async def receive(self, text_data):
    		text_data_json = json.loads(text_data)
    		message = text_data_json["message"]
    		username = text_data_json["username"]
    		await self.channel_layer.group_send(
    			self.roomGroupName,{
    				"type" : "sendMessage" ,
    				"message" : message , 
    				"username" : username ,
    			})
    		
    	async def sendMessage(self , event) : 
    		message = event["message"]
    		username = event["username"]
    		await self.send(text_data = json.dumps({"message":message ,"username":username}))
                
    Ver Explicación

    TEMPLATES

  • Ahora necesitaremos nuestras plantillas HTML para mostrar todo el contenido y para ello usaremos el sistema de Templates de Django.
  • Creamos una carpeta llamada templates dentro de nuestra app chat y dentro crearemos los siguientes archivos: loginPage.html y chatPage.html
  • loginPage.html

    
    {% load static %}
    <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <link rel="stylesheet" href="{% static 'css/styles.css' %}">
      <title>Login</title>
    </head>
    <body>
        <div style="display: flex;
        flex-direction: column;
        align-items: center;" class="box">
        <center><h1 style="margin-top: 40%;">Chat App</h1></center>
        <center><form style="height: 90%;" method="post">
          {% csrf_token %}
          {{ form.as_p }}
          <button type="submit">Login</button>
        </form></center>
      </div>
    </body>
    </html>
                

    chatPage.html

    
    {% load static %}
    <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <link rel="stylesheet" href="{% static 'css/styles.css' %}">
      <title>Home</title>
    </head>
    <body>
    
        <div class="box">
            <nav class="topbar">
                <h1>Let's Chat</h1>
                {% if request.user.is_autenticated %}
                <a href = "{% url 'logout-user' %}">Logout</a>
                {% endif %}
            </nav>
    
            <div class="caja" id="caja">
    
            </div>
    
            <div class="enviar">
                <input type="text" id="input-enviar" />
                <button type="submit" id="btn-enviar">Enviar</button>
            </div>
        </div>
        
    <script>
        const mySocket = new WebSocket("ws://" + window.location.host + "/");
    mySocket.onopen = function (e) {
        console.log("La conexión se estableció correctamente !");
        
    };
    mySocket.onclose = function (e) {
        console.log("¡Algo inesperado sucedió !");
    };
    
    document.querySelector("#input-enviar").focus();
    document.querySelector("#input-enviar").onkeyup = function (e) {
        if (e.keyCode == 13) {
            document.querySelector("#btn-enviar").click();
        }
    };
    
    document.querySelector("#btn-enviar").onclick = function (e) {
        var mensaje = document.querySelector("#input-enviar").value;
        mySocket.send(JSON.stringify({ message: mensaje, username : "{{request.user.username}}" }));
    };
    
    mySocket.onmessage = function (e) {
    const data = JSON.parse(e.data);
    var div = document.createElement("div");
    div.style.wordWrap = "anywhere";
    
    div.innerHTML = data.username + " : " + data.message;
    document.querySelector("#input-enviar").value = "";
    document.querySelector("#caja").appendChild(div);
    
    let contenedor = document.getElementById('caja');
    contenedor.scrollTop = contenedor.scrollHeight;
    };
    </script>
    
    </body>
    </html>
                
    Ver Explicación

    ARCHIVOS ESTATICOS

  • Ahora necesitamos estilar nuestro html y para esto vamos a utilizar CSS, primero necesitamos crear nuestra carpeta static dentro de nuestra app chat y dentro de ella creamos nuestro archivo styles.css dentro del cual vamos a introducir el siguiente código, si desean pueden modificarlo o hacerlo por ustedes mismos
  • 
    /*Estilos Globales*/
    html{
        scroll-behavior: smooth;
    }
    
    body{
        height: 100vh;
        margin: 0;
        background-color: rgb(24, 126, 126);
        display: flex;
        flex-direction: column;
        justify-content: center;
        align-items: center;
        font-family: 'Roboto Regular',sans-serif;
    }
    
    h1{
        color: #fff;
        margin-left: 20px;
    }
    
    a{
        text-decoration: none;
        color: #b8b0b0;
        font-size: 16px;
        margin-right: 20px;
    }
    
    input{
        border: none;
        border-radius: 10px;
        padding: 3px;
    }
    
    input:focus{
        outline: none;
    }
    
    button{
        border: none;
        border-radius: 10px;
    }
    
    button:hover{
        cursor: pointer;
        background-color: #304244;
        color: #fff;
    }
    
    /*Estilos del login*/
    label{
        color: #fff;
    }
    
    form p{
        margin-top: 30px;
    }
    
    form input{
        height: 20px;
    }
    
    form button{
        width: 55%;
        height: 30px;
        font-size: 18px;
        margin: 10% 0 0 12%;
    }
    
    /*Estilos de clases*/
    .box{
        display: flex;
        flex-direction: column;
        width: 70%;
        height: 60%;
        background: linear-gradient(rgb(24, 126, 126) 20%, rgb(48, 194, 194) 80%);
        box-shadow: 0 10px 15px 6px rgba(0, 0, 0, 0.3);
    }
    
    .topbar{
        width: 100%;
        height: 20%;
        display: flex;
        flex-direction: row;
        justify-content: space-between;
        align-items: center;
    }
    
    .caja{
        width: 100%;
        height: 300px;
        max-height: 300px;
        overflow-y: auto;
        display: flex;
        flex-direction: column;
        align-items: flex-start;
        justify-content: flex-start;
        scroll-behavior: smooth;
        margin-left: 40px;
    }
    
    .caja div{
        padding: 15px;
        display: flex;
        flex-direction: column;
        justify-content: center;
        align-items: flex-start;
        width: 50%;
        height: auto;
        border-top-right-radius: 5px;
        border-top-left-radius: 15px;
        border-bottom-right-radius: 5px;
        background-color: aqua;
        box-shadow: 0 6px 6px 0 rgba(0, 0, 0, 0.3);
        color: #000000;
        margin-top: 10px;
    }
    
    .enviar{
        display: flex;
        flex-direction: row;
        justify-content: space-between;
        align-items: center;
        width: 100%;
        height: 10%;
    }
    
    /*Estilos a elementos*/
    #input-enviar{
        margin-left: 20px;
        width: 80%;
        height: 50%;
        padding: 3px;
    }
    
    #btn-enviar{
        margin-right: 20px;
        width: 12%;
        height: 65%;
    }
                

    Esto es solo los pasos para poder crear tu aplicación de chat en tiempo real bastante básico y fácil de entender si ya posees los requisitos que deje en la parte superior de esta sección.
    Si deseas ver la explicación más a fondo y detallada sobre este contenido no dudes en visitar mi video de YouTube sobre este contenido: Link del video

    PREVIEW

    arrow-back

    Stream Content

    arrow-back

    Tu Primera API REST

    Código Completo

    En este proyecto estaremos explicando paso por paso lo necesario para crear una API con la arquitectura REST para esto estaremos utilizando el módulo Django Rest Framework.

    Importante:Acá solo estaremos haciendo el rol de desarrollo BACKEND por lo tanto no estaremos renderizando templates.

    API - Application Programming Interface
    REST - Representational State Transfer

    Dato del Autor: Cada uno de los pasos de este tutorial y su explicación a fondo esta disponible en mi canal de YouTube
    Link del video

    Recomendaciones:

  • Si nunca has visto o hecho una API usando arquitectura REST y no sabes la manera en que se trabaja es de vital importancia que veas el video de la explicación acerca de esto. Apuesto a que después podrás entender todo con mejor claridad; Debajo dejo el link del video

  • Ver Explicación
  • Si aún estas comenzando en Django y notas que este tutorial no lo estás entendiendo y necesitas más conocimientos sobre el framework te recomiendo que hagas los demás tutoriales sobre Django Básico como el del proyecto TO DO List donde está explicado paso por paso todo para empezar con el framework o si eres de los que necesita escuchar y ver ejemplos prácticos para entender mejor puedes ver mi video de introducción en Django, te dejo el link debajo
  • Ver Explicación

    Requisitos para este proyecto:

  • Conocimientos de Python
  • Conocimientos básicos de Django
  • Saber crear entornos de desarrollo con Python
  • Conocimiento básico de la arquitectura REST
  • CONTENIDO:

  • Configuración

  • Models

  • Serializers

  • Views

  • Urls

  • Probando Peticiones

  • Preview

  • Instalación y Configuración

  • Primero que nada creamos nuestro entorno, instalamos django en el y creamos nuestro proyecto
  • Luego vamos a instalar en nuestro entorno el paquete de rest framework de la siguiente manera:
  • pip install djangorestframework

  • Luego necesitamos crear una app llamada api y para ello vamos a ejecutar el siguiente comando:
  • py manage.py startapp api_rest

    "Por favor asegurarse en todo momento de tener el entorno activado"

  • Luego nos dirigmos a las INSTALLED_APPS dentro de nuestro archivo settings.py en la carpeta del proyecto e instalaremos nuestra app creada y además el módulo rest_framework agregándolos al final de la lista como se muestra a continuación:
  • 
    INSTALLED_APPS = [
        '............',
        'django.contrib.staticfiles',
    
        'rest_framework',
        'api_rest',
    ]
                

    MODELS

  • Luego creamos nuestro modelo para establecer la estructura de nuestra api en la base de datos en este caso estaremos utilizando la tabla producto como ejemplo
  • Lo haremos de la siguiente manera iremos a nuestro archivo models.py dentro de nuestra app api_rest y le introducimos lo siguiente:
  • 
    from django.db import models
    
    class Producto(models.Model):
        nombre= models.CharField(max_length= 50, null=False, blank=False, verbose_name='nombre')
        precio= models.DecimalField(decimal_places= 2, null= False, blank= False, verbose_name='precio')
        cantidad = models.IntegerField(null= False, blank= False, verbose_name= 'cantidad')
    
    
        class Meta:
            verbose_name= 'Producto'
            verbose_name_plural= 'Productos'
            db_table= 'producto'
    
    
        def __str__(self):
            data = "{0} {1} {2}"
            return data.format(self.nombre, self.precio, self.cantidad)
                
    Ver Explicación
  • Ahora necesitamos crear nuestras migraciones para nuestro modelo y para ello abrimos nuestra terminal y nos aseguramos siempre de que nuestro entorno este activo en todo momento y ejecutamos el comando:
  • py manage.py makemigrations api_rest

  • Ya estan lista nuestras migraciones para ser migradas a la base de datos y para esto ejecutamosel comando:
  • py manage.py migrate

  • Ahora registraremos nuestro modelo en el panel de administracion de Django para poder realizar acciones en el desde ahí y lo haremos de la siguiente manera; Nos ubicamos en el archivo admin.py dentro de nuestra app api_rest y dentro de el colocamos lo siguiente:
  • 
    from django.contrib import admin
    from .models import Producto
    
    admin.site.register(Producto)
                
    Ver Explicación

    Ya tenemos nuestro modelo creado y registrado en la base de datos y visible en el panel de administración

    SERIALIZERS

    La información desde un puerto a otro no puede viajar en forma de objeto python por lo que necesitaremos serializar estos objetos para poder enviar la respuesta a nuestra peticiones en formato JSON "JavaScript Object Notation"

    Importante saber que no es el unico formato de objeto que existe para transferencia de datos sin embargo es el mas utilizado.

  • Ahora vamos a crear nuestra clase serializadora para los objetos de nuestro modelo y lo haremos de la siguiente manera
  • Dentro de nuestra app api_rest creamos un archivo llamado serializers.py y dentro de este colocamos el siguiente código:
  • 
    from rest_framework.serializers import ModelSerializer
    from .models import Producto
    
    class ProductoSerializer(ModelSerializer):
        class Meta:
            model= Producto
            fields= '__all__'
                
    Ver Explicación
  • Con esto definimos el modelo que queremos serializar dentro de nuetra clase serializadora y establecemos que esto sucederá con todos los campos de nuestro modelo
  • VIEWS

    Ahora necesitaremos manejar la lógica de nuestras peticiones y respuestas y como trataremos los datos

  • Para ello ubicamos el archivo views.py dentro de nuestra app api_rest y colocamos el siguiente código:
  • Importante acá este ejemplo es bastante básico, por lo que les recomiendo mucho ver el video de explicación ya que esta lógica para tratar la información tiene muchas maneras diferentes y mas óptimas de hacerse y con diferentes métodos, en el video de explicación explico y muestro algunas de ellas y muestro ejemplos.

    
    from rest_framework.response import Response
    from .models import Producto
    from .serializers import ProductoSerializer
    from rest_framework.decorators import api_view
    
    # Función para obtener nuestros productos
    @api_view(['GET'])
    def obtener(request):
        producto = Producto.objects.all()
        serializer = ProductoSerializer(producto, many = True)
        return Response(serializer.data)
    
    # Función para crear un nuevo producto
    @api_view(['POST'])
    def crear(request):
        serializer = ProductoSerializer(data= request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data)
        return Response({"message":"Ops hay algo mal en la petición"})
    
    # Función para actualizar un producto existente
    @api_view(['PUT'])
    def actualizar(request, pk):
        data = Producto.objects.get(id=pk)
        serializer = ProductoSerializer(data=request.data, instance=data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data)
        return Response({"message":"Ops hay algo mal en la petición"})
    
    # Función para eliminar un producto
    @api_view(['DELETE'])
    def eliminar(request, pk):
        data = Producto.objects.get(id=pk)
        data.delete()
        return Response ({"message": "Se eliminó correctamente"})
                
    Ver Explicación

    URLS

  • Ahora necesitaremos crear las rutas que serán los endpoints donde apuntarán las peticiones para ejecutar las funciones que antes estuvimos creando
  • Para ellos creamos dentro de nuestra app api_rest un archivo llamado urls.py y dentro de el introducimos el siguiente código:
  • 
    from django.urls import path
    from .views import *
    
    urlpatterns = [
        path('api/', obtener, name='obtener'),
        path('api/post/', crear, name='crear'),
        path('api/put/<int:pk>/', actualizar, name='actualizar'),
        path('api/delete/<int:pk>/', eliminar, name='eliminar')
    ]
                
    Ver Explicación
  • Luego necesitaremos registrar las rutas de nuestra api en las patrón de rutas principal de nuestro proyecto y debería quedar de la siguiente manera:
  • 
    from django.contrib import admin
    from django.urls import path, include
    
    urlpatterns = [
        path('admin/', admin.site.urls),
        path('',include('api_rest.urls'))
    ]
                
    Ver Explicación

    THUNDER CLIENT

  • Necesitamos una herramienta para interactuar con nuestra api de manera mas cómoda existen varias alguna de las mas famosas Thunder Client(extensión de Vs Code), Postman y RapidApi
  • Por razones de gusto personal uso Thunder Client, pero son libres de elegir y utilizar la que deseen
  • Para la la Instalación e Interacción con nuestra API REST desde Thunder Client ver el video que les dejo en el siguiente link:

    Ver Video

    PREVIEW

    Calculadora

    Código Completo

    En esta sección estaremos realizando una calculadora con Python utilizando la librería Tkinter

    Importante: No se preocupen por la explicación de cada una de las lineas de este código porque se que muchas cosas acá puede que no les suenen familiar como las funciones lambdas por lo que el video de este tutorial esta disponible gratis en mi canal de YouTube y les explico el ejercicio paso por paso

    Video Completo

    Requisitos para este proyecto:

  • Conocimientos básicos de Python
  • Primeramente creamos nuestra carpeta de proyecto y la abrimos con nuestro editor de texto en mi caso yo utilizo VS Code y dentro de la carpeta creamos nuestro archivo llamado calculadora.py
  • Luego dentro del archivo que acabamos de crear introducimos el siguiente código:
  •                 
    from tkinter import *
    
    ventana = Tk()
    ventana.title("Calculadora")
    i = 0
    
    # Entradas
    e_texto = Entry(ventana, font = ("Calibri 20"))
    e_texto.grid(row=0, column=0, columnspan=4, padx=50, pady=5)
    
    # Funciones
    def clickBoton(valor):
        global i
        e_texto.insert(i, valor)
        i+=1
    
    def borrar():
        e_texto.delete(0,END)
        i = 0
    
    def operacion():
        ecuacion= e_texto.get()
        resultado = eval(ecuacion)
        e_texto.delete(0, END)
        e_texto.insert(0,resultado)
        i = 0
    
    # Botones
    boton1 = Button(ventana, text="1", width=7, height=2, command= lambda: clickBoton(1))
    boton2 = Button(ventana, text="2", width=7, height=2, command= lambda: clickBoton(2))
    boton3 = Button(ventana, text="3", width=7, height=2, command= lambda: clickBoton(3))
    boton4 = Button(ventana, text="4", width=7, height=2, command= lambda: clickBoton(4))
    boton5 = Button(ventana, text="5", width=7, height=2, command= lambda: clickBoton(5))
    boton6 = Button(ventana, text="6", width=7, height=2, command= lambda: clickBoton(6))
    boton7 = Button(ventana, text="7", width=7, height=2, command= lambda: clickBoton(7))
    boton8 = Button(ventana, text="8", width=7, height=2, command= lambda: clickBoton(8))
    boton9 = Button(ventana, text="9", width=7, height=2, command= lambda: clickBoton(9))
    boton0 = Button(ventana, text="0", width=20, height=2, command= lambda: clickBoton(0))
    
    botonBorrar = Button(ventana, text="AC", width=7, height=2,foreground= "White",background="Red", command= lambda: borrar())
    botonParentesis1 = Button(ventana, text="(", width=7, height=2, command= lambda: clickBoton("("))
    botonParentesis2 = Button(ventana, text=")", width=7, height=2, command= lambda: clickBoton(")"))
    botonPunto = Button(ventana, text=".", width=7, height=2, command= lambda: clickBoton("."))
    
    botonSumar = Button(ventana, text="+", width=7, height=2, command= lambda: clickBoton("+"))
    botonRestar = Button(ventana, text="-", width=7, height=2, command= lambda: clickBoton("-"))
    botonMult = Button(ventana, text="x", width=7, height=2, command= lambda: clickBoton("*"))
    botonDiv = Button(ventana, text="/", width=7, height=2, command= lambda: clickBoton("/"))
    
    botonIgual = Button(ventana, text="=", width=7, height=2, command= lambda: operacion())
    
    # Agregar Botones en Pantalla
    botonBorrar.grid(row = 1, column= 0, padx= 5, pady= 5)
    botonParentesis1.grid(row = 1, column= 1, padx= 5, pady= 5)
    botonParentesis2.grid(row = 1, column= 2, padx= 5, pady= 5)
    botonDiv.grid(row = 1, column= 3, padx= 5, pady= 5)
    
    boton7.grid(row = 2, column= 0, padx= 5, pady= 5)
    boton8.grid(row = 2, column= 1, padx= 5, pady= 5)
    boton9.grid(row = 2, column= 2, padx= 5, pady= 5)
    botonMult.grid(row = 2, column= 3, padx= 5, pady= 5)
    
    boton4.grid(row = 3, column = 0, padx = 5, pady = 5)
    boton5.grid(row = 3, column = 1, padx = 5, pady = 5)
    boton6.grid(row = 3, column = 2, padx = 5, pady = 5)
    botonRestar.grid(row = 3, column = 3, padx = 5, pady = 5)
    
    boton1.grid(row = 4, column = 0, padx =5, pady = 5)
    boton2.grid(row = 4, column = 1, padx =5, pady = 5)
    boton3.grid(row = 4, column = 2, padx =5, pady = 5)
    botonSumar.grid(row = 4, column = 3, padx =5, pady = 5)
    
    boton0.grid(row = 5, column = 0,columnspan = 2, padx = 5, pady =5)
    botonPunto.grid(row = 5, column = 2, padx = 5, pady =5)
    botonIgual.grid(row = 5, column = 3, padx = 5, pady =5)
    
    
    
    
    ventana.mainloop()
                    
                

    PREVIEW

    arrow-back

    Aplicación de Inventario

    Código Completo

    En este tutorial estaremos aprendiendo a hacer una aplicación de escritorio con python usando la librería PyQt para la interfaz gráfica de usuario y MySql como sistema gestor de base de datos

    Ver Video

    Requisitos para este proyecto:

  • Conocimientos sólidos de Python
  • Conocimientos previos sobre POO
  • Conocimientos básicos de SQL
  • CONTENIDO

  • Instalación de PyQt
  • Diseño de Interfaz Gráfica
  • Clase Controladora
  • Base de Datos SQL
  • Preview
  • INSTALACION

    Primero que nada necesitaremos un proyecto de Python y un entorno una vez creados estos procederemos a activar nuestro entorno y e instalar PyQt en él, y para ellos necesitamos ejecutar el siguiente comando en nuesra terminal:

    pip install PyQt5

  • Luego vamos a instalar el conector de mysql para python que necesitamos para la conexión y lo haremos con el siguiente comando:
  • pip install mysql-connector-python

  • Ya hemos instalado todo lo necesario referente a la parte de nuestro proyecto en Python, ahora necesitamos instalar MySql en nuestro dispositivo y crear nuestra base de datos
  • Para ellos vamos a la página oficial de MySql presionando AQUI
  • Procedemos a descargar el que se encuentra señalado en la foto:
  • Luego abrimos el instalador de MySql en linea por lo que debemos estar conectados a internet luego procedemos a agregar a nuestra lista de instalación lo que deseamos en el caso de este proyecto usamos mysql 8.0.36 por lo que seleccionamos dentro de mysql servers la version 8.0.36 y la herramienta MySql Workbench 8.0.36 la cual estaremos utilizando para gestionar nuestra base de datos y nuestro servidor local:
  • En este caso no me aparecen a mi esas versiones porque ya las tengo instaladas pero simplemente es seleccionar las dos mysql server 8.0.36 y Workbench 8.0.36 y en cada una tocar la flecha verde del medio para agregarlos a nuestra lista de instalación y continuar siguiendo los pasos recomendados de instalación en ambos casos

  • Para ver la explicación paso por paso de como crearemos nuestro servidor local y nuestra base de datos relacional por favor ver el video de explicación ya que en texto plano es complicado explicar este contenido:
  • Ver Video

    Interfaz Gráfica de Usuario

    Para crear la interfaz gráfica estaremos utilizando la herramienta Qt Designer al ser muy complicado explicar paso por paso el trabajo en ella mediante texto en este blog pueden ver el video tutorial dedicado especificamente a crear esta interfaz en esa herramienta en muy poco tiempo alrededor de 10 min les tomará ver el video y seguir los pasos que voy haciendo desde descargar e instalar Qt Designer hasta consumir de él cargándolo en nuestro main.py:

    Ver Video
  • Dentro de nuestro proyecto crearemos un archivo llamado main.py donde tendremos nuestra interfaz gráfica y lo que haremos será cargar el archivo ui con el método loadUi de la librería PyQt5 en lugar de convertirlo a .py, para ello debemos tener dentro de nuestra carpeta raíz del proyecto el archivo .ui generado en la herramienta Qt Designer y te los dejo debajo para que lo descargues; dentro de nuestro main.py introducimos el siguiente código
  • Descargar UI
    
    import sys
    from turtle import width
    from PyQt5.QtWidgets import QApplication, QMainWindow, QHeaderView
    from PyQt5.QtCore import QPropertyAnimation, QEasingCurve
    from PyQt5 import QtCore, QtWidgets
    from PyQt5.uic import loadUi
    from conexion import Comunicacion
    
    class VentanaPrincipal(QMainWindow):
        def __init__(self):
            super(VentanaPrincipal, self).__init__()
            loadUi('inventario_gui.ui', self)
    
            self.menu.clicked.connect(self.mover_menu)
            self.inventario = Comunicacion()
    
            #funciones de los botones de la ventana
            self.cerrar.clicked.connect(lambda: self.close())
    
            #elimina barra de titulo -opacidad
            self.setWindowFlag(QtCore.Qt.FramelessWindowHint)
            self.setWindowOpacity(1)
    
            #funciones botones del menu lateral
            self.btn_registrar.clicked.connect(lambda: self.stackedWidget.setCurrentWidget(self.pagina_registrar))
            self.btn_todo.clicked.connect(lambda: self.stackedWidget.setCurrentWidget(self.pagina_todo))
            self.btn_registrarRes.clicked.connect(lambda: self.stackedWidget.setCurrentWidget(self.pagina_registrarRes))
            self.btn_todoRes.clicked.connect(lambda: self.stackedWidget.setCurrentWidget(self.pagina_todoRes))
    
            #funciones de los botones de las paginas
            self.btn_agregar.clicked.connect(self.insertar_productos)
            self.btn_agregarRes.clicked.connect(self.insertar_restaurante)
            self.btn_todo.clicked.connect(self.mostrar_productos)
            self.btn_todoRes.clicked.connect(self.mostrar_restaurantes)
    
            #ancho de columna adaptable
            self.tabla.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
    
            self.navbar.mouseMoveEvent = self.mover_ventana
            
            
        #mover ventana
        def mousePressEvent(self, event):
            self.click_position = event.globalPos()
    
        def mover_ventana(self, event):
            if event.buttons()== QtCore.Qt.LeftButton:
                self.move(self.pos() + event.globalPos() - self.click_position)
                self.click_position = event.globalPos()
                event.accept()
            if event.globalPos().y() <=5:
                self.showMaximized()
    
    
        #mover el menu lateral izquierdo
        def mover_menu(self):
            if True:
                width = self.sidebar.width()
                normal = 0
                if width == 0:
                    extender = 200
                else:
                    extender = normal
                self.animacion = QPropertyAnimation(self.sidebar, b'minimumWidth')
                self.animacion.setDuration(300)
                self.animacion.setStartValue(width)
                self.animacion.setEndValue(extender)
                self.animacion.setEasingCurve(QtCore.QEasingCurve.InOutQuart)
                self.animacion.start()
    
    
        def insertar_productos(self):
            nombre = self.input_producto.text().upper()
            cantidad = self.input_cantidad.text().upper()
            restaurante = self.combo.text().upper()
    
            if nombre != '' and cantidad != '' and restaurante != '' :
                self.inventario.insertar_productos(nombre, cantidad,restaurante)
                self.input_producto.clear()            
                self.input_cantidad.clear()
                self.combo.clear()
    
    
        def insertar_restaurante(self):
            nombre = self.input_nombreRes.text().upper()
    
            if nombre != '':
                self.inventario.insertar_restaurante(nombre)
                self.input_nombreRes.clear()
    
    
        def mostrar_productos(self):
            datos = self.inventario.mostrar_productos
            i = len(datos)
            self.tabla.setRowCount(i)
            tablerow = 0
            for row in datos:
                self.id = row[0]
                self.tabla.setItem(tablerow,0,QtWidgets.QTableWidgetItem(row[1])) 
                self.tabla.setItem(tablerow,1,QtWidgets.QTableWidgetItem(row[2])) 
                self.tabla.setItem(tablerow,2,QtWidgets.QTableWidgetItem(row[3])) 
                
                tablerow +=1
    
        def mostrar_restaurantes(self):
            datos = self.inventario.mostrar_restaurantes
            i = len(datos)
            self.tablaRes.setRowCount(i)
            tablerow = 0
            for row in datos:
                self.id = row[0]
                self.tablaRes.setItem(tablerow,0,QtWidgets.QTableWidgetItem(row[1])) 
                self.tablaRes.setItem(tablerow,1,QtWidgets.QTableWidgetItem(row[2])) 
                self.tablaRes.setItem(tablerow,2,QtWidgets.QTableWidgetItem(row[3])) 
                
                tablerow +=1
    
    
    
        def llenarComboBox(self):
            results = self.inventario.mostrar_restaurantes()
            for row in results:
                self.combo.addItem(row[0])
    
    
    
    if __name__ == "__main__":
        app = QApplication(sys.argv)
        mi_app = VentanaPrincipal()
        mi_app.show()
        sys.exit(app.exec_())
        
                
    Ver Explicación

    Clase Controladora

  • Ahora crearemos nuestra clase controladora llamada conexion.py donde estaremos conectando la base de datos con sus respectivos parámetros y definiremos las funciones con los scripts que enviaremos a nuestra base de datos a través del uso del cursor de nuestra librería mysql-connector-python que antes descargamos y para ello introduciremos el siguiente código dentro del archivo conexion.py que acabamos de crear:
  •                 
    import mysql.connector
    
    class Comunicacion():
    
        def __init__(self):    
            self.conexion = mysql.connector.connect(user = 'root', password =  '12345',
                                                host='localHost',
                                                database = 'inventario',
                                                port = '3306')
                                                
    
        def insertar_productos(self, nombre, cantidad, restaurante):
            cur = self.conexion.cursor()
            sql = ''' INSERT INTO product (NAME, QUANTITY,RESTAURANT)
            VALUES('{}', '{}', '{}')'''.format(nombre, cantidad, restaurante)
            cur.execute(sql)
            self.conexion.commit()
            cur.close()
    
        def insertar_restaurante(self, nombre):
            cur = self.conexion.cursor()
            sql = ''' INSERT INTO restaurant (NAME)
            VALUES('{}')'''.format(nombre)
            cur.execute(sql)
            self.conexion.commit()
            cur.close()
    
        def mostrar_productos(self):
            cursor = self.conexion.cursor()
            sql = "SELECT * FROM product"
            cursor.execute(sql)
            registro = cursor.fetchall()
            return registro
    
        def mostrar_restaurantes(self):
            cursor = self.conexion.cursor()
            sql = "SELECT * FROM restaurant"
            cursor.execute(sql)
            registro = cursor.fetchall()
            return registro
    
    
        def eliminar_productos(self,nombre):
            cur = self.conexion.cursor()
            sql = '''DELETE FROM product WHERE NOMBRE= {}'''.format(nombre)
            cur.execute(sql)
            self.conexion.commit()
            cur.close()
    
        def eliminar_restaurante(self,nombre):
            cur = self.conexion.cursor()
            sql = '''DELETE FROM restaurant WHERE NAME= {}'''.format(nombre)
            cur.execute(sql)
            self.conexion.commit()
            cur.close()
                    
                
    Ver Explicación

    Base de Datos MySQL

  • Luego necesitaremos crear nuestra base de datos y sus tablas y para ellos te dejo el script que debes ejecutar en MySql Workbench la herramienta que antes descargamos:
  •                 
    -- MySQL Script generated by MySQL Workbench
    -- Tue Feb  6 20:01:39 2024
    -- Model: New Model    Version: 1.0
    -- MySQL Workbench Forward Engineering
    
    SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0;
    SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0;
    SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION';
    
    -- -----------------------------------------------------
    -- Schema mydb
    -- -----------------------------------------------------
    -- -----------------------------------------------------
    -- Schema inventario
    -- -----------------------------------------------------
    
    -- -----------------------------------------------------
    -- Schema inventario
    -- -----------------------------------------------------
    CREATE SCHEMA IF NOT EXISTS `inventario` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci ;
    USE `inventario` ;
    
    -- -----------------------------------------------------
    -- Table `inventario`.`restaurant`
    -- -----------------------------------------------------
    CREATE TABLE IF NOT EXISTS `inventario`.`restaurant` (
      `id` INT NOT NULL AUTO_INCREMENT,
      `name` VARCHAR(45) NOT NULL,
      PRIMARY KEY (`id`))
    ENGINE = InnoDB;
    
    
    -- -----------------------------------------------------
    -- Table `inventario`.`product`
    -- -----------------------------------------------------
    CREATE TABLE IF NOT EXISTS `inventario`.`product` (
      `id` INT NOT NULL AUTO_INCREMENT,
      `name` VARCHAR(45) NOT NULL,
      `quantity` INT NOT NULL,
      `restaurant_id` INT NOT NULL,
      PRIMARY KEY (`id`),
      INDEX `fk_product_restaurant_idx` (`restaurant_id` ASC) VISIBLE,
      CONSTRAINT `fk_product_restaurant`
        FOREIGN KEY (`restaurant_id`)
        REFERENCES `inventario`.`restaurant` (`id`)
        ON DELETE NO ACTION
        ON UPDATE NO ACTION)
    ENGINE = InnoDB;
    
    
    SET SQL_MODE=@OLD_SQL_MODE;
    SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS;
    SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS;
    
                    
                
  • Corremos nuestro main.py y como resultado tendremos nuestra aplicación de escritorio y debería verse de la siguiente manera:
  • PREVIEW

    arrow-back

    Evento de Chef

    Código Completo

    En este proyecto estaremos realizando una aplicación de escritorio simulando el control de personal de un evento de cocina aficionada, para ellos utilizaremos PyQt para la interfaz gráfica y PostgreSQL como sistema gestor de base de datos

    Ver Video

    Requisitos para este proyecto:

  • Conocimientos sólidos de Python
  • Conocimientos previos sobre POO
  • Conocimientos básicos de PostgreSQL
  • CONTENIDO

  • Instalación de PyQt
  • Diseño de Interfaz Gráfica
  • Clase Controladora
  • Base de Datos SQL
  • Preview
  • INSTALACION

    Primero que nada necesitaremos crear la carpeta donde queremos crear nuestro proyecto y un entorno dentro de ella una vez creados estos procederemos a activar nuestro entorno y e instalar PyQt en él, y para ellos necesitamos ejecutar el siguiente comando en nuesra terminal:

    python -m venv nombre_del_entorno

    nombre_del_entorno\scripts\activate

    pip install PyQt5

  • Ahora necesitamos instalar nuestro gestor de base de datos que será Postgre y el conector de este para Python, para ellos haremos lo siguiente:
  • Iremos a la página oficial de PostgreSQL haciendo click AQUI y luego abrimos la terminal en nuestro proyecto para descargar la librería psycopg2 y lo haremos de la siguiente manera:
  • pip install psycopg2

    INTERFAZ GRAFICA DE USUARIO

    Luego de haber instalado todo lo necesario empezaremos con crear nuestra interfaz gráfica de usuario y para esto utilizaremos la herramienta Qt Designer

    En mi canal de YouTube esta el video completo del diseño de esta aplicación utilizando Qt Designer desde cero les dejo el link debajo:

    Ver Video

    Por lo pronto si desean pueden descargar el archivo .ui que contiene la interfaz de este tutorial ya creada

    Descargar UI
  • Luego vamos a crear las diferentes funciones de la aplicación y para eso vamos a crear un archivo llamado main.py y dentro introduciremos este código que explico paso por paso en mi video de YouTube, te dejare el link debajo del código:
  • 
    import sys
    import logo
    from turtle import width 
    from PyQt5.QtWidgets import QApplication, QMainWindow, QHeaderView
    from PyQt5.QtCore import QPropertyAnimation, QEasingCurve
    from PyQt5 import QtCore, QtWidgets
    from PyQt5.uic import loadUi
    from conexionA import Comunicacion
    
    class VentanaPrincipal(QMainWindow):
        def __init__(self):
            super(VentanaPrincipal, self).__init__()
            loadUi('Master Chef/Master Chef.ui', self)
    
            self.boton_menu.clicked.connect(self.mover_menu)
            self.database = Comunicacion()
    
            #oculta el boton minSize
            self.boton_minSize.hide()
        
    
            #funciones de los botones de la ventana
            #self.boton_minimizar.clicked.connect(self.control_boton_minimizar)
            self.boton_minSize.clicked.connect(self.control_boton_minSize)
            self.boton_maxSize.clicked.connect(self.control_boton_maxSize)
            self.boton_cerrar.clicked.connect(lambda: self.close())
    
            #elimina barra de titulo -opacidad
            self.setWindowFlag(QtCore.Qt.FramelessWindowHint)
            self.setWindowOpacity(1)
    
            #SizeGrip
            self.gripSize = 10
            self.grip = QtWidgets.QSizeGrip(self)
            self.grip.resize(self.gripSize, self.gripSize)
    
            #mover la ventana
            self.frame_superior.mouseMoveEvent = self.mover_ventana
    
            #funciones botones del menu lateral
    
            self.boton_organizador.clicked.connect(lambda: self.stackedWidget.setCurrentWidget(self.pagina_organizador))
            self.boton_registro.clicked.connect(lambda: self.stackedWidget.setCurrentWidget(self.pagina_registro))
            self.boton_chef.clicked.connect(lambda: self.stackedWidget.setCurrentWidget(self.pagina_chef))
            self.boton_patrocinador.clicked.connect(lambda: self.stackedWidget.setCurrentWidget(self.pagina_patrocinador))
            self.boton_asistente.clicked.connect(lambda: self.stackedWidget.setCurrentWidget(self.pagina_asistente))
            self.boton_Menu.clicked.connect(lambda: self.stackedWidget.setCurrentWidget(self.pagina_menu))
            self.boton_recetas.clicked.connect(lambda: self.stackedWidget.setCurrentWidget(self.pagina_receta))
            self.boton_ingrediente.clicked.connect(lambda: self.stackedWidget.setCurrentWidget(self.pagina_ingredientes))
            self.boton_clasificacion.clicked.connect(lambda: self.stackedWidget.setCurrentWidget(self.pagina_clasificacion))
            self.boton_contacto.clicked.connect(lambda: self.stackedWidget.setCurrentWidget(self.pagina_contacto))
            self.boton_premio.clicked.connect(lambda: self.stackedWidget.setCurrentWidget(self.pagina_premio))
    
            self.boton_organizador.clicked.connect(self.mostrar_elementos)
            self.boton_registro.clicked.connect(self.mostrar_elementos)
            self.boton_chef.clicked.connect(self.mostrar_elementos)
            self.boton_patrocinador.clicked.connect(self.mostrar_elementos)
            self.boton_asistente.clicked.connect(self.mostrar_elementos)
            self.boton_Menu.clicked.connect(self.mostrar_elementos)
            self.boton_recetas.clicked.connect(self.mostrar_elementos)
            self.boton_ingrediente.clicked.connect(self.mostrar_elementos)
            self.boton_clasificacion.clicked.connect(self.mostrar_elementos)
            self.boton_contacto.clicked.connect(self.mostrar_elementos)
            self.boton_premio.clicked.connect(self.mostrar_elementos)
    
    
            #funciones de los botones de registrar de las paginas
            self.btn_registrarOrg.clicked.connect(self.insertar_elementos)
            self.btn_registrarReg.clicked.connect(self.insertar_elementos)
            self.btn_registrarChef.clicked.connect(self.insertar_elementos)
            self.btn_registrarPatroc.clicked.connect(self.insertar_elementos)
            self.btn_registrarAsist.clicked.connect(self.insertar_elementos)
            self.btn_registrarMenu.clicked.connect(self.insertar_elementos)
            self.btn_registrarReceta.clicked.connect(self.insertar_elementos)
            self.btn_registrarIngred.clicked.connect(self.insertar_elementos)
            self.btn_registrarClasif.clicked.connect(self.insertar_elementos)
            self.btn_registrarContac.clicked.connect(self.insertar_elementos)
    
            # # Botones de Eliminar de las paginas
            self.btn_eliminarOrg.clicked.connect(self.eliminar_elemento)
            self.btn_eliminarReg.clicked.connect(self.eliminar_elemento)
            self.btn_eliminarChef.clicked.connect(self.eliminar_elemento)
            self.btn_eliminarPatroc.clicked.connect(self.eliminar_elemento)
            self.btn_eliminarAsist.clicked.connect(self.eliminar_elemento)
            self.btn_eliminarMenu.clicked.connect(self.eliminar_elemento)
            self.btn_eliminarReceta.clicked.connect(self.eliminar_elemento)
            self.btn_eliminarIngred.clicked.connect(self.eliminar_elemento)
            self.btn_eliminarClasif.clicked.connect(self.eliminar_elemento)
            self.btn_eliminarContac.clicked.connect(self.eliminar_elemento)
    
             #ancho de columna adaptable
            self.tabla_organizador.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
            self.tabla_registro.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
            self.tabla_chef.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
            self.tabla_patrocinador.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
            self.tabla_asistentes.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
            self.tabla_Menu.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
            self.tabla_receta.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
            self.tabla_ingredientes.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
            self.tabla_clasificacion.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
            self.tabla_contacto.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
            self.tabla_premio.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
    
        
        def control_boton_minSize(self):
            self.showNormal()
            self.boton_minSize.hide()
            self.boton_maxSize.show()
    
        def control_boton_maxSize(self):
            self.showMaximized()
            self.boton_maxSize.hide()
            self.boton_minSize.show()
    
        #mover ventana
        def mousePressEvent(self, event):
            self.click_position = event.globalPos()
    
        def mover_ventana(self, event):
            if self.isMaximized() == False:
                if event.buttons()== QtCore.Qt.LeftButton:
                    self.move(self.pos() + event.globalPos() - self.click_position)
                    self.click_position = event.globalPos()
                    event.accept()
            
            if event.globalPos().y() <=10:
                self.showMaximized()
                self.boton_maxSize.hide()
                self.boton_minSize.show()
            else:
                self.showNormal()
                self.boton_minSize.hide()
                self.boton_maxSize.show()
    
        #mover el menu lateral izquierdo
        def mover_menu(self):
            if True:
                width = self.frame_control.width()
                normal = 0
                if width == 0:
                    extender = 300
                else:
                    extender = normal
                self.animacion = QPropertyAnimation(self.frame_control, b'minimumWidth')
                self.animacion.setDuration(300)
                self.animacion.setStartValue(width)
                self.animacion.setEndValue(extender)
                self.animacion.setEasingCurve(QtCore.QEasingCurve.InOutQuart)
                self.animacion.start()
    
        # FUNCIONES 
    
        def mostrar_elementos(self):
            datos = self.database.mostrar_productos()
            i = len(datos)
            self.tabla_premio.setRowCount(i)
            tablerow = 0
            for row in datos:
                self.codigo = row[0]
                self.tabla_premio.setItem(tablerow,0,QtWidgets.QTableWidgetItem(row[1])) 
                self.tabla_premio.setItem(tablerow,1,QtWidgets.QTableWidgetItem(row[2])) 
                self.tabla_premio.setItem(tablerow,2,QtWidgets.QTableWidgetItem(row[3])) 
                self.tabla_premio.setItem(tablerow,3,QtWidgets.QTableWidgetItem(row[4])) 
                
                tablerow +=1
                
    
        def insertar_elementos(self):
            nombre = self.entry_nombreOrg.text().upper()
            apellido1 = self.entry_apellidoOrg.text().upper()
            apellido2 = self.entry_apellido2Org.text().upper()
            nacimiento = self.entry_fechaOrg.text().upper()
            edad = self.entry_edadOrg.text().upper()
            supervisor = self.entry_supervOrg.text().upper()
    
            if nombre != '' and apellido1 != '' and apellido2 != '' and nacimiento != '' and edad != '' and supervisor != '' :
                self.naodatabase.insertar_productos(nombre, apellido1, apellido2, nacimiento, edad, supervisor)
                self.entry_nombreOrg.clear()            
                self.entry_apellidoOrg.clear()
                self.entry_apellido2Org.clear()
                self.entry_fechaOrg.clear()
                self.entry_edadOrg.clear()
                self.entry_supervOrg.clear()
              
            else:
                self.signal_registro.setText('Hay espacios vacios')
    
    
    
        def eliminar_elemento(self):
            self.row_flag = self.tabla_eliminar.currentRow()
            if self.row_flag == 0:
                self.tabla_eliminar.removeRow(0)
                self.naodatabase.eliminar_productos("'"+ self.producto_a_borrar +"'")
                self.signal_eliminar.setText('Producto Eliminado')            
    
    
    if __name__ == "__main__":
        app = QApplication(sys.argv)
        mi_app = VentanaPrincipal()
        mi_app.show()
        sys.exit(app.exec_())
        
                
    Ver Video

    CLASE CONTROLADORA

  • Ahora crearemos nuestra clase controladora que será la encargada de manejar la lógica de la conexión entre nuestra base de datos Postgre y nuestra Interfaz Gráfica, para ellos creamos un nuevo archivo en nuestro proyecto llamado conexion.py y dentro de el introducimos el siguiente código:
  • Es importante aclarar que en los parámetros del método connect de la clase psycopg2 debes sustituirlos por las credenciales correctas que definas en tu base de datos en Postgre, por lo que les recomiendo ver el video tutorial completo de esta sección si es que tienen dudas ya que les explicaré paso a paso con palabras y asi entenderán mejor

    
    import psycopg2
    
    class Comunicacion():
    
        def __init__(self):    
            self.conexion = psycopg2.connect("dbname=nombre_de_tu_BaseDeDatos user = tu_usuario password = tu_contraseña")
                                                
    
        def insertar_productos(self, nombre, apeellido1, apellido2, nacimiento, edad, supervisor):
            cur = self.conexion.cursor()
            sql = ''' INSERT INTO organizador (NOMBRE_ORG, APELLIDO_1, APELLIDO_2, FECHA_NAC, EDAD_ORG, IS_SUPERVISOR)
            VALUES('{}', '{}', '{}', '{}', '{}', '{}', '{}')'''.format(nombre, apeellido1, apellido2, nacimiento, edad, supervisor)
            cur.execute(sql)
            self.conexion.commit()
            cur.close()
    
        def mostrar_productos(self):
            cursor = self.conexion.cursor()
            sql = "SELECT * FROM premio"
            cursor.execute(sql)
            registro = cursor.fetchall()
            return registro
    
        def eliminar_productos(self,nombre):
            cur = self.conexion.cursor()
            sql = '''DELETE FROM inventario WHERE CODIGO or PRODUCTO = {}'''.format(nombre)
            cur.execute(sql)
            self.conexion.commit()
            cur.close()
                

    Base De Datos

  • Ahora necesitamos abrir la herramienta PgAdmin y dentro de un nuevo Query Tab ejecutamos el siguiente script:
  • 
    -- This script was generated by the ERD tool in pgAdmin 4.
    -- Please log an issue at https://redmine.postgresql.org/projects/pgadmin4/issues/new if you find any bugs, including reproduction steps.
    BEGIN;
    
    
    CREATE TABLE IF NOT EXISTS public.asistente
    (
        id_asistente serial NOT NULL,
        nombre_asistente character varying(100) COLLATE pg_catalog."default" NOT NULL,
        apellido_1 character varying(50) COLLATE pg_catalog."default" NOT NULL,
        apellido_2 character varying(50) COLLATE pg_catalog."default" NOT NULL,
        edad_asistente integer NOT NULL,
        is_confirm boolean NOT NULL,
        codigo_registro_registro integer NOT NULL,
        CONSTRAINT asistente_pk PRIMARY KEY (id_asistente)
    );
    
    CREATE TABLE IF NOT EXISTS public.chef
    (
        id_chef serial NOT NULL,
        nombre_chef character varying(50) COLLATE pg_catalog."default" NOT NULL,
        apellido_1 character varying(50) COLLATE pg_catalog."default" NOT NULL,
        apellido_2 character varying(50) COLLATE pg_catalog."default" NOT NULL,
        edad_chef integer NOT NULL,
        codigo_registro integer NOT NULL,
        CONSTRAINT chef_pk PRIMARY KEY (id_chef)
    );
    
    CREATE TABLE IF NOT EXISTS public.clasificacion
    (
        id_clasificacion serial NOT NULL,
        nombrechef character varying(50) COLLATE pg_catalog."default" NOT NULL,
        apellido1 character varying(50) COLLATE pg_catalog."default" NOT NULL,
        apellido2 character varying(50) COLLATE pg_catalog."default" NOT NULL,
        plato_popular character varying(50) COLLATE pg_catalog."default" NOT NULL,
        puesto_clasif character varying(10) COLLATE pg_catalog."default" NOT NULL,
        codigo_registro integer NOT NULL,
        id_chef_chef integer NOT NULL,
        CONSTRAINT "Clasificacion_pk" PRIMARY KEY (id_clasificacion)
    );
    
    CREATE TABLE IF NOT EXISTS public.contacto_organizador
    (
        id_organizador_organizador serial NOT NULL,
        telefono character varying(20) COLLATE pg_catalog."default" NOT NULL,
        email character varying(50) COLLATE pg_catalog."default",
        CONSTRAINT contacto_organizador_pk PRIMARY KEY (id_organizador_organizador)
    );
    
    CREATE TABLE IF NOT EXISTS public.ingrediente
    (
        codigo_ingredient serial NOT NULL,
        nombre_producto character varying(100) COLLATE pg_catalog."default" NOT NULL,
        cantidad_ing integer NOT NULL,
        unidad character varying(50) COLLATE pg_catalog."default",
        codigo_menu_menu integer NOT NULL,
        codigo_receta_receta integer NOT NULL,
        CONSTRAINT ingredientes_pk PRIMARY KEY (codigo_ingredient)
    );
    
    CREATE TABLE IF NOT EXISTS public.many_registro_has_many_organizador
    (
        codigo_registro_registro integer NOT NULL,
        id_organizador_organizador integer NOT NULL,
        CONSTRAINT many_registro_has_many_organizador_pk PRIMARY KEY (codigo_registro_registro, id_organizador_organizador)
    );
    
    CREATE TABLE IF NOT EXISTS public.menu
    (
        codigo_menu serial NOT NULL,
        cant_platos integer NOT NULL,
        id_chef_chef integer NOT NULL,
        id_producto integer NOT NULL,
        CONSTRAINT menu_pk PRIMARY KEY (codigo_menu, id_producto),
        CONSTRAINT menu_uq UNIQUE (id_chef_chef)
    );
    
    CREATE TABLE IF NOT EXISTS public.organizador
    (
        id_organizador serial NOT NULL,
        nombre_org character varying(50) COLLATE pg_catalog."default" NOT NULL,
        apellido_1 character varying(50) COLLATE pg_catalog."default" NOT NULL,
        apellido_2 character varying(50) COLLATE pg_catalog."default" NOT NULL,
        fecha_nac date NOT NULL,
        edad_org integer NOT NULL,
        is_supervisor boolean NOT NULL,
        CONSTRAINT organizador_pk PRIMARY KEY (id_organizador)
    );
    
    CREATE TABLE IF NOT EXISTS public.patrocinador
    (
        id_patrocinador serial NOT NULL,
        nombre_patroc character varying(100) COLLATE pg_catalog."default" NOT NULL,
        apellido_1 character varying(50) COLLATE pg_catalog."default" NOT NULL,
        apellido_2 character varying(50) COLLATE pg_catalog."default" NOT NULL,
        edad_patroc integer NOT NULL,
        codigo_registro_registro integer NOT NULL,
        CONSTRAINT patrocinador_pk PRIMARY KEY (id_patrocinador)
    );
    
    CREATE TABLE IF NOT EXISTS public.receta
    (
        codigo_receta serial NOT NULL,
        nombre character varying(100) COLLATE pg_catalog."default" NOT NULL,
        tipo_receta character varying(50) COLLATE pg_catalog."default" NOT NULL,
        codigo_menu_menu integer NOT NULL,
        CONSTRAINT receta_pk PRIMARY KEY (codigo_receta)
    );
    
    CREATE TABLE IF NOT EXISTS public.registro
    (
        codigo_registro serial NOT NULL,
        cant_chef integer NOT NULL,
        cant_patroc integer NOT NULL,
        cant_asistentes integer NOT NULL,
        plato_popular character varying(100) COLLATE pg_catalog."default" NOT NULL,
        ingred_popular character varying(100) COLLATE pg_catalog."default" NOT NULL,
        CONSTRAINT codigo_registro PRIMARY KEY (codigo_registro)
    );
    
    ALTER TABLE IF EXISTS public.asistente
        ADD CONSTRAINT registro_fk FOREIGN KEY (codigo_registro_registro)
        REFERENCES public.registro (codigo_registro) MATCH FULL
        ON UPDATE CASCADE
        ON DELETE SET NULL;
    
    
    ALTER TABLE IF EXISTS public.chef
        ADD CONSTRAINT codigo_registro FOREIGN KEY (codigo_registro)
        REFERENCES public.registro (codigo_registro) MATCH FULL
        ON UPDATE CASCADE
        ON DELETE SET NULL;
    
    
    ALTER TABLE IF EXISTS public.clasificacion
        ADD CONSTRAINT chef_fk FOREIGN KEY (id_chef_chef)
        REFERENCES public.chef (id_chef) MATCH SIMPLE
        ON UPDATE NO ACTION
        ON DELETE NO ACTION
        NOT VALID;
    
    
    ALTER TABLE IF EXISTS public.clasificacion
        ADD CONSTRAINT codigo_registro FOREIGN KEY (codigo_registro)
        REFERENCES public.registro (codigo_registro) MATCH SIMPLE
        ON UPDATE NO ACTION
        ON DELETE NO ACTION
        NOT VALID;
    
    
    ALTER TABLE IF EXISTS public.contacto_organizador
        ADD CONSTRAINT organizador_fk FOREIGN KEY (id_organizador_organizador)
        REFERENCES public.organizador (id_organizador) MATCH FULL
        ON UPDATE CASCADE
        ON DELETE SET NULL;
    CREATE INDEX IF NOT EXISTS contacto_organizador_uq
        ON public.contacto_organizador(id_organizador_organizador);
    
    
    ALTER TABLE IF EXISTS public.ingrediente
        ADD CONSTRAINT receta_fk FOREIGN KEY (codigo_receta_receta)
        REFERENCES public.receta (codigo_receta) MATCH FULL
        ON UPDATE CASCADE
        ON DELETE SET NULL;
    
    
    ALTER TABLE IF EXISTS public.many_registro_has_many_organizador
        ADD CONSTRAINT organizador_fk FOREIGN KEY (id_organizador_organizador)
        REFERENCES public.organizador (id_organizador) MATCH FULL
        ON UPDATE CASCADE
        ON DELETE RESTRICT;
    
    
    ALTER TABLE IF EXISTS public.many_registro_has_many_organizador
        ADD CONSTRAINT registro_fk FOREIGN KEY (codigo_registro_registro)
        REFERENCES public.registro (codigo_registro) MATCH FULL
        ON UPDATE CASCADE
        ON DELETE RESTRICT;
    
    
    ALTER TABLE IF EXISTS public.menu
        ADD CONSTRAINT chef_fk FOREIGN KEY (id_chef_chef)
        REFERENCES public.chef (id_chef) MATCH FULL
        ON UPDATE CASCADE
        ON DELETE SET NULL;
    CREATE INDEX IF NOT EXISTS menu_uq
        ON public.menu(id_chef_chef);
    
    
    ALTER TABLE IF EXISTS public.menu
        ADD FOREIGN KEY (codigo_menu)
        REFERENCES public.ingrediente (codigo_ingredient) MATCH SIMPLE
        ON UPDATE NO ACTION
        ON DELETE NO ACTION
        NOT VALID;
    
    
    ALTER TABLE IF EXISTS public.patrocinador
        ADD CONSTRAINT registro_fk FOREIGN KEY (codigo_registro_registro)
        REFERENCES public.registro (codigo_registro) MATCH FULL
        ON UPDATE CASCADE
        ON DELETE SET NULL;
    
    -- Creando la vista PREMIO (CHEF - REGISTRO - CLASIFICACION)
    CREATE VIEW PREMIO AS
    SELECT
        C.NOMBRE_CHEF,
        A.PLATO_POPULAR,
        G.PUESTO_CLASIF,
        A.CODIGO_REGISTRO
    FROM 
    (CHEF C JOIN REGISTRO A ON C.CODIGO_REGISTRO = A.CODIGO_REGISTRO)
    JOIN CLASIFICACION G ON C.CODIGO_REGISTRO = G.CODIGO_REGISTRO
    
    END;
                

    Luego de ejecutar el script se crearan todas las tablas, las relaciones entre ellas y la vista llamada Premio y ya estaría lista nuestra base de datos, si no sabes los pasos a seguir para crear correctamente la base de datos a traves del script puedes ver el video tutorial en mi canal de YouTube donde explico todos los pasos detalladamente:

    Ver Video

    PREVIEW

  • Finalmente nuestra aplicación de escritorio deberia verse de esta manera:
  • arrow-back

    Gestor Estadístico(Aplicación de Consola)

    Código Completo

    En este proyecto estaremos realizando un programa en C++ para principiantes, el cual trata de calcular estadísticas de los jugadores de un torneo de Dota2

    Ver Video
  • Primero que nada crearemos una carpeta y la abriremos con un IDE de C++ como Dev C++ o como en mi caso que estaré usando VS Code como editor de texto pero es importante tener instalada(s) la(s) extension(es) ya que es un editor de texto como antes dije y no un IDE de desarrollo en C++
  • Luego de instalar nuestra extension de soporte para C++ en VS Code debemos crear un archivo llamado dota.cpp y dentro de el incluir este código:
  • 
    #include<iostream>
        #include<conio.h>
        
        using namespace std;
        
        int DiaMasProductivo(int num, int num2){    //Funcion que devuelve el mayor valor de las partidas ganadas(1ra fila)
            int mayor =num;   // Se iguala la variable mayor al primer numero que entra en la fila
            if(num2>mayor){   //Y si el segundo numero que entra es mayor al anterior ,
                mayor=num2;   //la variable mayor toma el valor de dicho numero
            }
            return mayor;
        }
        
        int DiaMenosProductivo(int num, int num2){  //Funcion que devuelve el mayor valor de las partidas perdidas(2da fila)
            int mayor =num;  
            if(num2>mayor){   
                mayor=num2;  
            }
            return mayor;
        }
        
        
        int main(){
            int cant_partic,numero[3][8],filas=2,columnas=7; //Declarando las variables
            int mayor=0;
            int opc,SioNo;
            int totalwin=0;
            int promediowin=0;
            int totallose=0;
            int promediolose=0;
            
            cout<<"..........................BIENVENIDO AL CENTRO DE ESTADISTICA DEL X TORNEO NACIONAL DE DoTa2............................"<<endl;
            cout<<"\n";
            cout<<"\n";
            cout<<"A continuacion le pediremos la introduccion de la cantidad de jugadores acerca de los que desea saber: "<<endl;
            cout<<"\n";
        
            cout<<"Digite la cantidad de jugadores del torneo de videojuegos: ";//se le pide al usuario que introduzca la cantidad de partic. del torneo
            cin>cant_partic;
            
            
            for(int a=0;a<cant_partic;a++) //Ciclo que se repetiras mientras que a sea menor que la cantidad de jugadores
            {
        
            for(int i=1;i<=filas;i++) // Ciclo para que el usuario introduzca las partidas del jugador
            {
                if(i==1){                                              //Si se esta rellenando la fila 1 se le pedira al usuario solo las partidas ganadas
              cout<<"Introduzca la cantidad de partidas ganadas del jugador: \n";
             }else{                                                     //sino se le pediran las partidas perdidas
                 cout<<"Introduzca la cantidad de partidas perdidas del jugador: \n"; 
             }
            
              for(int j=1;j<=columnas;j++)        //el usuario rellenara primero todas las columnas que corresponde con los dias por cada una de las filas
              {
                  cout<<"[Dia "<<j<<" ]: ";   //Se muestra la palabra Dia con el valor de la iteracion de J en ese momento y se pide introducir el valor correspondiente
                  cin>numero[i][j];	       
              }
              cout<<"\n";
              
            }
            
            for(int i=1;i<=filas;i++){  //Se muestra la matriz antes rellenada por el usuario mediante un bucle anidado
               if(i==1){
                   cout<<"partidas ganadas:  "; // Se imprimira en consola partidas g o p segun la fila e la que se encuentre la iteracion
               }else{
                   cout<<"partidas perdidas: ";
               }
        
              for(int j=1;j<=columnas;j++){  //Se mustran en columnas los valores de la fila 1 y la 2
                  cout<<"    "<<numero[i][j];
                  cout<<"| ";
              }
              cout<<"\n";
              cout<<"\n";
            }
            cout<<"\n";
        
            cout<<"Desea conocer detalles sobre este jugador?"<<endl;
            cout<<"1- Si \n";
            cout<<"0- No \n";
            cout<<"\n";
            
            cin>SioNo;   //Se introduce 0 o 1 para dar respuesta negativa o positiva respectivamente
            
            if(SioNo==1){  //Si se responde positivamente el usuario accedera a un menu con opciones
             
             cout<<"....Opciones: \n";
             cout<<"1- Dia que mas partidas gano \n";
                            cout<<"2- Dia que mas partidas perdio \n";
                            cout<<"3- Cantidad total de partidas ganadas \n";
                            cout<<"4- Promedio de partidas ganadas en la semana \n";
                            cout<<"5- Cantidad total de partidas perdidas \n";
            cout<<"6- Promedio de partidas perdidas en la semana \n";
            cout<<"7- Salir \n";
            
            
                       for(opc=1;opc!=7;){
            cin>opc;  //El usuario debe digitar la opcion deseada
            switch(opc){  //Evalua los casos y se ejecuta el correspondiente a la opcion seleccionada
           
            case 1: 
            for(int x=1; x<=7; x++){          //Ciclo usado para comparar los valores de la funcion DiaMasProductivo y se muestre en consola el mayor valor de la fila 1 (PG)
               mayor=DiaMasProductivo(mayor,numero[1][x]);
           }
           
            for(int x=1; x<=7; x++){
                if(numero[1][x]==mayor){
                cout<<"El dia que mas partidas ganadas tuvo fue el dia ["<<x<<"]: " <<mayor<< " partidas\n";	
                }
            }   
              break;
           
            case 2:
             
             for(int x=1; x<=7; x++){   //Ciclo usado para comparar los valores de la funcion DiaMenosProductivo y se muestre en consola el mayor valor de la fila 2 (PP)
               mayor=DiaMenosProductivo(mayor,numero[2][x]);
           }
           
            for(int x=1; x<=7; x++){   
                if(numero[2][x]==mayor){ 
                cout<<"El dia que mas partidas perdio fue el dia ["<<x<<"]: " <<mayor<< " partidas\n";	
                }
            }   
             break;
            
            case 3: 
               for(int j=1;j<=7;j++){
             
                totalwin += numero[1][j];		
           
           }
           cout<<"El total de partidas ganadas en la semana fue: "<<totalwin<<endl;	
        
            break;
            
            case 4:
            
             promediowin+=totalwin/7;	
           cout<<"El promedio de partidas que gano en la semana fue: "<<promediowin<<endl;
           break;
           
           case 5:
             for(int i=2;i<=7;i++){
             
                totallose += numero[2][i];		
           
           }
           cout<<"El total de partidas perdidas en la semana fue: "<<totallose<<endl;	
        
            break;
            
            case 6:
               promediolose+=totallose/7;	
           cout<<"El promedio de partidas que perdio en la semana fue: "<<promediolose<<endl;
           break;
             
             case 7:
             break;
           }
          cout<<"\n";
          cout<<"\n";
         }
              
         }
         cout<<"\n";
         cout<<"\n";
           
        }
        
           
             cout<<"..........GRACIAS POR USAR NUESTRA PLATAFORMA..........  :)\n"; 
             cout<<"\n";
             cout<<"\n";
            
            getch();  //funcion que se encuentra en la libreria conio.h usada para usar el ejecutable de nuestro programa directamente 
            return 0;
         }
                        
                

    Sugiero ver el video de YouTube donde explico cada una de las líneas de este código por paso y detalladamente

    Ver Video

    PREVIEW

    Librerias Importantes

    arrow-back

    Json Web Token

    Json Web Token (JWT) es un estándar abierto (RFC 7519) que define un formato compacto y autónomo para transmitir de manera segura la información entre dos partes como un objeto JSON. Un JWT puede ser utilizado para autenticar usuarios y compartir información de forma segura entre el cliente y el servidor.

    En esta sección estaremos tocando especificamente la integración de JWT con el framework Django con el uso de la librería djangorestframework-simplejwt y acá te enseñaremos como.

    INSTALACION

  • Primero debemos instalar la librería en el entorno de desarrollo de nuestro proyecto a traves de pip
  • pip install djangorestframework-simplejwt

  • Si planeas hacer modificaciones de codificación o decodificación de tokens usando algoritmos criptográficos RSA y ECDSA necesitamos instalar ademas como requisito de la librería simple-jwt la librería de criptografía (RECOMENDABLE)
  • pip install djangorestframework-simplejwt[crypto]

    CONFIGURACION

  • Luego debemos configurar simple-jwt en nuestro proyecto Django de la siguiente manera:
  • 
    REST_FRAMEWORK = {
        ...
        'DEFAULT_AUTHENTICATION_CLASSES': (
            ...
            'rest_framework_simplejwt.authentication.JWTAuthentication',
        )
        ...
    }
                
  • Ademas en nuesras urls.py de nuestro proyecto o en cualquier app que desees usar la autenticación jwt debemos incluir lo siguiente:
  • 
    from rest_framework_simplejwt.views import (
        TokenObtainPairView,
        TokenRefreshView,
    )
    
    urlpatterns = [
        ...
        path('app/token/', TokenObtainPairView.as_view(), name='token_obtain_pair'),
        path('app/token/refresh/', TokenRefreshView.as_view(), name='token_refresh'),
        ...
    ]
                
  • Ahora incluiremos rest_framework_simplejwt dentro de la lista de aplicaciones instaladas en el settings.py de nuestro proyecto
  • 
    INSTALLED_APPS = [
        ...
        'rest_framework_simplejwt',
        ...
    ]
                
  • Ahora comprobaremos si esta funcionando de una manera sencilla levantamos el servidor de desarrollo y en la url escribimos la ruta exacta que le definimos a la vista TokenObtainPairView por ejemplo: http://127.0.0.1:8000/app/token/
  • Cuando el token de acceso tiene una corta duración de validez podemos utilizar el token de refresh para obtener un nuevo token de acceso a traves de la ruta http://127.0.0.1:8000/app/token/refresh/
  • Ahora en el settings.py de nuestro proyecto pegaremos estas configuraciones de simple-jwt, las cuales puedes modificar según las necesidades de tu proyecto
  • 
    from datetime import timedelta
    
    ....
    
    SIMPLE_JWT = {
        "ACCESS_TOKEN_LIFETIME": timedelta(minutes=5),
        "REFRESH_TOKEN_LIFETIME": timedelta(days=1),
        "ROTATE_REFRESH_TOKENS": False,
        "BLACKLIST_AFTER_ROTATION": False,
        "UPDATE_LAST_LOGIN": False,
    
        "ALGORITHM": "HS256",
        "SIGNING_KEY": settings.SECRET_KEY,
        "VERIFYING_KEY": "",
        "AUDIENCE": None,
        "ISSUER": None,
        "JSON_ENCODER": None,
        "JWK_URL": None,
        "LEEWAY": 0,
    
        "AUTH_HEADER_TYPES": ("Bearer",),
        "AUTH_HEADER_NAME": "HTTP_AUTHORIZATION",
        "USER_ID_FIELD": "id",
        "USER_ID_CLAIM": "user_id",
        "USER_AUTHENTICATION_RULE": "rest_framework_simplejwt.authentication.default_user_authentication_rule",
    
        "AUTH_TOKEN_CLASSES": ("rest_framework_simplejwt.tokens.AccessToken",),
        "TOKEN_TYPE_CLAIM": "token_type",
        "TOKEN_USER_CLASS": "rest_framework_simplejwt.models.TokenUser",
    
        "JTI_CLAIM": "jti",
    
        "SLIDING_TOKEN_REFRESH_EXP_CLAIM": "refresh_exp",
        "SLIDING_TOKEN_LIFETIME": timedelta(minutes=5),
        "SLIDING_TOKEN_REFRESH_LIFETIME": timedelta(days=1),
    
        "TOKEN_OBTAIN_SERIALIZER": "rest_framework_simplejwt.serializers.TokenObtainPairSerializer",
        "TOKEN_REFRESH_SERIALIZER": "rest_framework_simplejwt.serializers.TokenRefreshSerializer",
        "TOKEN_VERIFY_SERIALIZER": "rest_framework_simplejwt.serializers.TokenVerifySerializer",
        "TOKEN_BLACKLIST_SERIALIZER": "rest_framework_simplejwt.serializers.TokenBlacklistSerializer",
        "SLIDING_TOKEN_OBTAIN_SERIALIZER": "rest_framework_simplejwt.serializers.TokenObtainSlidingSerializer",
        "SLIDING_TOKEN_REFRESH_SERIALIZER": "rest_framework_simplejwt.serializers.TokenRefreshSlidingSerializer",
    }
                

    Importante: Antes de realizar cualquier modificación en estas configuraciones recomiendo una buena lectura de la documentación oficial de la librería para mas información que necesiten

    More Info

    Si deseas mas información detallada sobre el uso de JWT con Django configuraciones adicionales, interactuar en proyecto real con la autenticación de usuarios, customizar reclamos del token y mucho mas. Te invito a ver el video que tengo en mi canal de YouTube acerca del tema!

    Ver Video
    arrow-back

    Politicas CORS

    El intercambio de recursos de origen cruzado CORS, por sus siglas en inglés es un mecanismo de seguridad que permite a un servidor indicar cualquier dominio, esquema o puerto con un origen distinto del suyo desde el que un navegador debería permitir la carga de recursos.

    En esta seccion estaremos tratando temas referentes a la instalación de la librería
    django-cors-headers; desde como instalarla y configurarla en nuestro proyecto hasta manejar los permisos de Cross Origin Resource Sharing "CORS" en español Transferencia de Recursos de Origen Cruzado.

    INSTALACION

  • Para instalar la libreria que usaremos en nuestro proyecto usaremos el siguiente comando de pip:
  • pip install django-cors-headers

  • Luego de instalarla agregaremos la app corsheaders en nuestra lista de INSTALLED_APPS en el settings.py de nuestro proyecto:
  • 
    INSTALLED_APPS = [
    ...
    'corsheaders',
    ...
    ]
                
  • Luego debemos de incluir el middleware que propone corsheaders en nuestra lista de middleware de nuestro proyecto en settings.py:
  • 
    MIDDLEWARE = [
    'corsheaders.middleware.CorsMiddleware',
    'django.middleware.security.SecurityMiddleware',
      ...
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
    ]
                
  • Por favor asegurate de colocar el middleware de corsheaders encima de 'SecurityMiddleware'
  • Ahora empezaremos a utilizar algunas variables de entorno para habilitar o deshabilitar segun nuestras necesidades las transferencias de origen cruzado
  • Por ejemplo si quieres permitir que desde cualquier sitio se le haga una petición a tu servidor una vez esté en producción puedes utilizar la variable de entorno siguiente en el settings.py de nuestro proyecto:
  • 
    CORS_ALLOW_ALL_ORIGIN = True
                
  • Si en caso contrario no deseas esto solo cambias el valor de esta variable por False
  • 
    CORS_ALLOW_ALL_ORIGIN = False
                
  • En otro caso si deseas solo habilitar algunos dominios especificos seria de la siguiente manera:
  • 
    CORS_ALLOWED_ORIGINS = [
        "http://localhost:3000",
    ]
                
  • Finalmente con esto podemos hacer basicamente un manejo de permisos de peticiones a nuestro servidor
  • arrow-back

    WHITENOISE

    WhiteNoise es una biblioteca de Python que simplifica enormemente la tarea de servir archivos estáticos en aplicaciones web.

    Muchas veces cuando estamos intentando desplegar,es decir cuando la variable DEBUG en nuestro settings.py es False nuestras aplicaciones web creadas con Django tenemos problemas con la carga de nuestros estilos y demas archivos estáticos, pues aqui te muestro una solución bastante efectiva.

    INSTALACION

  • Primeramente necesitaremos instalar la biblioteca de whitenoise en nuestro entorno y lo haremos de la siguiente manera:
  • pip install whitenoise

  • Ahora necesitamos incluir el middleware de whitenoise en nuestra lista de MIDDLEWARE en settings.py
  • 
    MIDDLEWARE = [
        'django.middleware.security.SecurityMiddleware',
        ....
        'django.middleware.clickjacking.XFrameOptionsMiddleware',
        'whitenoise.middleware.WhiteNoiseMiddleware',
    ].
                
  • Luego necesitamos recuperar todos estos estilos y archivos estáticos que debemos tener definidos en la carpeta static de cada app que tenga nuestro proyecto; por lo que vamos a ejecutar lo siguiente en la terminal:
  • manage.py collectstatic

  • Luego de ejecutar esto se te creará en tu directorio raiz una carpeta llamada staticfiles que es donde se encuentra todos los estilos y contenido estático de todo nuestro proyecto
  • Si estas utilizando alguna plataforma de PAAS como render para desplegar tu aplicación o alguna otra de este tipo y estas conectando el despliegue desde un repositorio de GitHub; En ese caso no debes ignorar esta carpeta en el archivo .gitignore y asegurate de que se ha subido correctamente la carpeta staticfiles a tu repositorio remoto.