Crud En Django con Slug, 2022
Tags: django, crud, slug, tutorial
Published: 2021-12-20 19:13:55

Hoy vamos a crear un crud(Create, Read, Update, Delete) básico en el framework Django

**Requisito**
Tener Python instalado, en caso de no tenerlo descargue e instale desde su página: https://www.python.org/

**Instalación**
Comenzamos instalando el entorno virtual, para ello abrimos cmd(símbolo del sistema) e ingresamos el siguiente comando _pip install virtualenv_, una vez instalada de manera global comenzamos con los siguientes paso

1. Creamos una carpeta la cual contendrá el proyecto, en mi caso se llamara django-crud

2. Abrimos cmd y nos ubicamos en la carpeta creada

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hi54ozf079w3jkl724iy.JPG)
3. Creamos el entorno virtual con el comando _virtualenv env_, esto nos sirve para la instalación de dependencias que requiere este crud.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/n7m7pwcw9z54g7qme1lx.JPG)

4. Activamos el entorno virtual  con el comando _.\env\Scripts\activate_

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/58osj2epkacrai7o854o.JPG)

5. Instalamos Djando con el siguiente comando _pip install Django==4.0_

6. Creamos el proyecto con el siguiente comando _django-admin startproject NombreProyecto_, en mi caso se llamará mycrud


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/smi5oq2oh9ix9t67v7hr.JPG)
7. Accedemos ala carpeta del proyecto _cd mycrud
8. Creamos una aplicación con el comando _py manage.py startapp crud_
9. Levantamos el proyecto con el comando _py manage.py runserver_, accedemos ala web http://localhost:8000/

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/e44496si1vfycef08ic0.JPG)

**Configuración settings.py**
Abrimos nuestro proyecto en nuestro editor de texto Favorito, en mi caso estaré usando Visual Studio Code y abrimos el archivo _settings.py _ para configurar algunas opciones

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fna0dvga48lvqqxtocdx.JPG)

1. Definimos nuestra aplicación en **INSTALLED_APPS** agregando el nombre _'crud.apps.CrudConfig',_

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gu133mv64k9bebkdj63z.JPG)

**models.py**
Abrimos el archivo models.py y creamos nuestro modelo de la siguiente manera

```
from django.db import models
from django.utils.text import slugify

class Developer(models.Model):
    name = models.CharField(max_length=50,null=True)
    age = models.IntegerField(null=True,blank=True)
    skill = models.CharField(max_length=50,null=True)
    slug = models.SlugField(null=True, blank=True)

    class Meta:
        verbose_name = 'Developer'
        verbose_name_plural = 'Developers'
    
    def __str__(self):
        return self.name

    def save(self, *args, **kwargs):
        if self.slug == None:
            slug = slugify(self.name)

            has_slug = Developer.objects.filter(slug=slug).exists()
            count = 1
            while has_slug:
                count += 1
                slug = slugify(self.name) + '-' + str(count) 
                has_slug = Developer.objects.filter(slug=slug).exists()

            self.slug = slug
        super().save(*args, **kwargs)
```
    
En este modelo se puede observar sus 4 variables de registro como name, age, skill, slug, este último _slug_ es la variable que generara nombres únicos para cada registro el cual podrá remplazar al _id_

Creamos la migración del modelo con el comando _python manage.py makemigrations_


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mwddighevnkjrtxls9k4.JPG)

Y Migramos con el comando _python manage.py migrate_

**CRUD Ingresar views.py**
Para hacer el ingresar en Django nos situamos en archivo _views.py_ y ingresamos el siguiente código

```
from django.shortcuts import render, redirect
from .models import *
from django.contrib import messages

# CRUD DEVELOPERS

#------ Create ------

def createDev(request):
    name = request.POST['nameInput']
    age = request.POST['ageInput']
    skill = request.POST['skillInput']

    developer = Developer.objects.create(
        name=name, age=age, skill=skill)
    messages.success(request, 'User: ' + name +' ¡Save Success !')
    return redirect('/')
```
En este código se importan algunos elementos propios de Djando que facilitaran la creación del crud, además de mensajes tipo request para su interacción.

**urls.py**
Para direccionar las funciones de nuestra vista creamos un archivo con el nombre urls.py bajo la carpeta **crud** y ingresamos el siguiente código 

```
from django.urls import path
from . import views

urlpatterns = [
    
    # Urls Developers
    path('register/', views.createDev),
    
]
```
y en nuestra **urls.py** principal que esta baja la carpeta de _mycrud_ se tiene el código de la siguiente manera, misma que indica referencia ala dirección urls de la aplicación crud

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kx86ic622ityofej092v.JPG)

**Platilla**
una platilla es la base en la cual se trabajará el proyecto; Bajo la carpeta _crud_ creamos una carpeta llamada **templates** en cuál contendrá todos nuestros documentos html y creamos el archivo base.html
con la siguiente estructura

```
<!DOCTYPE html>
<html lang="en">

<head>
    {% load static %}
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>{% block title %} {% endblock %}</title>
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/css/bootstrap.min.css"
        integrity="sha384-TX8t27EcRE3e/ihU7zmQxVncDAy5uIKz4rEkgIXeMed4M0jlfIDPvg6uqKI2xXr2" crossorigin="anonymous">
    <link rel="stylesheet" href="{% static 'css/style.css' %}">
</head>

<body>
    <nav class="navbar navbar-expand-lg navbar-light bg-dark">
        <a class="navbar-brand" href="#" style="color: white">Developers</a>
        
    </nav>
    <div class="container py-4">
        {% block body %}

        {% endblock %}
    </div>
    <script src="https://code.jquery.com/jquery-3.5.1.slim.min.js"
        integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj" crossorigin="anonymous">
    </script>
    <script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.1/dist/umd/popper.min.js"
        integrity="sha384-9/reFTGAW83EW2RDu2S0VKaIzap3H66lZH81PoYlFhbGU+6BZp6G7niu735Sk7lN" crossorigin="anonymous">
    </script>
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/js/bootstrap.min.js"
        integrity="sha384-w1Q4orYjBQndcko6MimVbzY0tgp4pWB4lZ7lr30WKz0vr/aWKhXdBNmNb5D92v7s" crossorigin="anonymous">
    </script>
    <script src="{% static 'js/bntButton.js' %}"></script>
</body>

</html>
```
para los archivos css y javascript, bajo el archivo **crud ** se crea una carpeta de nombre static y se tiene los siguientes archivos:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2i336s9t84hwm708dqpd.JPG)
con la siguiente información de cada archivo

_style.css_

```
th,
td {
  text-align: center;
  vertical-align: middle;
}

```
_bntButton.js_

```
(function () {

    const btnDelete = document.querySelectorAll(".btnDelete");

    btnDelete.forEach(btn => {
        btn.addEventListener('click', (e) => {
            const confirmacion = confirm('¿Confirm to delete?');
            if (!confirmacion) {
                e.preventDefault();
            }
        });
    });
    
})();
```
**Formulario**
Nuestro formulario será la vista en la cual se podrá realizar el registro, para este ejemplar se ingresara desarrolladores o programadores con nombre, edad y habilidad o lenguaje de programación que maneja.
Para ello creamos un documento html en la carpeta **templates** en este caso se llamará _index.html_ con la siguiente información

```
{% extends "./base.html" %}

{% block title %} Form {% endblock %}

{% block body %}
<div class="row">
    <div class="col-sm-12 col-md-6 col-lg-4 col-xl-4">
        {% if messages %}
        {% for message in messages %}
        <div class="alert alert-dismissible alert-success">
            <button type="button" class="close" data-dismiss="alert">&times;</button>
            <strong class="text-dark">{{ message }}</strong>
        </div>
        {% endfor %}
        {% endif %}
        <h2>Form</h2>
        <div class="card">
            <div class="card-body">
                <form action="/register/" method="POST">{% csrf_token %}
                    <div class="form-group">
                        <input type="text" id="nameInput" name="nameInput" class="form-control" placeholder="Name"
                            required>
                    </div>
                    <div class="form-group">
                        <input type="number" id="ageInput" name="ageInput" class="form-control" placeholder="Age"
                            maxlength="50" required>
                    </div>
                    <div class="form-group">
                        <input type="text" id="skillInput" name="skillInput" class="form-control" min="1" max="9"
                        placeholder="Skill" required>
                    </div>
                    <div class="form-group">
                        <button type="submit" class="btn btn-info btn-block text-white">Save</button>
                    </div>
                </form>
            </div>
        </div>
    </div>

</div>
{% endblock %}
```
 
Levantamos el servicio en cmd _python manage.py runserver_
y tenemos el siguiente resultado

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jk9bsp8v7xwiaatg7t0b.png)

**Realizamos un registro**
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/aq99k04shtvwf9oqe07y.png)
**Resultado**
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xxy8ga4aqan0tfrxhbp8.png)

Ahora bien nos toca mostrar, editar y eliminar para ello realizamos los siguientes cambios en cada uno de los archivos que les menciona.
**views.py**

```
from django.shortcuts import render, redirect
from .models import *
from django.contrib import messages


# CRUD DEVELOPERS

#------ Create ------

def createDev(request):
    name = request.POST['nameInput']
    age = request.POST['ageInput']
    skill = request.POST['skillInput']

    developer = Developer.objects.create(
        name=name, age=age, skill=skill)
    messages.success(request, 'User: ' + name +' ¡Save Success !')
    return redirect('/')


#------ List ------
def listDev(request):
    developers = Developer.objects.all()
    return render(request, "index.html", {"developers": developers})


#------ Edit ------
def editDev(request, slug):
    developer = Developer.objects.get(slug=slug)

    name = request.POST.get('nameInput')
    age = request.POST.get('ageInput')
    skill = request.POST.get('skillInput')
    if request.method == 'POST':

        developer.name = name
        developer.age = age
        developer.skill = skill
        developer.save()
        messages.success(request, 'User: ' + name +' ¡Edit Success!')
        return redirect('/')
    
    return render (request, "edit.html", {"developer": developer})

#------ Delete ------
def deleteDev(request, slug):
    developer = Developer.objects.get(slug=slug)
    
    developer.delete()
    messages.success(request, '¡Developer Deleted!')
    return redirect('/')  
```
**urls.py** 
El que esta bajo la carpeta crud

```
from django.urls import path
from . import views

urlpatterns = [
    
    # Urls Developers
    path('', views.listDev),
    path('register/', views.createDev),
    path('edit/<slug:slug>', views.editDev, name='editUrl'),
    path('delete/<slug:slug>', views.deleteDev, name='deleteUrl'),
    
]
```
**index.html**

```
{% extends "./base.html" %}

{% block title %} Form {% endblock %}

{% block body %}
<div class="row">
    <div class="col-sm-12 col-md-6 col-lg-4 col-xl-4">
        {% if messages %}
        {% for message in messages %}
        <div class="alert alert-dismissible alert-success">
            <button type="button" class="close" data-dismiss="alert">&times;</button>
            <strong class="text-dark">{{ message }}</strong>
        </div>
        {% endfor %}
        {% endif %}
        <h2>Form</h2>
        <div class="card">
            <div class="card-body">
                <form action="/register/" method="POST">{% csrf_token %}
                    <div class="form-group">
                        <input type="text" id="nameInput" name="nameInput" class="form-control" placeholder="Name"
                            required>
                    </div>
                    <div class="form-group">
                        <input type="number" id="ageInput" name="ageInput" class="form-control" placeholder="Age"
                            maxlength="50" required>
                    </div>
                    <div class="form-group">
                        <input type="text" id="skillInput" name="skillInput" class="form-control" min="1" max="9"
                        placeholder="Skill" required>
                    </div>
                    <div class="form-group">
                        <button type="submit" class="btn btn-info btn-block text-white">Save</button>
                    </div>
                </form>
            </div>
        </div>
    </div>
    <div class="col-sm-12 col-md-6 col-lg-8 col-xl-8">
        <h1>Developer List</h1>
        <div class="table-responsive py-2">
            <table class="table table-striped table-hover">
                <thead class="bg-info">
                    <tr class="text-light">
                        <th>#</th>
                        <th>Name</th>
                        <th>Age</th>
                        <th>Skills</th>
                        <th colspan="2"> options</th>
                    </tr>
                </thead>
                <tbody>
                    {% for dev in developers %}
                    <tr>
                        <td>{{forloop.counter}}</td>
                        <td>{{dev.name}}</td>
                        <td>{{dev.age}}</td>
                        <td>{{dev.skill}}</td>
                        <td><a href="{%  url 'editUrl' dev.slug %}" class="btn btn-info">Edit</a></td>
                        <td><a href="{%  url 'deleteUrl' dev.slug %}" class="btn btn-danger btnDelete">Delete</a></td>
                    </tr>
                    {% endfor %}
                </tbody>
            </table>
        </div>
    </div>
</div>
{% endblock %}

```
Para el editar creamos otro documento html con el nombre de **edit.html**, esta bajo la carpeta templates y ingresamos el siguintes codigo

```
{% extends "./base.html" %}

{% block title %} Edit {% endblock %}

{% block body %}
<div class="row">
    <div class="col-md-4 offset-md-4">
        <h2>Edit</h2>
        <div class="card">
            <div class="card-body">
                <form action="{% url 'editUrl' developer.slug %}"  method="POST">
                    {% csrf_token %}
                    <div class="form-group">
                        <input type="text" id="nameInput" name="nameInput" value="{{developer.name}}" class="form-control" placeholder="Name"
                            required>
                    </div>
                    <div class="form-group">
                        <input type="number" id="ageInput" name="ageInput" value="{{developer.age}}" class="form-control" placeholder="Age"
                            maxlength="50" required>
                    </div>
                    <div class="form-group">
                        <input type="text" id="skillInput" name="skillInput" value="{{developer.skill}}" class="form-control"
                        placeholder="Skill"required>
                    </div>
                    <div class="form-group">
                        <button type="submit" class="btn btn-success btn-block text-white">Edit</button>
                    </div>
                </form>
            </div>
        </div>
    </div>
</div>
{% endblock %}
```
Con esos cambios ya se tiene el CRUD completo, a continuacion les muestro como queda.
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7liu77bty15vxbqsxxx4.png)

**Creamos uno**
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/o17duxwlc5l15t20purf.png)
  
**Editamos uno**
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nhhoedmcpt2kk3fdxcvu.png)
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jpacf5wzvb0ecv2p740f.png)
**Slug**
se puede ver lo que hace el slug que remplaza nuestro id por una valor unico en este caso el nombre, esto ayuda mucho para temas de seguridad ya que con esto se oculta el id ala que pertencese el registro
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/acjvm8wq0g4z848nnneb.png)
 

**Eliminamos uno**
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/h46kzy39gjnfda47jm8k.png)

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4t7uit24ehaikf3uguwn.png)
  
Espero que les ayude...
_Fuetes:_
https://docs.djangoproject.com/en/4.0/
https://www.youtube.com/watch?v=iQcJPeCcjNo&t=1585s
https://www.youtube.com/watch?v=uSbDMs7Y9yI&t=189s