diff --git a/TiendaOnline/README.md b/TiendaOnline/README.md new file mode 100644 index 0000000..b87b360 --- /dev/null +++ b/TiendaOnline/README.md @@ -0,0 +1,970 @@ +**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]()* \ No newline at end of file