intro_Django/TiendaOnline
2020-11-18 14:18:38 -03:00
..
gestionPedidos edit FormContacto atributos de campos 2020-11-15 19:15:22 -03:00
TiendaOnline +EMAIL_HOST 2020-11-15 03:47:54 -03:00
manage.py 1er nivel 2020-11-13 01:10:32 -03:00
README.md esteticos 2020-11-18 14:18:38 -03:00

Ir a: Repositorio, Wiki


Proyecto y Aplicacion(es)

Modularización

ej. Reutilización de webApps en dos proyectos

 __________________________________              __________________________________   
|      Proyecto Tienda Online      |            |      Proy. Gestión Almacen       |  
|                                  |            |                                  |  
|      App1           App2         |            |      App1           App2         |  
| [Panel Control]    [Stock]       |            | [Proveedores]      [Stock]       |  
|                                  |            |                                  |  
|      App3           App4         |            |      App3           App4         |  
|    [Ventas]        [Pagos]       |            |    [Ventas]        [Pagos]       |  
|                                  |            |                                  |  
|      App5           App6         |            |      App5           App6         |  
|    [Envíos]       [Promos]       |            |    [Envíos]       [Promos]       |  
|__________________________________|            |__________________________________|  


Recordar activar entorno virtual
🔸 source /home/sat/weblocal/.django-env/bin/activate

Creación de proyecto : django-admin startproject TiendaOnline
Cambiar a carpeta de proy.: cd /TiendaOnline
Ajustar TZ en settings.py : TIME_ZONE = 'America/Santiago'
Creación primera app : python3 manage.py startapp gestionPedidos

/TiendaOnline/gestionPedidos/

- migrations/
- admin.py
- apps.py
- __init__.py
- models.py
- tests.py
- views.py

SQLite3 (default)

/gestionPedidos/models.py

from django.db import models

class Clientes(models.Model):
    nombre = models.CharField(max_length=30)
    direccion = models.CharField(max_length=50)
    email = models.EmailField()
    fono = models.CharField(max_length=10)

class Articulos(models.Model):
    nombre = models.CharField(max_length=30)
    seccion = models.CharField(max_length=20)
    precio = models.IntegerField()

class Pedidos (models.Model):
    numero = models.IntegerField()
    fecha = models.DateField()
    entregado = models.BooleanField() 

settings.py

...
INSTALLED_APPS = [
    ...
    'gestionPedidos',
]
...

Chequear código :

python3 manage.py check gestionPedidos

Creación del modelo Base de Datos :

python3 manage.py makemigrations

# Ver instrucciones SQL
python3 manage.py slqmigrate gestionPedidos 0001

Creación de BD:

python3 manage.py migrate

🔸 acceder al 'shell de django': python3 manage.py shell

Insertar Registros

(InteractiveConsole)

>>> from gestionPedidos.models import Articulos
>>> 
>>> art = Articulos(nombre='mesa', seccion='decoracion', precio=96)
>>> art.save()
>>> 
>>> art2 = Articulos(nombre='camisa', seccion='vestuario', precio=25)
>>> art2.save()
>>> 
>>> art3 = Articulos.objects.create(nombre='taladro', seccion='ferreteria', precio=65)
>>> 

Actualizar Registros

>>> art.precio=99
>>> art.save()
>>> 

Borrar Registros

>>> art5 = Articulos.objects.get(id=2)
>>> art5.delete()
(1, {'gestionPedidos.Articulos': 1})
>>> 

Select

>>> ResConsulta = Articulos.objects.all()
>>> ResConsulta
<QuerySet [<Articulos: Articulos object (1)>, <Articulos: Articulos object (3)>]>
>>> 
>>> # Ver instrucción SQL:
>>> ResConsulta.query.__str__()
'SELECT "gestionPedidos_articulos"."id", "gestionPedidos_articulos"."nombre", "gestionPedidos_articulos"."seccion", "gestionPedidos_articulos"."precio" FROM "gestionPedidos_articulos"'
>>> 

PostgreSQL

Install

sudo apt install postgresql postgresql-contrib libpq-dev

Success. You can now start the database server using:

    pg_ctlcluster 11 main start


# Cambiar a user postgres
sudo -i -u postgres

# sql cli
postgres@sat:~$ psql

# salir
postgres=# \q

# Crear usuario *ambiente de desarrollo
postgres@sat:~$ createuser -P --interactive
Enter name of role to add: django
Shall the new role be a superuser? (y/n) y

# Crear BD con nombre de usuario 
(postgres automaticamente se conecta a un DB con el mismo nombre de usuario)
postgres@sat:~$ createdb django

# Crear password

postgres@ratsat:~$ psql
psql (11.9 (Raspbian 11.9-0+deb10u1))
Type "help" for help.

postgres=# \password django

postgres=# \q

postgres@ratsat:~$ psql -U django -W

Problemas con Log-in ?

sudo vim /etc/postgresql/11/main/pg_hba.conf

# CAMBIAR

# Database administrative login by Unix domain socket
local   all             postgres                               peer

por

# Database administrative login by Unix domain socket
local   all             postgres                                md5
# "local" is for Unix domain socket connections only
local   all             all                                     md5

# Reiniciar servicio
sudo service postgresql restart 

# Para borrar usuario
DROP OWNED BY your_user;
DROP USER your_user;

# Modificar
vim  /etc/postgresql/10/main/postgresql.conf
        ...
        listen_addresses = 'localhost,your-server-ip'
        ...

sudo vim /etc/postgresql/11/main/postgresql.conf
# IPv4 local connections:
host	all		all		192.168.0.0/24		md5

# Regla Firewall
sudo ufw allow proto tcp from 192.168.0.0/24 to any port 5432

Usar algun DBbrowser y crear bd ArticulosClientes

Instalar Python-Django PostgreSQL connector

pip3 install psycopg2

vim settings.py

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

ej.
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql_psycopg2',
        'NAME': 'articulosclientes',
        'USER': 'nombre_usuario',
        'PASSWORD': 'clave_usuario',
        'HOST': '127.0.0.1',
        'DATABASE_PORT': '5432',
    }
}

# Check
python3 manage.py check

# Hacer migraciones
python3 manage.py makemigrations

# Migrar
python3 manage.py migrate

Crear un registro

python3 manage.py shell

(InteractiveConsole)
>>> from gestionPedidos.models import Clientes
>>> 
>>> cli = Clientes(nombre='Pedro', direccion='dnd vive', email='pe@mail.ru', fono=123456789)
>>> cli.save()
>>> 

Valores en tabla articulos

id nombre seccion precio
1 mesa deco 90
2 lámpara deco 50
3 pantalón vestuario 45
4 destornillador ferreteria 5
5 balón deporte 25
6 raqueta deporte 105
7 muñeca juguetes 15
8 tren eléctrico juguetes 50

Intruccion SQL desde Django

# Select * ... where seccion='deporte';
>>> from gestionPedidos.models import Articulos
>>> 
>>> Articulos.objects.filter(seccion='deporte')
<QuerySet [<Articulos: Articulos object (5)>, <Articulos: Articulos object (6)>]>
>>> 

/TiendaOnline/gestionPedidos/models.py

class Articulos(models.Model):
    nombre = models.CharField(max_length=30)
    seccion = models.CharField(max_length=20)
    precio = models.IntegerField()
    
    def __str__(self):
        return 'Nombre: %s, Depto. %s, Precio $ %s' % (self.nombre, self.seccion, self.precio)
python3 manage.py makemigrations
python3 manage.py migrate
python3 manage.py shell
>>> from gestionPedidos.models import Articulos
>>> 
>>> Articulos.objects.filter(seccion='deporte')
>>> <QuerySet [<Articulos: Nombre: balón, Depto. deporte, Precio $ 25>,
               <Articulos: Nombre: raqueta, Depto. deporte, Precio $ 105>]>
>>> 


# Select * ... where nombre='mesa' seccion='deco';
>>> Articulos.objects.filter(nombre='mesa', seccion='deco')
<QuerySet [<Articulo: Nombre: mesa, Depto. deco, Precio $ 90>]>
>>> 

# Para buscar por ej. un elemento con precio mayor a 100 desde la shell django
>>> Articulos.objects.filter(precio__gte=100)
<QuerySet [<Articulos: Articulo: raqueta, Depto. deporte, Precio $ 105>]>

otros: __tle, __range, ...).order_by(precio o -precio para inverso) 

>>> Articulos.objects.filter(precio__range=(40,90))
>>> 
<QuerySet [<Articulos: Nombre: mesa, Depto. deco, Precio $ 90>, <Articulos: Nombre: lámpara, Depto. deco, Precio $ 50>, <Articulos: Nombre: pantalón, Depto. vestuario, Precio $ 45>, <Articulos: Nombre: tren eléctrico, Depto. juguetes, Precio $ 50>]>
>>>

>>> Articulos.objects.filter(precio__gte=50).order_by('precio')
<QuerySet [<Articulos: Articulo: lámpara, Depto. deco, Precio $ 50>, <Articulos: Articulo: tren eléctrico, Depto. juguetes, Precio $ 50>, <Articulos: Articulo: mesa, Depto. deco, Precio $ 90>, <Articulos: Articulo: raqueta, Depto. deporte, Precio $ 105>]>


Panel de Administrador

Activo por defecto

settings.py

...
INSTALLED_APPS = [
    'django.contrib.admin',
    ...

urls.py

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

enlace a admin

Crear SuperUsuario, con perfíl de administrador

python3 manage.py createsuperuser
Username (leave blank to use 'sat'): Roberrrt
Email address: admin@gitea.com
Password: 
Password (again): 
Superuser created successfully.

Explorando la BD

tabla auth_user

id password last_login is_superuser username first_name last_name email is_staff is_active date_joined
1 pbkdf2_sha256$216000$zw4Zuc6weyCN$Vwn8SM6zPA3hIofcmQiz4mIuU6tL7U/vvWs= 2020-11-13 02:40:20 true Roberrrt admin@gitea.com true true 2020-11-13 02:38:41
2 pbkdf2_sha256$216000$188Gs3iRpsWb$X49lPG/2IlRlsaanXqUBZ6YOMIvctZ+U0Gg= false Roberrrt2 false true 2020-11-13 02:41:43

Administrar tablas desde el Panel

admin.py

from django.contrib import admin
from gestionPedidos.models import Clientes

admin.site.register(Clientes)

Configurar campo 'email' como opcional, clase Cliente

models.py

...
class Clientes(models.Model):
    ...
    email = models.EmailField(blank=True, null=True)
    ...

cambios en el modelo, requieren migración
python3 manage.py makemigrations
python3 manage.py migrate

Personalización del Panel

Admin. Clientes Django por defecto presenta los nombres de los campos capitalizados y elimina el texto posterior a '_' ( ej. modelo nombre_clientes ).

Columnas en el Panel de admin.

Add clientes

Nombre:
Direccion:
Email:
Fono:

Modificar nombre visible de tablas

Personalizar nombre a mostrar en panel, en el Modelo. models.py

class Clientes(models.Model):
    ...
    direccion = models.CharField(max_length=50, verbose_name="La Direcc.:")
    ...

Vista Panel

Add clientes

Nombre:
La Direcc.:
Email:
Fono:

Ver otros campos de tablas a modificar en panel

gestionPedidos/admin.py

from django.contrib import admin
from gestionPedidos.models import Clientes, Articulos, Pedidos

class ClientesAdm(admin.ModelAdmin):
    list_display("nombre", "direccion", "fono")

admin.site.register(Clientes, ClientesAdmin)

vista en Panel

Nombre La Direcc. Fono
Elejendre calle 16 2445234234
Zerafín dnd vive 123456789
Pedro ruta 2 9873456789

Agregar campo de busqueda

gestionPedidos/admin.py

class ClientesAdmin(admin.ModelAdmin):
    list_display = ("nombre", "direccion", "fono")
    # Campos de busqueda en la barra
    search_fields = ("nombre","fono")

Agregar Filtros

admin.py

class ArticulosAdmin(admin.ModelAdmin):
    list_filter = ("seccion",)

vista filtro en panel

Filter by Secion
All
deco
deporte
ferreteria
jugetes
vestuario

Tambien se puede filtar por fecha
admin.py

class PedidosAdmin(admin.ModelAdmin):
    list_display = ("numero", "fecha")
    list_filter = ("fecha",)


admin.site.register(Pedidos, PedidosAdmin)

vista filtro en panel

Filter by Fecha
Any date
Today
Past 7 days
This month
This Year

Filtro de disposicion horizontal, estilo menú
admin.py

class PedidosAdmin(admin.ModelAdmin):
    list_display = ("numero", "fecha")
    list_filter = ("fecha",)
    # Filtro-barra*
    date_hierarchy = "fecha"

Vista del Panel de Administrador
img

Cambiar Idioma

settings.py

#LANGUAGE_CODE = 'en-us'
LANGUAGE_CODE = 'es-CL'

Agregar usuarios, perfiles

Usuarios STAFF, puede administrar el sitio desde el panel, se puede limitar.
Usuarios ACTIVO, puede entrar en partes del sitio que requieran autenticación.

Agregar grupos

Permite crear Grupos, con permisos específicos.
Se pueden agregar permisos extra pro usuario.


Formularios

Forms doc

Creacion del formulario

/TiendaOnline/gestionPedidos/templates/busqueda_prods.html

<html>
    <head>
        <title>Búsqueda de productos</title>
    </head>
    <body>
        <form action="/buscar/" method="GET">
            <input type="text" name="prod">
            <input type="submit" value="Buscar">
        </form>
    </body>
</html>

Creación vista formulario

gestionPedidos/views.py

from django.shortcuts import render

# Create your views here.
def busqueda_productos(request):
    return render(request, "busqueda_prods.html")

Restistrar url (path)

urls.py

...
from gestionPedidos import views

urlpatterns = [
    ....
    path('buscar_productos/', views.busqueda_productos),
    ....

Crear vista para el submit 'buscar'

gestionPedidos/views.py

...
from django.http import HttpResponse

...
def buscar(request):
    msj = "Estas búscando por: %r" %request.GET["prod"]
    return HttpResponse(msj)

Registrar url

urls.py

...

urlpatterns = [
    ...
        path('buscar/', views.buscar),
]

Metodo GET http://192.168.0.4:8000/buscar/?prod=alicate

Busqueda en BBDD

views.py

...
from gestionPedidos.models import Articulos

...
def buscar(request):
    # Validación campo vacio
    if request.GET["prod"]:
        #msj = "Estas búscando por: %r" %request.GET["prod"]
        prod_buscar = request.GET["prod"]
        articulos = Articulos.objects.filter(nombre__icontains=prod_buscar)
        return render(request, "resultado_busqueda.html", {"articulos":articulos, "query":prod_buscar})

    else:
        msj = "Debes introducir un termino de búsqueda"
    return HttpResponse(msj)

__icontains similar a like SQL, busca en el campo indicado,
articulos que CONTENGAN la palabra a buscar.

resultado_busqueda.html

<body>
    <p>Estás buscando <strong>{{query}}</strong></p>
    {% if articulos %}
        <p>Encontrados : {{articulos|length}} artículos</p>
        <ul>
            {% for articulo in articulos %}
                <li>{{articulo.nombre}} &nbsp; {{articulo.seccion}} &nbsp;${{articulo.precio}}</li>
            {% endfor %}
        </ul>
    {% else %}
        <p>Artículo no encontrado</p>
    {% endif %}
</body>

Limitar cantidad de caracteres en busqueda

views.py

...

def buscar(request):
    # Validación campo vacio
    if request.GET["prod"]:
        prod_buscar = request.GET["prod"]
        if len(prod_buscar) > 20:
            msj = "Termino de búsqueda demasiado largo"
        else:
            articulos = Articulos.objects.filter(nombre__icontains=prod_buscar)
            return render(request, "resultado_busqueda.html", {"articulos":articulos, "query":prod_buscar})
    ...

Formulario de contacto

views.py

contacto.html

    <body>
        <h1>Formulario de contacto</h1>
        <form action="/contacto/" method="POST">
        {% csrf_token %}
        
            <p>Asunto: <input type="text" name="asunto"></p>
            <p>Mail  : <input type="text" name="mail"></p>
            <p>Mensaje: </p>
            <p><textarea name="mensaje" rows="15" cols="45"></textarea></p>
            
            <input type="submit" value="Enviar">
        </form>
    </body>

{% csrf_token %} Protección contra CSRF
This should not be done for POST forms that target external URLs, since that would cause the CSRF token to be leaked, leading to a vulnerability.

gracias.html

...
<h1>Gracias por contactarnos</h1>
...

views.py

...
def contacto(request):
    if request.method == "POST":
        return render(request, "gracias.html")
    return render(request, "contacto.html")

urlpatterns urls.py

...
    path('contacto/', views.contacto),`  
...

Envio de Mails

Libreria core.mail

settings.py

EMAIL_BACKEND = 'django.core.imail.backends.smtp.EmailBackend'
EMAIL_HOST = 'smtp.gmail.com'
EMAIL_USE_TLS = True
EMAIL_PORT = 587
EMAIL_HOST_USER = 'ejemplo@gmail.com'
EMAIL_HOST_PASSWORD = 'clave-ejemplo'

settings-doc django.core.mail-doc

🔸python3 manage.py shell

(InteractiveConsole)
>>> from django.core.mail import send_mail
>>> 
>>> send_mail('Test Django Admin', 
              'Mensaje desde la consola de django',
              'webmaster@django.net',
              ['destinatario@mail.com'],
              fail_silently = False,
    )

Envio de mail desde formulario de contacto

views.py

...
from django.core.mail import send_mail
from django.conf import settings

...
def contacto(request):
    if request.method == "POST":
        subject = request.POST['asunto']
        message = request.POST['mensaje']+' '+request.POST['email']
        email_from = settings.EMAIL_HOST_USER
        recipient_list = ['ratablastard@gmail.com']
        send_mail(subject, message, email_from, recipient_list)
        return render(request, "gracias.html")
    return render(request, "contacto.html")

API Forms

Creación de formularios con API Forms

Simplifica la creación de formularios e incluye validacion.

API Forms doc
Indice Forms Api doc

Crear /TiendaOnline/gestionPedidos/forms.py

from django import forms

class FormContacto(forms.Form):
    asunto = forms.CharField(max_length=35, required=False, initial='Contacto')
    email  = forms.EmailField(max_length=35)
    msj    = forms.CharField(widget=forms.Textarea(attrs={'rows': 5, 'cols': 26}))

🔸python3 manage.py shell

(InteractiveConsole)
>>> from gestionPedidos.forms import FormContacto
>>> 
>>> miForm = FormContacto()
>>> 
>>> print(miForm)
<tr><th><label for="id_asunto">Asunto:</label></th><td><input type="text" name="asunto" required id="id_asunto"></td></tr>
<tr><th><label for="id_email">Email:</label></th><td><input type="email" name="email" required id="id_email"></td></tr>
<tr><th><label for="id_msj">Msj:</label></th><td><input type="text" name="msj" required id="id_msj"></td></tr>
>>> 

El formulario esta formateado como tabla.
Crea las etiquetas 'label', les da un nombre.
Crea los asuntos e inputs.
Por defecto estos son requeridos.

Cambiando el formato del formulario

ej. como parrafo

>>> print(miForm.as_p())
<p><label for="id_asunto">Asunto:</label> <input type="text" name="asunto" required id="id_asunto"></p>
<p><label for="id_email">Email:</label> <input type="email" name="email" required id="id_email"></p>
<p><label for="id_msj">Msj:</label> <input type="text" name="msj" required id="id_msj"></p>
>>> 

ej. como lista (unsorted list)

>>> print(miForm.as_ul())
<li><label for="id_asunto">Asunto:</label> <input type="text" name="asunto" required id="id_asunto"></li>
<li><label for="id_email">Email:</label> <input type="email" name="email" required id="id_email"></li>
<li><label for="id_msj">Msj:</label> <input type="text" name="msj" required id="id_msj"></li>
>>> 

Probando el formulario is_valid() y cleaned_data

>>> miForm = FormContacto({'asunto':'prueba', 'email':'test@mail.com', 'msj':'mensaje de prueba'})
>>> 
>>> miForm.is_valid()
True
>>> 
>>> miForm.cleaned_data
{'asunto': 'prueba', 'email': 'test@mail.com', 'msj': 'mensaje de prueba'}
>>> 

Campo email invalido

>>> miForm = FormContacto({'asunto':'prueba', 'email':'test@mailcom', 'msj':'mensaje de prueba'})
>>> 
>>> miForm.is_valid()
False
>>> 
>>> miForm.cleaned_data
{'asunto': 'prueba', 'msj': 'mensaje de prueba'}
>>> 

Cambiando views.py para usar el Api Forms

...
from gestionPedidos.forms import FormContacto

...
def contacto(request):
    if request.method == "POST":
        miForm = FormContacto(request.POST)
        if miForm.is_valid():
            contenido = miForm.cleaned_data
            
            send_mail(contenido['asunto'], contenido['msj'],
            contenido.get('email',''),['webmasterd@test.com'],)
            
            return render(request, 'gracias.html')
    else:
        miForm = FormContacto()
    
    return render(request, 'form_contacto.html', {"form":miForm})

templates/form_contacto.html

...
    <body>
        <h1>Formulario de contacto</h1>
        {% if forms.errors %}
            <p style="color:red;"> Por favor revisa este campo</p>
        {% endif %}
        <form action="" method="POST">{% csrf_token %}
            <table>
                {{ form.as_table}}
            </table>
            <input type="submit" value="Enviar">
        </form>
    </body>
...

Ir a: Repositorio, Wiki