You've already forked Curso-lenguaje-python
Restructure content and add notes from HolaMundo
Signed-off-by: Manuel Vergara <manuel@vergaracarmona.es>
This commit is contained in:
@@ -0,0 +1,4 @@
|
||||
from django.contrib import admin
|
||||
from .models import Tarea
|
||||
|
||||
admin.site.register(Tarea)
|
||||
@@ -0,0 +1,6 @@
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class BaseConfig(AppConfig):
|
||||
default_auto_field = 'django.db.models.BigAutoField'
|
||||
name = 'base'
|
||||
@@ -0,0 +1,30 @@
|
||||
# Generated by Django 4.0 on 2023-01-20 15:06
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
('auth', '0012_alter_user_first_name_max_length'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Tarea',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('titulo', models.CharField(max_length=200)),
|
||||
('descripcion', models.TextField(blank=True, null=True)),
|
||||
('completo', models.BooleanField(default=False)),
|
||||
('creado', models.DateTimeField(auto_now_add=True)),
|
||||
('usuario', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='auth.user')),
|
||||
],
|
||||
options={
|
||||
'ordering': ['completo'],
|
||||
},
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,20 @@
|
||||
from django.db import models
|
||||
from django.contrib.auth.models import User
|
||||
|
||||
|
||||
class Tarea(models.Model):
|
||||
usuario = models.ForeignKey(User,
|
||||
on_delete=models.CASCADE,
|
||||
null=True,
|
||||
blank=True)
|
||||
titulo = models.CharField(max_length=200)
|
||||
descripcion = models.TextField(null=True,
|
||||
blank=True)
|
||||
completo = models.BooleanField(default=False)
|
||||
creado = models.DateTimeField(auto_now_add=True)
|
||||
|
||||
def __str__(self):
|
||||
return self.titulo
|
||||
|
||||
class Meta:
|
||||
ordering = ['completo']
|
||||
@@ -0,0 +1,18 @@
|
||||
{% extends 'base/principal.html' %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<div class="barra-superior">
|
||||
<h1>Ingresar</h1>
|
||||
</div>
|
||||
|
||||
<div class="cuerpo-tarjeta">
|
||||
<form method="POST">
|
||||
{% csrf_token %}
|
||||
{{form.as_p}}
|
||||
<input class='boton' type="submit" value="Ingresar">
|
||||
</form>
|
||||
<p>¿No tienes una cuenta? <a href="{% url 'registro' %}">Regístrate</a></p>
|
||||
</div>
|
||||
|
||||
{% endblock content %}
|
||||
@@ -0,0 +1,150 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="es">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Lista de Pendientes</title>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Lato:ital,wght@1,300&display=swap" rel="stylesheet">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Abel&display=swap" rel="stylesheet">
|
||||
|
||||
<style>
|
||||
body {
|
||||
background-color: #E5D4D1;
|
||||
font-family: 'Abel', sans-serif;
|
||||
padding-top: 60px;
|
||||
}
|
||||
|
||||
.container{
|
||||
max-width: 560px;
|
||||
margin: auto;
|
||||
background-color: #E1DFDE;
|
||||
-webkit-box-shadow: 2px 2px 13px -4px rgba(0, 0, 0, 0.21);
|
||||
box-shadow: 2px 2px 13px -4px rgba(0, 0, 0, 0.21);
|
||||
}
|
||||
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6 {
|
||||
font-family: 'Lato', sans-serif;
|
||||
}
|
||||
|
||||
a,
|
||||
p {
|
||||
color: #996E66;
|
||||
}
|
||||
|
||||
.barra-superior{
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
color: #ffffff;
|
||||
padding: 10px;
|
||||
border-radius: 5px 5px 0 0;
|
||||
background: linear-gradient(90deg, #4D9FE3 0%, #3A8ACC 43%, #3A8ACC 100%);
|
||||
}
|
||||
|
||||
.barra-superior a {
|
||||
color: rgb(255, 255, 255);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.envoltorio-tarea {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 20px;
|
||||
background-color: #ffffff;
|
||||
border-top: 1px solid rgb(225, 225, 225);
|
||||
}
|
||||
|
||||
.titulo-tarea{
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.titulo-tarea a {
|
||||
text-decoration: none;
|
||||
color: #4B5156;
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.icono-tarea-completa {
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
background-color: rgb(105, 162, 105);
|
||||
border-radius: 50px;
|
||||
}
|
||||
|
||||
.icono-tarea-incompleta {
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
background-color: rgb(218, 218, 218);
|
||||
border-radius: 50px;
|
||||
}
|
||||
|
||||
.enlace-eliminar {
|
||||
text-decoration: none;
|
||||
font-weight: 900;
|
||||
color: #EA4220;
|
||||
font-size: 22px;
|
||||
line-height: 0;
|
||||
}
|
||||
|
||||
#envoltorio-agregar-buscar {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
#enlace-agregar{
|
||||
color: #EA4220;
|
||||
text-decoration: none;
|
||||
font-size: 42px;
|
||||
text-shadow: 1px 1px #81413B;
|
||||
}
|
||||
|
||||
input[type=text],
|
||||
input[type=password],
|
||||
textarea {
|
||||
border: 1px solid #EB796F;
|
||||
border-radius: 5px;
|
||||
padding: 10px;
|
||||
width: 90%;
|
||||
}
|
||||
|
||||
label {
|
||||
padding-top: 10px !important;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.boton {
|
||||
border: 1px solid #EB796F;
|
||||
background-color: #FAFAFA;
|
||||
color: #EB796F;
|
||||
padding: 10px;
|
||||
font-size: 14px;
|
||||
border-radius: 5px;
|
||||
cursor: pointer;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.cuerpo-tarjeta {
|
||||
padding: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
{% block content %}
|
||||
|
||||
{% endblock content %}
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,25 @@
|
||||
{% extends 'base/principal.html' %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<div class="barra-superior">
|
||||
<h1>Registrarse</h1>
|
||||
</div>
|
||||
|
||||
<div class="cuerpo-tarjeta">
|
||||
<form method="POST">
|
||||
{% csrf_token %}
|
||||
<label>{{form.username.label}}</label>
|
||||
{{form.username}}
|
||||
|
||||
<label>{{form.password1.label}}</label>
|
||||
{{form.password1}}
|
||||
|
||||
<label>{{form.password2.label}}</label>
|
||||
{{form.password2}}
|
||||
<input style="margin-top: 10px" class='boton' type="submit" value="Registrar">
|
||||
</form>
|
||||
<p>¿Ya tienes una cuenta? <a href="{% url 'login' %}">Ingresa</a></p>
|
||||
</div>
|
||||
|
||||
{% endblock content %}
|
||||
@@ -0,0 +1 @@
|
||||
<h1>Tarea: {{tarea}}</h1>
|
||||
@@ -0,0 +1,17 @@
|
||||
{% extends 'base/principal.html' %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<div class="barra-superior">
|
||||
<a href="{% url 'tareas' %}">🡠 Volver</a>
|
||||
</div>
|
||||
|
||||
<div class="cuerpo-tarjeta">
|
||||
<form method="POST">
|
||||
{% csrf_token %}
|
||||
<p>Vas a eliminar esta tarea: "{{tarea}}"</p>
|
||||
<input class='boton' type="submit" value="Eliminar">
|
||||
</form>
|
||||
</div>
|
||||
|
||||
{% endblock content %}
|
||||
@@ -0,0 +1,17 @@
|
||||
{% extends 'base/principal.html' %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<div class="barra-superior">
|
||||
<a href="{% url 'tareas' %}">🡠 Volver</a>
|
||||
</div>
|
||||
|
||||
<div class="cuerpo-tarjeta">
|
||||
<form method="POST" action="">
|
||||
{% csrf_token %}
|
||||
{{form.as_p}}
|
||||
<input class='boton' type="submit" value="enviar">
|
||||
</form>
|
||||
</div>
|
||||
|
||||
{% endblock content %}
|
||||
@@ -0,0 +1,48 @@
|
||||
{% extends 'base/principal.html' %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<div class="barra-superior">
|
||||
<div>
|
||||
<h1>Hola {{request.user|title}}</h1>
|
||||
<h3 style="margin:0">Tienes <i>{{count}}</i> tarea{{count|pluralize}} incompleta{{count|pluralize}}</h3>
|
||||
</div>
|
||||
{% if request.user.is_authenticated %}
|
||||
<a href="{% url 'logout' %}">Salir</a>
|
||||
{% else %}
|
||||
<a href="{% url 'login' %}">Ingresar</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div id="envoltorio-agregar-buscar">
|
||||
<form method="GET" style="margin-top: 20px; display: flex;">
|
||||
<input type="text" name="area-buscar" value="{{valor_buscado}}">
|
||||
<input class='boton' type="submit" value="buscar">
|
||||
</form>
|
||||
<a id='enlace-agregar' href="{% url 'crear-tarea' %}">🞥</a>
|
||||
</div>
|
||||
|
||||
<div class="envoltorio-items-tarea">
|
||||
{% for tarea in tareas %}
|
||||
<div class="envoltorio-tarea">
|
||||
{% if tarea.completo %}
|
||||
<div class="titulo-tarea">
|
||||
<div class="icono-tarea-completa"></div>
|
||||
<i><s><a href="{% url 'editar-tarea' tarea.id %}">{{tarea}}</a></s></i>
|
||||
</div>
|
||||
<a class='enlace-eliminar' href="{% url 'eliminar-tarea' tarea.id %}">⨯</a>
|
||||
{% else %}
|
||||
<div class="titulo-tarea">
|
||||
<div class="icono-tarea-incompleta"></div>
|
||||
<a href="{% url 'editar-tarea' tarea.id %}">{{tarea}}</a>
|
||||
</div>
|
||||
<a class='enlace-eliminar' href="{% url 'eliminar-tarea' tarea.id %}">⨯</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
{% empty %}
|
||||
<h3>No hay elementos en esta lista</h3>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
{% endblock content %}
|
||||
@@ -0,0 +1,3 @@
|
||||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
||||
@@ -0,0 +1,12 @@
|
||||
from django.urls import path
|
||||
from .views import ListaPendientes, DetalleTarea, CrearTarea, EditarTarea, EliminarTarea, Logueo, PaginaRegistro
|
||||
from django.contrib.auth.views import LogoutView
|
||||
|
||||
urlpatterns = [path('', ListaPendientes.as_view(), name='tareas'),
|
||||
path('login/', Logueo.as_view(), name='login'),
|
||||
path('registro/', PaginaRegistro.as_view(), name='registro'),
|
||||
path('logout/', LogoutView.as_view(next_page='login'), name='logout'),
|
||||
path('tarea/<int:pk>', DetalleTarea.as_view(), name='tarea'),
|
||||
path('crear-tarea/', CrearTarea.as_view(), name='crear-tarea'),
|
||||
path('editar-tarea/<int:pk>', EditarTarea.as_view(), name='editar-tarea'),
|
||||
path('eliminar-tarea/<int:pk>', EliminarTarea.as_view(), name='eliminar-tarea')]
|
||||
@@ -0,0 +1,81 @@
|
||||
from django.shortcuts import render, redirect
|
||||
from django.views.generic.list import ListView
|
||||
from django.views.generic.detail import DetailView
|
||||
from django.views.generic.edit import CreateView, UpdateView, DeleteView, FormView
|
||||
from django.contrib.auth.forms import UserCreationForm
|
||||
from django.contrib.auth import login
|
||||
from django.contrib.auth.views import LoginView
|
||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||
from django.urls import reverse_lazy
|
||||
from .models import Tarea
|
||||
|
||||
|
||||
class Logueo(LoginView):
|
||||
template_name = 'base/login.html'
|
||||
fields = '__all__'
|
||||
redirect_authenticated_user = True
|
||||
|
||||
def get_success_url(self):
|
||||
return reverse_lazy('tareas')
|
||||
|
||||
|
||||
class PaginaRegistro(FormView):
|
||||
template_name = 'base/registro.html'
|
||||
form_class = UserCreationForm
|
||||
redirect_authenticated_user = True
|
||||
success_url = reverse_lazy('tareas')
|
||||
|
||||
def form_valid(self, form):
|
||||
usuario = form.save()
|
||||
if usuario is not None:
|
||||
login(self.request, usuario)
|
||||
return super(PaginaRegistro, self).form_valid(form)
|
||||
|
||||
def get(self, *args, **kwargs):
|
||||
if self.request.user.is_authenticated:
|
||||
return redirect('tareas')
|
||||
return super(PaginaRegistro, self).get(*args, **kwargs)
|
||||
|
||||
|
||||
class ListaPendientes(LoginRequiredMixin, ListView):
|
||||
model = Tarea
|
||||
context_object_name = 'tareas'
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
context['tareas'] = context['tareas'].filter(usuario=self.request.user)
|
||||
context['count'] = context['tareas'].filter(completo=False).count()
|
||||
|
||||
valor_buscado = self.request.GET.get('area-buscar') or ''
|
||||
if valor_buscado:
|
||||
context['tareas'] = context['tareas'].filter(titulo__icontains=valor_buscado)
|
||||
context['valor_buscado'] = valor_buscado
|
||||
return context
|
||||
|
||||
|
||||
class DetalleTarea(LoginRequiredMixin, DetailView):
|
||||
model = Tarea
|
||||
context_object_name = 'tarea'
|
||||
template_name = 'base/tarea.html'
|
||||
|
||||
|
||||
class CrearTarea(LoginRequiredMixin, CreateView):
|
||||
model = Tarea
|
||||
fields = ['titulo', 'descripcion', 'completo']
|
||||
success_url = reverse_lazy('tareas')
|
||||
|
||||
def form_valid(self, form):
|
||||
form.instance.usuario = self.request.user
|
||||
return super(CrearTarea, self).form_valid(form)
|
||||
|
||||
|
||||
class EditarTarea(LoginRequiredMixin, UpdateView):
|
||||
model = Tarea
|
||||
fields = ['titulo', 'descripcion', 'completo']
|
||||
success_url = reverse_lazy('tareas')
|
||||
|
||||
|
||||
class EliminarTarea(LoginRequiredMixin, DeleteView):
|
||||
model = Tarea
|
||||
context_object_name = 'tarea'
|
||||
success_url = reverse_lazy('tareas')
|
||||
Reference in New Issue
Block a user