Projeto 2 - Django Todolist

Django: TO DO LIST

Bem-vindo ao mundo da organização eficiente e da produtividade aprimorada! Em nosso dia a dia agitado, a capacidade de manter nossas tarefas e responsabilidades bem organizadas é mais importante do que nunca. É por isso que hoje vamos mergulhar na criação de um modelo de projeto intuitivo e personalizável, que não só nos ajudará a manter o controle de nossas atividades, mas também nos permitirá adaptar-se às mudanças com facilidade. Prepare-se para transformar a maneira como você gerencia suas listas de tarefas com nosso guia passo a passo para modelar seu próprio sistema de gerenciamento de tarefas.

 

Github: 

Depois de feito as configurações iniciais e executado o projeto.

Vamos para criação do nosso modelo e estrutura do projeto.

  • Modelando Projeto

    Vamos criar duas tabelas. A primeira é TodoList e uma outra Status, que serve ****para criar o droplist das informações dos status exemplo: “⛔️ a Fazer, ⚠️ Fazendo e ✅ Finalizado”. Poderíamos deixar essas informações fixas em um simples array. Mas achei melhor criar uma tabela onde usuário pode customizar essas informações.

    Na tabela TodoList vamos criar um campo tipo ForeignKey relacionando com tabela status. Note que to_field='id', default='1' ao adicionar essas duas tags, que significa que para field id da tabela Status colocar como default o item de Id=1. Sempre que criarmos um formulário partir dessa tabela TodoList o campo ‘status” já terá um valor default.

     

 

  • myapp/templates/models.py

    from django.db import models
    
    class Status(models.Model):
        name = models.CharField(max_length=20)
        
        def __str__(self):
            return self.name
        
    
    class TodoList(models.Model):
        title = models.CharField(max_length=100)
        status = models.ForeignKey(Status, on_delete=models.CASCADE, 
                                   related_name='status', to_field='id', default='1') 
       
        def __str__(self):
            return self.title
    

    No template HTML vamos ter um formulário simples de um campo somente para adicionar um item na lista. Então, teremos que configurar o forms e adicionar classes bootstrap para esse campo “title”.

    Note que exclude = ('status',) quando adiciono essa tag significa que estou excluindo esse campo status do formulário. Como foi dito acima esse campo inicialmente já recebe um valor default.

    myapp/forms.py

    from django import forms 
    from .models import TodoList
    
    class TodoListForm(forms.ModelForm):  
         
        class Meta:
            model = TodoList
            fields = ('title',)
            exclude = ('status',)
        
        def __init__(self, *args, **kwargs): # Adiciona 
            super().__init__(*args, **kwargs)  
            for field_name, field in self.fields.items():   
                  field.widget.attrs['class'] = 'form-control'
    

    Na views do projeto vamos adicionar essas duas funções. create_item onde vamos fazer um post para criar um item na lista. E função delete_item para deletar um item da lista.

    myapp/views.py

    def create_item(request):
        todo = TodoList.objects.all() # Query com todos objetos da lista
        if request.method == "POST": # para POST
    				form = TodoListForm(request.POST)  
            if form.is_valid():
                form.save() # salva informação
                return redirect('/')
            
        form = TodoListForm() # Formulário
        context = {"form" : form, 'todo': todo}
        return render(request, 'index.html', context)
    
    def delete_item(request, id):
        todo = TodoList.objects.get(id=id) # pega o objeto
        todo.delete() # deleta
        return redirect('index')
    

    Criar as rotas para acessar nossas views.

    myapp/urls.py

    from django.urls import path 
    from myapp import views
    
    urlpatterns = [
        path('', views.create_item, name='create_item'),  
        path('delete/<int:id>/', views.delete_item, name="delete"),
    ]
    

    No nosso template index.html vamos adicionar nosso {{form}} e rodar a aplicação para testar.

    No momento não estamos utilizando Ajax para deixar as coisas mais dinâmicas. Apenas estamos fazendo as configurações iniciais e testando “estrutura” do projeto.

    Estou utilizando ícones dessa biblioteca Font Awesome.

    Segue documentação para instalar via CDN ou PIP. Você escolhe.

     

    myapp/base/templates/base.html

    # prefiro utilizar versão free CDN.
    <head>
    	 ....
    	<link rel="stylesheet" href="<https://use.fontawesome.com/releases/v5.15.4/css/all.css>" integrity="sha384-DyZ88mC6Up2uqS4h/KRgHuoeGwBcD4Ng9SiP4dIRy0EXTlnuz47vAwmeGwVChigm" crossorigin="anonymous"/>
     
    </head>
    

    myapp/templates/index.html

    {% extends 'base.html' %} 
    {% block title %}Pagina 1{% endblock %} 
    {% block content %}
    	<h2>Pagina 1</h2>  
    	<div class="p-5"> 
    		<form class="d-flex gap-4 col-md-6" method="POST"> 
    	      {% csrf_token %}  
    				<button type="submit" class="btn btn-success"><i class="fa fa-plus"></i></button> 
    				{{form}}  
    		</form>     
    		<table class="table">  
                <thead>
                    <tr>
                        <th scope="col">Titulo</th> 
                        <th scope="col">Status</th>
                        <th scope="col">Deletar</th> 
                    </tr>
                </thead> 
                {% for el in todo %} # são as linhas que vão repetir
                <tbody>
                    <tr class="table align-middle">
                        <th scope="row">{{el.title}}</th>  
                        <th scope="row">{{el.status}}</th> 
                        <th scope="row">
    						 <a class="btn" href="{% url 'delete' el.id %}">
    						 	<i class="fa fa-trash link-danger"></i>
    						 </a>
    					 </th> 
                    </tr>
                </tbody>  
                {% endfor %} 
            </table>     
    	</div> 
    {% endblock %}
    
  • Configurar AJAX

    Primeiro para usar o AJAX precisamos importar jquery no nosso projeto.

    Pode pegar o CDN daqui 

     <body>
    	...
    	<script src="<https://code.jquery.com/jquery-3.6.1.min.js>" integrity="sha256-o88AwQnZB+VDvE9tvIXrMQaPlFFSUTR+nldQm1LuPXQ=" crossorigin="anonymous"></script> 
    	{% block scripts %} {% endblock %}
    </body>
    </html>
    
  • Atualizar Titulo da Lista

    Atualizar titulo da lista com AJAX.

    Primeiro pensei nessa estrutura. O usuário clica no titulo e aparece o formulário para editar. Então vou ter duas tags:

    1 - Adicionei uma “div para “title”. E criei um atributo para data-title que recebe o identificador do item da lista. E com esse data-title conseguimos chamar via Ajax e pegar o id.

    2 - <form> se inicia Oculto. Esse formulário só vai aparecer quando clicar em cima do titulo. Esse form não tem nenhuma novidade. é um campo input e botão submit.

    Lembrando Método GET

    <th scope="row" style="width:35rem;">
    
    	<div class="title" id="title{{el.id}}" data-title="{{el.id}}">{{el.title}}</div>
    	
    	<form class="d-none d-flex" id="form-title{{el.id}}" method="GET" style="width:35rem;">
    		<input class="form-control" type="text" id="inputText{{el.id}}" value="{{el.title}}">
    		<button type="submit" class="btn" id="edit{{el.id}}"><i class="fa fa-edit link-warning"></i></button>
    	</form>
    
    </th>
    

    Debaixo de {% endblock %} vamos chamar tag script assim: {% block scripts %}.

    Nessas duas linhas de destaque em amarelo é feita aquela jogada. Quando usuário clicar em cima do titulo aparece o campo para editar e remove o contexto anterior.

    Logo mais abaixo quando success: function (data) recebemos uma resposta do servidor de que a informação foi salva. Então volta para configuração inicial.

    Nota que precisamos adicionar no HTML essa resposta, para que usuário veja essa alteração acontecendo em tempo real. Assim que recebe a informação do servidor já atualiza no template HTML o resultado da alteração.

    {% endblock %}
    
    {% block scripts %}
    <script type="text/javascript"> 
    		// Atualiza Titulo da Lista
    		$("div.title").click(function () {
    	
    			var data_id = $(this).attr("data-title");
    	
    			$("form#form-title" + data_id).removeClass('d-none')
    			$("div#title" + data_id).addClass('d-none')
    	
    			$('button#edit' + data_id).on("click", function (e) {
    				e.preventDefault();
    			  
    				title = $('input#inputText'+ data_id).val();
    	 
    				$.ajax({
    					type: 'GET',
    					url: '{% url "update-item" %}',
    					data: {'data_id': data_id,'title': title,},
    					datatype: "json",
    					success: function (data) {
    						if (data.status == "update-item") {
    							$("form#form-title" + data_id).addClass('d-none');
    							$("div#title" + data_id).removeClass('d-none'); 
    							$("#title" + data_id).html(data.title); 
    						}  
    					}
    				}); 
    
    			});
    		});
    </script> 
    {% endblock %}
    

    Na nossa views vamos criar uma função. Chamei de Update Item.

    def update_item(request):  
        data_id  = request.GET.get('data_id') # Id da Lista
        title = request.GET.get('title') # Id do status
        print(data_id, title)
    
        todo = get_object_or_404(TodoList,id=data_id) # Get Objeto lista
        todo.title = title # status recebe novo valor "Id do status"
        todo.save() # salva  
    
        data = {'status':'update-item', 'title':title}
        return JsonResponse(data) # retorna
    
  • Atualizar Status

    Atualizar o Status do Item da Lista

    Vamos criar um select para atualizar o status do item.

    Esse status_list é um context que vamos configurar na views para listar todos os status que existe a partir do modelo Status. Na função create_item adicionamos esse context.

    status_list = Status.objects.all() .

    <th scope="row">
    
    	<div class="SelDiv">
    		<select class="form-select" name="status" id="{{el.id}}">
    			{% for st in status_list %}
    				<option value="{{st.id}}" {% if el.status.id == st.id %}selected{% endif %}>
    				{{st.name}}
    				</option>
    			{% endfor %}
    		</select>
    	</div>
    
    </th>
    
    

    Nossa configuração AJAX fica assim…

    	// Muda Status do Item da Lista
    	$("div.SelDiv select").on('change', function () {
    
    		var data_id = this.id;
    		var status_id = $(this).find('option').filter(':selected').val();
    
    		console.log("data_id: ", data_id, "status_id: ", status_id)
    		
    		$.ajax({
    			type: 'GET',
    			url: '{% url "update-status" %}',
    			data: {
    				'data_id': data_id,
    				'status_id': status_id,
    			},
    			datatype: "json",
    
    			success: function (data) {
    				console.log(data)
    			}
    
    		});
    	});
    

    Função vai receber esses dados e salvar na tabela.

    def update_status(request):  
        data_id  = request.GET.get('data_id') # Id da Lista
        status_id = request.GET.get('status_id') # Id do status 
        
    		status = Status.objects.get(id=status_id) # Get objeto status 
        
    		todo = get_object_or_404(TodoList,id=data_id) # Get Objeto lista
        todo.status = status # status recebe novo valor "Id do status"
        todo.save() # salva  
        
    		data = {'status':status_id}
        return JsonResponse(data)
    
  • Deletar um Item da Lista

    Deletar Item da Lista

    Para deletar um item da lista primeiro precisamos adicionar um botão template.

    Nota que adicionamos essa tag data-delete="{{el.id}}" Assim conseguimos fazer um “GET” via Ajax e pegar o identificador do item da lista.

    Poderíamos aproveitar o identificador da tag tr#id. Optei por criar essa tag data-delete.

    <th scope="row">
    	<a class="btn" id="btn-delete" data-delete="{{el.id}}"><i class="fa fa-trash link-danger"></i></a>
    </th>
    

    Para deletar é mesmo conceito dos outros. Nos passamos o identificador e na views é feito o filtro para deletamos o item da tabela.

    Mas agora adicionamos uma regra no retorno. Por que quando deletamos um item o usuário não recebe essa atualização no frontend. Só é mostrado para usuário depois q atualiza a pagina.

    Então fica assim, envio o identificador para views e delete acontece. Depois na função “success” vamos ter um retorno onde definimos uma condição. Se status for igual a “delete” pego o identificador da coluna e remove().

    	// Deleta um Item da Lista
    	$("a#btn-delete").on("click", function (e) {
    		e.preventDefault();
    
    		var todo_id = $(this).attr("data-delete");
    		console.log(todo_id);
    
    		$.ajax({
    			type: 'GET',
    			url: '{% url "delete" %}',
    			data: { 'todo_id': todo_id },
    			datatype: "json",
    			success: function (data) {
    
    				if (data.status == "delete") {
    					$("tbody tr#" + todo_id).fadeOut("slow", function () {
    						$("tbody tr#" + todo_id).remove();
    					});
    				} else {
    					// faz alguma coisa
    				}
    
    			}
    		});
    	})
    

    Essa é função para deletar um item da tabela.

    def delete_item(request):
        todo_id  = request.GET.get('todo_id') # Id da Lista
        todo = TodoList.objects.get(id=todo_id) # Pega Objeto
        todo.delete() # Deleta
        data = {'status':'delete'}
        return JsonResponse(data)
    

E assim, damos os toques finais em nosso modelo de projeto personalizado. Com as tabelas TodoList e Status agora estabelecidas, você tem o poder de personalizar e adaptar seu sistema de gerenciamento de tarefas às suas necessidades específicas. A flexibilidade de um campo ForeignKey e a conveniência de valores padrão pré-definidos garantem que você esteja sempre um passo à frente, pronto para marcar cada tarefa com um satisfatório “✅ Finalizado”. Esperamos que este guia tenha iluminado seu caminho para uma organização mais eficiente e que você esteja tão empolgado quanto nós para colocar essas estruturas em ação. Até a próxima!

Comentários

Total de Comentários: 0