**Ir a:** *[Repositorio](https://gitea.kickto.net/jp.av.dev/intro_Django#user-content-django-wiki)*, *[Templates](https://gitea.kickto.net/jp.av.dev/intro_Django/wiki/1_DjangoTemplates#user-content-crear-proyecto)*, *[Admin Panel](https://gitea.kickto.net/jp.av.dev/intro_Django/wiki/3_Panel_Administrador#user-content-panel-de-administrador)* ------- ## Proyecto y Aplicacion(es) ### Modularización *ej. Proyecto1* ``` __________________________________ | Proyecto Tienda Online | | | | App1 App2 | | [Panel Control] [Stock] | | | | App3 App4 | | [Ventas] [Pagos] | | | | App5 App6 | | [Envíos] [Promos] | |__________________________________| ``` *ej. Proyecto2* ``` __________________________________ | Proy. Gestión Almacen | | | | App1 App2 | | [Proveedores] [Stock] | | | | App3 App4 | | [Ventas] [Pagos] | | | | App5 App6 | | [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 , ]> >>> >>> # 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') , ]> >>> ``` */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') >>> , ]> >>> # Select * ... where nombre='mesa' seccion='deco'; >>> Articulos.objects.filter(nombre='mesa', seccion='deco') ]> >>> # Para buscar por ej. un elemento con precio mayor a 100 desde la shell django >>> Articulos.objects.filter(precio__gte=100) ]> otros: __tle, __range, ...).order_by(precio o -precio para inverso) >>> Articulos.objects.filter(precio__range=(40,90)) >>> , , , ]> >>> >>> Articulos.objects.filter(precio__gte=50).order_by('precio') , , , ]> ``` ------- **Ir a:** *[Repositorio](https://gitea.kickto.net/jp.av.dev/intro_Django#user-content-django-wiki)*, *[Templates](https://gitea.kickto.net/jp.av.dev/intro_Django/wiki/1_DjangoTemplates#user-content-crear-proyecto)*, *[Admin Panel](https://gitea.kickto.net/jp.av.dev/intro_Django/wiki/3_Panel_Administrador#user-content-panel-de-administrador)* **Ir a:** *[Repositorio](https://gitea.kickto.net/jp.av.dev/intro_Django#user-content-django-wiki)*, *[Proyecto y apps (bd)](https://gitea.kickto.net/jp.av.dev/intro_Django/wiki/2_Apps%26BD#user-content-proyecto-y-aplicacion-es)*, *[Formularios](https://gitea.kickto.net/jp.av.dev/intro_Django/wiki/4_Formularios#user-content-formularios)* ------- ## Panel de Administrador ### Activo por defecto *settings.py* ``` ... INSTALLED_APPS = [ 'django.contrib.admin', ... ``` *urls.py* ``` ... urlpatterns = [ path('admin/', admin.site.urls), ... ``` [*enlace a admin*](http://192.168.0.4:8000/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*](http://192.168.0.4:8000/admin/gestionPedidos/clientes/add/) 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](https://gitea.kickto.net/jp.av.dev/intro_Django/raw/branch/master/TiendaOnline/gestionPedidos/img/admin-filtro-fecha.png) ### Cambiar Idioma *settings.py* ``` #LANGUAGE_CODE = 'en-us' LANGUAGE_CODE = 'es-CL' ``` - [LANGUAGE_CODE](http://www.i18nguy.com/unicode/language-identifiers.html) - [Django-doc](https://docs.djangoproject.com/en/3.1/ref/contrib/admin/) ### 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. ------- **Ir a:** *[Repositorio](https://gitea.kickto.net/jp.av.dev/intro_Django#user-content-django-wiki)*, *[Proyecto y apps (bd)](https://gitea.kickto.net/jp.av.dev/intro_Django/wiki/2_Apps%26BD#user-content-proyecto-y-aplicacion-es)*, *[Formularios](https://gitea.kickto.net/jp.av.dev/intro_Django/wiki/4_Formularios#user-content-formularios)* **Ir a:** *[Repositorio](https://gitea.kickto.net/jp.av.dev/intro_Django#user-content-django-wiki)*, *[Admin Panel](https://gitea.kickto.net/jp.av.dev/intro_Django/wiki/3_Panel_Administrador#user-content-panel-de-administrador)*, *[Envio de Mails](https://gitea.kickto.net/jp.av.dev/intro_Django/wiki/5_Envio_de_mails#user-content-envio-de-mails)* ------- ## Formularios [*Forms doc*](https://docs.djangoproject.com/en/3.1/topics/forms/) ### Creacion del formulario */TiendaOnline/gestionPedidos/templates/busqueda_prods.html* ``` Búsqueda de productos
``` ### 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* ```

Estás buscando {{query}}

{% if articulos %}

Encontrados : {{articulos|length}} artículos

    {% for articulo in articulos %}
  • {{articulo.nombre}}   {{articulo.seccion}}  ${{articulo.precio}}
  • {% endfor %}
{% else %}

Artículo no encontrado

{% endif %} ``` ### 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* ```

Formulario de contacto

{% csrf_token %}

Asunto:

Mail :

Mensaje:

``` ***{% csrf_token %}*** Protección contra [**CSRF**](https://docs.djangoproject.com/en/3.0/ref/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* ``` ...

Gracias por contactarnos

... ``` *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),` ... ``` ------- **Ir a:** *[Repositorio](https://gitea.kickto.net/jp.av.dev/intro_Django#user-content-django-wiki)*, *[Admin Panel](https://gitea.kickto.net/jp.av.dev/intro_Django/wiki/3_Panel_Administrador#user-content-panel-de-administrador)*, *[Envio de Mails](https://gitea.kickto.net/jp.av.dev/intro_Django/wiki/5_Envio_de_mails#user-content-envio-de-mails)* **Ir a:** *[Repositorio](https://gitea.kickto.net/jp.av.dev/intro_Django#user-content-django-wiki)*, *[Formularios](https://gitea.kickto.net/jp.av.dev/intro_Django/wiki/4_Formularios#user-content-formularios)*, *[Api Form](https://gitea.kickto.net/jp.av.dev/intro_Django/wiki/6_API_Forms#user-content-api-forms)* ------- ## 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](https://docs.djangoproject.com/en/dev/ref/settings/#email-host) [django.core.mail-doc](https://docs.djangoproject.com/en/3.0/topics/email/#module-django.core.mail) 🔸️***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") ``` ------- **Ir a:** *[Repositorio](https://gitea.kickto.net/jp.av.dev/intro_Django#user-content-django-wiki)*, *[Formularios](https://gitea.kickto.net/jp.av.dev/intro_Django/wiki/4_Formularios#user-content-formularios)*, *[Api Form](https://gitea.kickto.net/jp.av.dev/intro_Django/wiki/6_API_Forms#user-content-api-forms)* **Ir a:** *[Repositorio](https://gitea.kickto.net/jp.av.dev/intro_Django#user-content-django-wiki)*, *[Envio de Mails](https://gitea.kickto.net/jp.av.dev/intro_Django/wiki/5_Envio_de_mails#user-content-envio-de-mails)*, *[Proyecto Web Completo]()* ------- ## API Forms ### Creación de formularios con API Forms Simplifica la creación de formularios e incluye validacion. [*API Forms doc*](https://docs.djangoproject.com/en/3.1/ref/forms/api/) [*Indice Forms Api doc*](https://docs.djangoproject.com/en/3.1/ref/forms/#forms) 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) >>> ``` 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())

>>> ``` ej. como ***lista*** (unsorted list) ``` >>> print(miForm.as_ul())
  • >>> ``` **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* ``` ...

    Formulario de contacto

    {% if forms.errors %}

    Por favor revisa este campo

    {% endif %}
    {% csrf_token %} {{ form.as_table}}
    ... ``` ------- **Ir a:** *[Repositorio](https://gitea.kickto.net/jp.av.dev/intro_Django#user-content-django-wiki)*, *[Envio de Mails](https://gitea.kickto.net/jp.av.dev/intro_Django/wiki/5_Envio_de_mails#user-content-envio-de-mails)*, *[Proyecto Web Completo]()*