Todo List with Django (Dennis Ivy)


0. Upgrade Django Version

pip install --upgrade django==3.0

1. Create Project

django-admin startproject todolist[name of project]

2. Run Server


inside the project folder

ctrl + c: break on server

3. Migrate DB

python manage.py migrate
by default, django uses sqlite but normally you'd wanna use postgresql or mysql.
created our default table: user

4. Create Admin(User)

python manage.py createsuperuser 

5. Launch an App


python manage.py startapp [app name]
Register an App into settings
settings.py
INSTALLED_APPS

6. url Routing


1. Create 'index function' at views.py

from django.http import HttpResponse

# Create your views here.
def index(request):
    return HttpResponse("hello world")

2. Create urls.py

from django.urls import path
from . import views
urlpatterns = [
    path('', views.index)
]

3. Include url in base urls.py


This should sets up the response. Turn on server to check.
from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', include('tasks.urls'))
]
Successfully set up

7. Templates


app> templates > tasks > html

Note the file name
def index(request):
    return render(request, 'tasks/list.html')

8. Create Model


__str__


when you retrieve data from DB with Blog.objects.all() , It will return <QuerySet [<Blog:>,<Blog:>,...]It's hard to recognize the objects.
Better way is retrieving them by one of its properties which you set it as name.<QuerySet [<Blog:itsName>,<Blog:itsName>,...]models.py
class Task(models.Model):
    title = models.CharField(max_length=200)
    complete = models.BooleanField(default=False)
    created = models.DateTimeField(auto_now_add=True)
    
    def __str__(self):
        return self.title
any new models?
python manage.py makemigrations
create in DB
python manage.py migrate

Register model at admin
admin.py
from .models import *

admin.site.register(Task)
so we can see table in our server admin section

9. Render Data in template

  • context is our third parameter that passes DB object.
  • views.py
    from .models import *
    
    def index(request):
        tasks = Task.objects.all()
        context = { 'tasks': tasks }
        return render(request, 'tasks/list.html', context)
  • I already added tasks into db through admin website to see if it's rendered correctly.
  • list.html
    <h3>todo</h3>
    
    {% for task in tasks %}
    <div>
        <p>
            {{ task }}
        </p>
    </div>
    {% endfor %}

    10. Model Forms


    Ivy prefers to use 'Model Forms' over 'built-in Class based views forms'. He thinks It's too easy to make it work but not helps to underderstand it.
    Hence, He goes for 'Model Forms'

    1. Create folder 'forms.py'**


    inside app folder
    form representation of a model
    forms.py
    from django import forms
    from django.forms import ModelForm
    from .models import *
    
    class TaskForm(forms.ModelForm):
    
        class Meta:
            model = Task
            fields = '__all__'

    2. Pass the data as parameter


    Pass form instance as 3rd parameter inside context
    views.py
    from .forms import *
    
    def index(request):
        tasks = Task.objects.all()
    
        form = TaskForm()
    
        context = {'tasks': tasks, 'form': form}
        return render(request, 'tasks/list.html', context)

    3. Into Template


    Pass the form into template
    create html form element and get passed the data
    list.html
    <form>
        {{ form.title }}
        <input type="submit" name="Create Task">
    </form>

    CRUD


    11. Create Item


    when we submit data, it's gonna send it back to view

    1. template

  • make sure to include csrf_token
  • list.html
        {% csrf_token %}

    2. view

  • passing the 'post method' into the form
  • views.py
    def index(request):
        tasks = Task.objects.all()
        form = TaskForm()
        
        if request.method == 'POST':
            form = TaskForm(request.POST) 
            if form.is_valid():
                form.save() 
            return redirect('/')
    
        context = {'tasks': tasks, 'form': form}
        return render(request, 'tasks/list.html', context)

    12. Update Item (Edit)


    pk == primary key
    grab the pk from url pattern
  • <str:pk> : str value works with numbers and letters
  • Dynamic Name: make url dynamic by assigning name.
    how we call the url pattern
  • 1. url


    urls.py
    urlpatterns = [
        path('', views.index, name="list"),
        path('update_task/<str:pk>',
        	views.update_task, name="update_task"),
    ]

    2. view


    1) GET


    pre-populated form
    views.py
    def update_task(request, pk):
        task = Task.objects.get(id=pk)
        form = TaskForm(instance=task) 
        context = {'form': form}
    
        return render(request, 'tasks/update_task.html', context)
    

    2) POST


    Add post. if we only pass request.POST, it will create a new obj. Make sure to pass instance we are updating.form = TaskForm(request.POST, instance=task)
    def update_task(request, pk):
        task = Task.objects.get(id=pk)
        form = TaskForm(instance=task)
        context = {'form': form}
        
        if request.method == 'POST':
            form = TaskForm(request.POST, instance=task) 
            if form.is_valid():
                form.save()
                return redirect('/')
        return render(request, 'tasks/update_task.html', context)

    3. template

  • Create Update link to direct to edit page
    -used 'name' 'update_task' in url pattern<a href="{% url 'update_task' task.id %}">Update</a>== update_task/1
  • list.html
    {% for task in tasks %}
    <div>
    
        <a href="{% url 'update_task' task.id %}">Update</a>
        
        <p>{{ task }}</p>
    </div>
    {% endfor %}

    update_task.html
    <h3>Update Task</h3>
    
    <form action="" method="POST">
        {% csrf_token %}
        {{ form }}
        <input type="submit" name="Update Task">
    </form>

    13. Delete Item


    UI: Ask user to confirm their action: sure you want to delete?

    1. url

    path('delete_task/<str:pk>',views.delete_task, 
          name="delete_task"),

    2. views


    1. GET


    Get confirmation
    def delete_task(request, pk):
        item = Task.objects.get(id=pk)
        context = {'item': item}
        return render(request, 'tasks/delete.html', context)

    2. POST


    Actually delete from DB
    def delete_task(request, pk):
        item = Task.objects.get(id=pk)
        context = {'item': item}
    
        if request.method == 'POST':
            item.delete()
            return redirect('/')
    
        return render(request, 'tasks/delete.html', context)

    3. templates


    list.html
    <a href="{% url 'delete_task' task.id %}">Delete</a>
  • We don't need a form as it will just delete the item.
  • delete.html
    <p> Are you sure you want to delete "{{ item }}"? </p>
    
    <a href="{% url 'list' %}"> Cancel </a>
    
    <form action="" method="POST">
        {% csrf_token %}
        <input type="submit" name="Confirm">
    </form>

    14. Cross out complete items

  • html strike tag will place a strikethrough (horizontal line) over text on the complete task (ticked off)
  • list.html
        {% if task.complete == True %}
            <strike>{{ task }}</strike>
        {% else %}
            <span> {{task}} </span>
        {% endif %}
  • complete is from form model
  • models.py
    class Task(models.Model):
        complete = models.BooleanField(default=False)

    15. Style template


    video 36:05 - Style template

    form: sets a place holder


    as we don't use html form, we can't set placeholder directly from html form element.
    -customizing the form field
    form.py
    class TaskForm(forms.ModelForm):
        title = forms.CharField(
        	widget= forms.TextInput(
            attrs={'placeholder': 'Add new task...'}))
    

    Result



    Dennis Ivy Todolist