Django 3 Tutorail - Part 1
These notes are currently in the grimoire becuase there's a lot of work to do to get them to not blow up MDX
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
--
TODO: Put this in for handling IDs that don't exist (verify this is the good way to do it)
from django.http import Http404
def detail_path(request, pk):
try:
obj = Detail.objects.get(pk=pk)
except Detail.DoewNotExist:
raise Http404
return(...)
TODO: Think about using straight calls in the urls to ``views.whatever`` instead of includes?
-- hr
More advanced, but look at replacing the ID with a UUID but putting this in your model class:
db_id = models.UUIDField(primary_key=True, default=uuid.uuid4())
not something taht should be necessary most of the time, but it might have use
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
--
Depending on how new you are to Python and/or Programming, you're not going to understand parts of this. That's fine for now.
You're going to see a lot of errors in your programming career. They're your friends. Sometimes they piss you off, but they always have your back.
Here's the thing to keep in mind, error messages are your friend. We're going to use them. They'll tell us what we need to do.
Each time we see a new one, we're getting more information about what to do and how to make things work. Every time we see a new one, we're making progress.
We're doing to ignore git for now.
Your going to want to have four things opened:
- 2 terminal windows (one we'll call the "server terminal" and the other we'll call "TKTKTKT")
- your text editor
- your browser
V2:
- Make the directory for your project and move into it:
mkdir django
And move into it:
cd django
- Make sure you have a virtual environment setup:
TODO: Replace this wity pyvenv
--python3 -m venv venv--
Then make sure you're using it with:
source venv/bin/activate
You'll see ``(venv)`` show up in front of your command line prompt.
TODO: Put note in here about opening second terminal window and also start up venv for it.
- Install Django:
pip install django
You'll probably see a warning about the version of pip your using. That looks something like the below. You can safely ignore it:
WARNING: You are using pip version 19.2.3, however version 20.2.3 is available.
You should consider upgrading via the 'pip install --upgrade pip' command.
- Make a new Django site:
django-admin startproject config .
If you see an error like this: CommandError: 'tutorial-site-2020-10-17' is not a valid project name. Please make sure the name is a valid identifier. It's because you used dashes instead of underscores.
- Move into your site directory with this. The rest of these notes will assume you're there as you site root
TKTKTK, figre out what you need to do with the directories ehre.
It's important to point out there there's another directory called ``tutorial_site`` inside the one you just made. It's just part of the way Django is structured, but it can be confusing.
- We can check the server right now.
Be ready, you're about to see your first (intentional) warning/error.
It's going to throw a big red warning starting with something like "You have 18 unapplied migration(s)", but that's okay for now. Just run this:
python manage.py runserver
And then checkout the site running on your local machine with:
< >
Kill it with CTRL+C
Go back to the web browser and you'll see that the site is offline. (You'll probably run into this again, when you aren't expecting it. This is just so you know what it looks like, when the server is either stopped or couldn't start.)
- Now, run this command.
python manage.py migrate
Don't worry to much about it at this point, but basically when you start a new Django project it creates a database. It's not fully setup though. This command does that setup. We'll also use it when we make changes, but running it now just prevents a bunch of scary warning messages from showing up.
- Now, just to prove it worked, run the server again. This time you won't see the big red warning....
python manage.py runserver
- When you've finished admiring your shinny new site, stop the server by holding down the "Control" key and pressing "C".
That's it, your live with the platform to start building on.
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
--
### Making A Home Page
TKTKTKTKKTT: Consider building the homepage directly at the root instead of ``/pages/homepage`` and then moving it.
We're going to build the page at:
TKTKTKTK: Don't build the page then move it,
# Build it and the home page level, you can
# explain more about the paths in later.
http://127.0.0.1:8000/pages/homepage
The move it to:
http://127.0.0.1:8000/
So that it it's the first thing you see. You know, because it's a home page.
All the wires are hooked up. So, let's make our own home page. We're going to do this in a way that causes lots of errors and then address each error as we see it. The other way is to make a whole bunch of changes without seeing anything but that can get very confusing when you finally get to the end if something doesn't work right.
THe way we'll do this is by making a "pages" module (aka app) and putting the home page in it.
There are, of course, other ways to appraoch this, but this will give you good experience with error message... TKTKTKTK doing it this way give you experience with error messages that will also show you how to move forward.
For now, Just follow the directions to get the page running and then we'll go back and walk through the details.
- First, start up the server if it's not already running:
python manage.py runserver
Make sure it's live:
http://127.0.0.1:8000
- Our first step is to setup the paths to point the root of our home page to the path where we're going to put the file we want to display.
We do that by updating this file
/Users/alans/django/tutorial_site/tutorial_site/urls.py
(And just to point it out, ``tutorial_site/tutorial_site`` is not a typo. You gotta go into the second one, as confusing as that is. )
Change this:
urlpatterns = [
path('admin/', admin.site.urls),
]
To this:
urlpatterns = [
path('pages/', include('pages.urls')),
path('admin/', admin.site.urls),
]
The command line will freak out and throw an error that ends with something like this:
File "/Users/alans/django/tutorial_site/tutorial_site/urls.py", line 21, in
path('', include('pages.urls'))
NameError: name 'include' is not defined
(NOTE Also that the website shows you the same error that you got at the start before we setup the server. That's because the server can't start in this state. )
Note that the file listed "ROOT_DIR/tutorail_site/urls.py" is the one we just worked on. (In my case ROOT_DIR = "/Users/alans/django") So, we know that's were the problem is. To fix this error, we need to change this:
from django.urls import path
To this:
from django.urls import include, path
Next error:
ModuleNotFoundError: No module named 'pages'
Fix:
Stop the web server with:
CTRL+C
Then run
python manage.py startapp pages
Note that this made a new "pages" directory at "DJANGO_ROOT/tutorail_site/pages". There's also other stuff in there. We'll get to it later, but you can sniff around if you're interested.
And in ``~/django/tutorial_site/settings.py`` add this line into `INSTALLED_APPS`:
'pages',
So, it'll look like this:
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'pages',
]
Error:
Run the server again:
python manage.py runserver
And you'll see our new error:
ModuleNotFoundError: No module named 'pages.urls'
Fix:
Make this file:
touch /Users/alans/django/tutorial_site/pages/urls.py
Put this in that file (Don't worry too much about it right now):
from django.urls import path
from . import views
urlpatterns = [
path('homepage', views.homepage),
]
TKTKTKTKT: Put in note about pathing. Explain bascially what you did at a high level.
TKTKTKT: consider making actual URLs to start with. (Don't know what I meant by that.... )
Error:
Stop the server if it's running:
[Command] + c
Then, run it again:
python manage.py runserver
And we made progress and got ourselves a new error message:
AttributeError: module 'pages.views' has no attribute 'homepage'
Fix:
Edit: ~/django/tutorial_site/pages/views.py to add this.
def homepage(request):
return render(
request,
'pages/homepage.html'
)
So, the full file will look like:
from django.shortcuts import render
def homepage(request):
return render(
request,
'pages/homepage.html'
)
The server will start working with the last messages looking like:
Django version 3.1.2, using settings 'tutorial_site.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.
Error:
With the command line server running we can switch to the Browser and try to open:
http://127.0.0.1:8000/pages/homepage
The error you'll see is:
TemplateDoesNotExist at /pages/homepage
pages/homepage.html
Fix:
Stop the server since we'll need to make two directories to fix this:
Command+c
We need to make two directories and a file to fix this. The first directory is:
mkdir /Users/alans/django/tutorial_site/pages/templates
mkdir /Users/alans/django/tutorial_site/pages/templates/pages
(yeah, that ``pages/tempates/pages`` is a little weird looking, but that's the just how django works)
Make a new file in that last director called:
touch /Users/alans/django/tutorial_site/pages/templates/pages/homepage.html
With those directories made, we can put make a new HTML file with:
Hello, World
And save it to:
/Users/alans/django/tutorail_site/pages/templates/pages/homepage.html
Restart the server and refresh the web page and you'll see your page.
python manage.py runserver
And, we're live at:
http://127.0.0.1:8000/pages/homepage
The last thing we need to do is to move it from:
http://127.0.0.1:8000/pages/homepage
To:
http://127.0.0.1:8000/
This is done by updating the urls.py file.
TKTKTKTKT: Add more description for what's going on here.
First one is:
/Users/alans/django/tutorial_site/tutorail_site/urls.py
Change this:
path('pages/', include('pages.urls')),
To this:
path('', include('pages.urls')),
This will fail
http://127.0.0.1:8000/pages/homepage
Because the server is now setup to server it here:
http://127.0.0.1:8000/homepage
TKTKTKT: put in notes on how URLs work here.
Now, we move it all the way up,
That's done by editing `/Users/alans/django/tutorial_site/pages/urls.py`, and changing this:
path('homepage', views.homepage),
to this:
path('', views.homepage),
And, that's it! Our home page is now live at:
http://127.0.0.1:8000
Boom.
### Make an About Page
TODO: Figure out if the slash at the end (e.g. ``about/`` is best practice and if there's problems likely if you don't do it and just do ``about`` without the slash)
Lots of the work to build the home page supports other pages. To add an About page, all we need to do is:
-- hr
Let's start out by making a link to the about page that we can work with.
Add this line to:
About Page
To:
/Users/alans/tutorial_site/pages/templates/pages/homepage.html
So that the file looks like this:
Hello, World
About Page
Now when we click on that link, we'll get a 404 (Page not found)
http://127.0.0.1:8000/about
You'll get a 404.
In this file:
/Users/alans/django/tutorial_site/pages/urls.py
From:
urlpatterns = [
path('', views.homepage),
]
To:
urlpatterns = [
path('about', views.about),
path('', views.homepage),
]
Error:
Will render an error in the Server Terminal
AttributeError: module 'pages.views' has no attribute 'about'
Fix:
Add this to ~/django/tutorail_site/pages/views.py:
def about(request):
return render(
request,
'pages/about.html'
)
So the full file looks like:
from django.shortcuts import render
def homepage(request):
return render(
request,
'pages/homepage.html'
)
def about(request):
return render(
request,
'pages/about.html'
)
NOTE: If you get ``IndentationError: unexpected indent`` it means the defs are nested incorrectly.
Error:
The Server Terminal will be okay now and the site will be serving.
Go here to see our next error:
http://127.0.0.1:8000/about
Which is:
TemplateDoesNotExist at /about/
pages/about.html
Fix:
Make this file:
touch /Users/alans/django/tutorial_site/pages/templates/pages/about.html
Put this HTML:
About Page
That gets it to show up. And now we can add this line, to link back to the home page:
Home Page
About Page</>
That's it.
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
--
# Linking our pages:
Our links are currently hard coded. But we want to use Django to create them.
TODO: Switch this to make homepage first then about page and
TODO: This needs clean up
Making it dynamic:
Add this to pages/urls.py
app_name = 'pages'
TKTKTL: Figure out how to describe what ``app_name='pages'`` does. Pretty sure that lets the URL handlers, know that the first part of "pages.about" is where "pages" = "app_name".
And the update:
path('about', views.about),
To this:
path('about', views.about, name='about'),
So the full file looks like:
from django.urls import path
from . import views
app_name = 'pages'
urlpatterns = [
path('', views.homepage),
path('about', views.about, name='about'),
]
That gives us the ability to use ``pages:about`` (where pages is the ``app_name`` and ``about`` is the name in the ``path()`` call. It looks like this:
About Page
About Page
To get our home page do this, change this:
path('', views.homepage),
To this:
path('', views.homepage, name='homepage'),
We don't need to add ``app_name`` because we already have.
Full pages:
Homepage - Hello, World
Home Page
About Page
About Page
Home Page
About Page
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
--
### Headers and Footers
Now we're going to add consistent headers/footers to the pages.
To start, we're going to introduce an intentional error. Change ~/homepage.html to:
TODO: Make sure this is the right way to call top level templates. Of if maybe you should put them in their own app?
{% extends 'base.html' %}
Homepage
Home Page
About Page
Error:
TemplateDoesNotExist at /
base.html
You'll also see this in the terminal:
django.template.exceptions.TemplateDoesNotExist: base.html
[18/Oct/2020 15:49:39] "GET / HTTP/1.1" 500 118815
Fix:
Create dir:
mkdir /Users/alans/django/tutorial_site/templates
then add:
touch /Users/alans/django/tutorial_site/templates/base.html
NOTE That you're not in:
django/tutorial_site/tutorial_site/templates
But:
django/tutorial_site/templates
In
/Users/alans/django/tutorail_site/tutorail_site/settings.py:
In the section:
TEMPLATES = [...]
Update:
'DIRS': [],
To:
'DIRS': [str(BASE_DIR.joinpath('templates'))],
TKTKTKTKT: Change 'body_block' to 'content', maybe?
with:
Tutorial Site
Home Page
About Page
{% block body_block %}{% endblock %}
The "Home Page" text will disappear and be replace by "Tutorail Site". That's because base.html is being called, but we aren't populating it.
TKTKTKT: Talk about global nav on all our pages.
Fix:
Add these to homepage.html so that it looks like this:
{% extends 'base.html' %}
{% block body_block %}
Homepage - Hello, World
{% endblock %}
And we can do the same thing to about.html
{% extends 'base.html' %}
{% block body_block %}
About Page
{% endblock %}
TKKTKTKTK: Add basic CSS stuff.
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
--
First make a page to hold your data.
Attempt 2
- Add link to checklist on the template in the base.html file
Tutorial Site
Home Page
About Page
Checklist
{% block body_block %}{% endblock %}
Error:
404
Add this:
path('checklist/', include('checklist.urls')),
To:
/Users/alans/django/tutorial_site/tutorial_site/urls.py
So you get:
path('admin/', admin.site.urls),
path('checklist/', include('checklist.urls')),
path('', include('pages.urls')),
Error:
ModuleNotFoundError: No module named 'checklist'
Fix:
Stop Server:
Run:
python manage.py startapp checklist
Run server:
python manage.py runserver
Error:
ModuleNotFoundError: No module named 'checklist.urls'
Fix:
Gotta do two things to fix that, first:
'checklist.apps.ChecklistConfig',
in INSTALLED_APPS
Then,
touch /Users/alans/django/tutorial_site/checklist/urls.py
Error:
Run the server and you'll get this giant error:
django.core.exceptions.ImproperlyConfigured: The included URLconf ' ' does not appear to have any patterns in it. If you see valid patterns in the file then the issue is probably caused by a circular import.
Fix:
Put this inside:
/Users/alans/django/tutorial_site/checklist/urls.py
Content:
from django.urls import path
from . import views
app_name = 'checklist'
urlpatterns = [
path('', views.checklist_homepage, name="checklist_homepage"),
]
TKTKTKT: Write up notes about how we're adding ``app_name`` and ``name=`` here at the start compared to last time when we added them after seeing errors. This is because we'll pretty much always need them.
Error:
AttributeError: module 'checklist.views' has no attribute 'checklist_homepage'
Fix:
Edit: /Users/alans/django/tutorial_site/checklist/views.py to add this.
def checklist_homepage(request):
return render(
request,
'checklist/checklist_homepage.html'
)
So, the full file will look like:
from django.shortcuts import render
def homepage(request):
return render(
request,
'pages/homepage.html'
)
Error:
TemplateDoesNotExist at /checklist/
checklist/checklist_homepage.html
Fix:
Make directories:
mkdir /Users/alans/django/tutorial_site/checklist/templates
mkdir /Users/alans/django/tutorial_site/checklist/templates/checklist
Make a new file with:
touch /Users/alans/django/tutorial_site/checklist/templates/checklist/checklist_homepage.html
Run the server:
python manage runserver
Our page loads, but there's nothing in it since ``checklist_homepage.html`` is blank. So, add this to it:
{% extends 'base.html' %}
{% block body_block %}
Checklist Page
{% endblock %}
That has it working .
Last thing we need to do, is update the link in the ``base.html`` template.
From:
Checklist
To:
Checklist
Now we've got something we can work with.
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
--
Next stop is the admin page:
First, make the super user:
Working with Forms and the admin:
Create the super user:
python manage.py createsuperuser
Choose whatever name you want and give it a password (you don't have to put in an email address)
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
--
http://127.0.0.1:8000/admin/
Let's add a link to our header (Django provides this out of the box)
Admin
So we get this:
Tutorial Site
Home Page
About Page
Checklist
Admin
{% block body_block %}{% endblock %}
Click the link and we'll hit our admin page.
There are a couple things there for users and groups. We don't need to mess with them right now, but click into them if you'd like.
In order to get our checklist showing up, we need to make our checklist stuff.
We'll need to do three things:
- Create our model definition
- Run migrations to update the database with the model definition
- Add the app to the admin dashboard
- Add this to this file:
/Users/alans/django/tutorial_site/checklist/models.py
Code:
class ChecklistItem(models.Model):
checklist_text = models.CharField(
"Checklist Item",
max_length=200
)
The full file will look like this:
from django.db import models
class ChecklistItem(models.Model):
checklist_text = models.CharField(
"Checklist Item",
max_length=200
)
Do the migrations with:
python manage.py makemigrations checklist
python manage.py migrate
Add it to the admin dashboard by updating the apps ``admin.py`` file. In this case, that's
/Users/alans/checklist/admin.py
Add these two lines:
from .models import ChecklistItem
admin.site.register(ChecklistItem)
So the full file looks like:
from django.contrib import admin
from .models import ChecklistItem
admin.site.register(ChecklistItem)
Now, refresh the page and you can add and remove checklist items as you see fit.
Note that when they are listed, it just says `ChecklistItem object (2)`. We'll fix that soon.
Make sure to add a few to play with.
Note that when you see the list, it'll say `ChecklistItem object (1)`, `ChecklistItem object (2)`, etc... To see the actual text, we can add a `__str__ method to your class.
Add this to `example/checklist/models/py`:
def __str__(self):
return self.checklist_text
So that the full file looks like this:
from django.db import models
class ChecklistItem(models.Model):
checklist_text = models.CharField(
"Checklist Item",
max_length=200
)
def __str__(self):
return self.checklist_text
Make sure to indent the new ``def __str__`` so that it's at the same level as `checklist_text`.
That's it for this section.
-- hr
We're going to add a debug thing for this tutorial only. Like a lot of debug stuff, you don't want to run with this in production. In fact, you don't want to run with with in general development. We're only using it here because it helps show how:
In /Users/alans/django/tutorial_site/tutorial_site/settings.py
Add this:
'string_if_invalid': 'INVALID_CALL',
Inside the ``OPTIONS`` section of `TEMPLATES`.
It should look like this:
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [str(BASE_DIR.joinpath('templates'))],
'APP_DIRS': True,
'OPTIONS': {
'string_if_invalid': 'INVALID_CALL',
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
Getting stuff to show up on the checklist_homepage
We'll do that piece by piece.
Start on our checklist page:
http://127.0.0.1:8000/checklist/
The way data gets to the template is by passing a "context" variable.
We'll work on these two files:
/Users/alans/django/tutorial_site/checklist/templates/checklist/checklist_homepage.html
/Users/alans/django/tutorial_site/checklist/views.py
We'll start with the template where we request a variable named `key`.
So, setup:
/Users/alans/django/tutorial_site/checklist/templates/checklist/checklist_homepage.html
With this:
{% extends 'base.html' %}
{% block body_block %}
Checklist Page
When we request "item" we get: {{ item }}
{% endblock %}
Look at the page, and you'll see:
When we request "item" we get: INVALID_CALL
That's because nothing is getting passed to the template. We can fix that by passing a dict by adding it to the end of the render. So, change this:
def checklist_homepage(request):
return render(
request,
'checklist/checklist_homepage.html'
)
To this:
def checklist_homepage(request):
return render(
request,
'checklist/checklist_homepage.html',
{ 'item': 'An Example Item' }
)
Now let's move that to it's own variable which will call `context`.
def checklist_homepage(request):
context = {
'item': 'Another Example Item'
}
return render(
request,
'checklist/checklist_homepage.html',
context
)
Now that we know how stuff moves, let's pass a list.
Note that we change ``item`` to ``items`` for clarity.
in the template:
When we request "items" we get: {{ items }}
in the view:
from django.shortcuts import render
def checklist_homepage(request):
items = [
"First Item", "Second Item","Third Item"
]
context = {
'items': items
}
return render(
request,
'checklist/checklist_homepage.html',
context
)
TKTKTKTKT: note the pattern of:
- load items
- put items in context
- put context in render
The output shows our list:
When we request "items" we get: ['First Item', 'Second Item', 'Third Item']
We can loop over the list like this:
{% for item in items %}
{{ item.id }} - {{ item }}
{% endfor %}
Now we're ready to bring in our data. We're going to make use of a Django specific call.
Add this:
from .models import ChecklistItem
Then switch out items to:
items = ChecklistItem.objects.all()
Temporarily Remove ``from .models import ChecklistItem`` Just so you know what happens if you forget to put in
That's it, your data is showing up on the page.
Huzzah!
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
--
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
--
More notes (originally from index-2.md)
Django 3 Tutorial - Linking Checklist
Now let's get the links going.
Note that the items we pulled in are full objects. Each object has an ID. (the reason we're seeing the string is the ``__str__`` method we added a while ago.)
To see the id, just do this: item.id inside the ``{{ }}`` tags
{% for item in items %}
{{ item.id }} - {{ item }}
{% endfor %}
Change to this:
{% extends 'base.html' %}
{% block body_block %}
Checklist Page
{% for item in items %}
{{ item }}
{% endfor %}
{% endblock %}
That gets the links going. Click on one and we'll see our next error (A 404 page not found)
Fix:
/Users/alans/django/tutorial_site/checklist/urls.py
To add:
path(' ', views.checklist_item),
So
urlpatterns = [
path(' ', views.checklist_item),
path('', views.checklist_homepage, name="checklist_homepage"),
]
Error:
AttributeError: module 'checklist.views' has no attribute 'checklist_item'
Fix:
def checklist_item(request, checklist_item_id):
context = {}
return render(request, 'checklist/checklist_item.html', context)
TODO: Talk about how ``checklist_item_id`` sends to the parameter
Error in browser:
TemplateDoesNotExist at /checklist/1
checklist/checklist_item.html
Make:
touch /Users/alans/django/tutorial_site/checklist/templates/checklist/checklist_item.html
The page will render, but there's nothing there.
http://127.0.0.1:8000/checklist/1
Note that the number may be different if you deleted checklist items.
Add this into the template:
{% extends 'base.html' %}
{% block body_block %}
Checklist Item
{% endblock %}
Now we can add our stuff to it with this inside:
/Users/alans/django/tutorial_site/checklist/views.py
This:
from django.shortcuts import render
to this:
from django.shortcuts import get_object_or_404, render
And get the function to look like this:
def checklist_item(request, checklist_item_id):
checklist_item = get_object_or_404(
ChecklistItem,
pk=checklist_item_id
)
context = { "checklist_item": checklist_item }
return render(
request,
'checklist/checklist_item.html',
context
)
Full file looks like:
from django.shortcuts import get_object_or_404, render
from .models import ChecklistItem
def checklist_homepage(request):
checklist_items = ChecklistItem.objects.all()
context = {
'checklist_items': checklist_items
}
return render(
request,
'checklist/checklist_homepage.html',
context
)
def checklist_item(request, checklist_item_id):
checklist_item = get_object_or_404(
ChecklistItem,
pk=checklist_item_id
)
context = { "checklist_item": checklist_item }
return render(
request,
'checklist/checklist_item.html',
context
)
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
--
Update Homepage URL (might want to move this)
{% for checklist_item in checklist_items %}
{{ checklist_item }}
{% endfor %}
With urls.py looking like:
from django.urls import path
from . import views
app_name = 'checklist'
urlpatterns = [
path(' ', views.checklist_item, name="checklist_item"),
path('', views.checklist_homepage, name="checklist_homepage"),
]
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
--
Make an edit page:
First, create a link in /Users/alans/django/tutorial_site/checklist/templates/checklist/checklist_item.html
Edit
Error in the browser:
NoReverseMatch at /checklist/1
Reverse for 'edit_checklist_item' not found. 'edit_checklist_item' is not a valid view function or pattern name.
Fix:
In:
/Users/alans/django/tutorial_site/checklist/urls.py
Add:
path(' /edit/', views.edit_checklist_item, name="edit_checklist_item"),
TODO: Figure out the best practies for url structures.
Error (Terminal):
File "/Users/alans/django/tutorial_site/checklist/urls.py", line 8, in
path('edit/ ', views.edit_checklist_item, name="edit_checklist_item"),
AttributeError: module 'checklist.views' has no attribute 'edit_checklist_item'
Add this to /Users/alans/django/tutorial_site/checklist/views.py
def edit_checklist_item(request, checklist_item_id):
checklist_item = get_object_or_404(
ChecklistItem,
pk=checklist_item_id
)
context = { "checklist_item": checklist_item }
return render(
request,
'checklist/edit_checklist_item.html',
context
)
Error (Browser):
TemplateDoesNotExist at /checklist/edit/1
checklist/edit_checklist_item.html
Fix:
echo "here" > /Users/alans/django/tutorial_site/checklist/templates/checklist/edit_checklist_item.html
TKTKTKTKTK: If you use ``echo`` explian it, up front.
Update it with this html:
{% extends 'base.html' %}
{% block body_block %}
Edit Checklist Item
{% csrf_token %}
{% endblock %}
This just gets us working. To see it, now we can update the form call with this which will do an expected break:
Error:
NoReverseMatch at /checklist/edit/1
Reverse for 'update_checklist_item' not found. 'update_checklist_item' is not a valid view function or pattern name.
Fix:
Add this to /Users/alans/django/tutorial_site/checklist/urls.py
path(' /update/', views.update_checklist_item, name="update_checklist_item"),
Error:
File "/Users/alans/django/tutorial_site/checklist/urls.py", line 8, in
path(' /update/', views.update_checklist_item, name="update_checklist_item"),
AttributeError: module 'checklist.views' has no attribute 'update_checklist_item'
Fix:
This time, we're going to make a redirect first since the update won't actually have a page.
Add this to the top of /Users/alans/django/tutorial_site/checklist/views.py
from django.http import HttpResponseRedirect
from django.urls import reverse
And add this new function:
def update_checklist_item(request, checklist_item_id):
checklist_item = get_object_or_404(ChecklistItem, pk=checklist_item_id)
checklist_item.checklist_text = request.POST['checklist_update_text']
checklist_item.save()
return HttpResponseRedirect(reverse('checklist:checklist_item', args=(checklist_item_id,)))
Make sure that you have that last comma before the close parenthesis at:
args=(checklist_item_id,)))
The full file should now look like:
from django.shortcuts import get_object_or_404, render
from django.http import HttpResponseRedirect
from django.urls import reverse
from .models import ChecklistItem
def checklist_homepage(request):
checklist_items = ChecklistItem.objects.all()
context = {'checklist_items': checklist_items }
return render(
request, 'checklist/checklist_homepage.html', context
)
def checklist_item(request, checklist_item_id):
checklist_item = get_object_or_404(
ChecklistItem,
pk=checklist_item_id
)
context = { "checklist_item": checklist_item }
return render(
request, 'checklist/checklist_item.html', context
)
def edit_checklist_item(request, checklist_item_id):
checklist_item = get_object_or_404(
ChecklistItem,
pk=checklist_item_id
)
context = { "checklist_item": checklist_item }
return render(
request, 'checklist/edit_checklist_item.html', context
)
def update_checklist_item(request, checklist_item_id):
checklist_item = get_object_or_404(
ChecklistItem,
pk=checklist_item_id
)
checklist_item.checklist_text = request.POST['checklist_update_text']
checklist_item.save()
return HttpResponseRedirect(
reverse('checklist:checklist_item', args=(checklist_item_id,))
)
TKTKTKT: Put in:
{% if error_message %} {{ error_message }} {% endif %}
With an example:
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
--
Orignally from "original_notes_v2.md"
TKTKTKT: Figure out where to put this:
Django Basic URL structure:
- ./checklist/ - the index page
- ./checklist/1 - detail pages.
- ... add
- ... update
- ... delete
Now, working on edit,
Put link on individul page to that points to the edit page:
e.g. Edit
TKTK find a better way to do that.
Error: 404:
Fix: Update urls.py
path(' /edit', views.checklist_item_edit, name='checklist_item_edit'),
New Error:
AttributeError: module 'checklist.views' has no attribute 'checklist_item_edit'
Fix: add to views:
def checklist_item_edit(request, checklist_item_id):
checklist_item = get_object_or_404(ChecklistItem, pk=checklist_item_id)
context = {"checklist_item": checklist_item}
return render(request, 'checklist/checklist_item_edit.html', context)
New error in browser:
TemplateDoesNotExist at /checklist/1/edit
checklist/checklist_item_edit.html
Fix: add template:
checklist_item_edit.html
{% extends 'base.html' %}
{% block body_block %}
Edit Checklist Item
{% csrf_token %}
{% endblock %}
TODO: Make sure you've explained how URLs work and use the numbers here.
TODO: Figure out when you need to add this to checklist/urls.py
app_name = 'checklist'
Error:
NoReverseMatch at /checklist/1/edit
Reverse for 'checklist_item_update' not found. 'checklist_item_update' is not a valid view function or pattern name.
Need this:
path(' /update', views.checklist_item_update, name='checklist_item_update'),
Then you'd get this error:
AttributeError: module 'checklist.views' has no attribute 'checklist_item_update'
which you fix with a view, which we want to do this with.
Start with a redirect before editing the data:
in views.py add these:
from django.http import HttpResponseRedirect
from django.urls import reverse
and then this for the new function in views.py
def checklist_item_update(request, checklist_item_id):
checklist_item = get_object_or_404(ChecklistItem, pk=checklist_item_id)
checklist_item.checklist_text = request.POST['checklist_update_text']
checklist_item.save()
return HttpResponseRedirect(reverse('checklist:checklist_item', args=(checklist_item_id,)))
Make sure to have that last comma in ``args=(checklist_item_id,)`` otherwise things will explode.
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
-- hr
--
Originally from "original_notes.md"
October 9, 2020 - working session:
Testing:
- NOTE: The mozilla tutorial says to make `example/checklist/tests/test_models.py`, but that isn't found when you run `python manage.py test`.
- NOTE: Don't really use this, it's just for your reference. Skip to the next item. This is the example code with a failing test. file example/checklist/test.py so it looks like this (TKTKTK - refine this down):
from django.test import TestCase
class YourTestClass(TestCase):
@classmethod
def setUpTestData(cls):
print("setUpTestData: Run once to set up non-modified data for all class methods.")
pass
def setUp(self):
print("setUp: Run once for every test method to setup clean data.")
pass
def test_false_is_false(self):
print("Method: test_false_is_false.")
self.assertFalse(False)
def test_false_is_true(self):
print("Method: test_false_is_true.")
self.assertTrue(False)
def test_one_plus_one_equals_two(self):
print("Method: test_one_plus_one_equals_two.")
self.assertEqual(1 + 1, 2)
- This is how you do a models test `example/checklist/test.py`.
from django.test import TestCase
from checklist.models import ChecklistItem
class ChecklistItemTest(TestCase):
def test_checklist_text_works(self):
ChecklistItem.objects.create(checklist_text='Make this work')
expected = 'Make this work'
actual = ChecklistItem.objects.get(id=1).checklist_text
self.assertEquals(expected, actual)
TODO: Exapliain this and everything else line by line.
- Move the object creation into its own function:
from django.test import TestCase
from checklist.models import ChecklistItem
class ChecklistItemTest(TestCase):
@classmethod
def setUpTestData(cls):
ChecklistItem.objects.create(checklist_text='Make this work')
def test_checklist_text_works(self):
expected = 'Make this work'
actual = ChecklistItem.objects.get(id=1).checklist_text
self.assertEquals(expected, actual)
- Now, let's do a red green test pattern. TKTKTKT, these need to be refined.
First, create a new test that checks to see if a boolean field exists.
# Intentional Red to Prove the Test is wired up properly
def test_is_done(self):
expected = True
actual = False
self.assertEquals(expected, actual)
# Shameless Green to get back to green
def test_is_done(self):
expected = True
actual = True
self.assertEquals(expected, actual)
# Fails because we haven't added it yet.
def test_is_done(self):
expected = True
actual = ChecklistItem.objects.get(id=1).is_done
self.assertEquals(expected, actual)
AttributeError: 'ChecklistItem' object has no attribute 'is_done'
- now add it with shameless green as a simple return true.
is_done = True
Now, we're green again and have a backstop to do our work under.
The first thing we're going to do is add a line that sets is_done to a model value from Django.
It's going to fail. Don't be surprised about that. This is effectively a way to make a test go red. That's what we want for the next step in our cycle.
is_done = False
is_done = models.BooleanField(default=False)
We're leaving ``is_done = False`` in there so we can get back to it (and Green) any time we want.
But, when we run tests with ``is_done = models.BooleanField(default=True)`` in place, it'll bomb with a bunch of text that ends something like:
django.db.utils.OperationalError: no such column: checklist_checklistitem.is_done
This is a sign that we haven't created and run the migrations that tell the database about the updated fields in the model.
We tell Django that we want to make migrations for the checklist model with:
python manage.py makemigrations checklist
And then apploy those migrations with:
python manage.py migrate
-- hr
TKTKTKT Add this somewhere:
Show the is_done boolean by removing ``admin.site.register(ChecklistItem)`` and adding this to example/checklist/admin.py
@admin.register(ChecklistItem)
class ChecklistItemAdmin(admin.ModelAdmin):
list_display = ('checklist_text', 'is_done')
So, it'll look like this:
from django.contrib import admin
from .models import ChecklistItem
@admin.register(ChecklistItem)
class ChecklistItemAdmin(admin.ModelAdmin):
list_display = ('checklist_text', 'is_done')
TKTKTKT: add this in the approprate place:
Upadgte views.py to have this:
from django.shortcuts import get_object_or_404, render
so that this ``get_object_or_404`` works.
-- hr
October 10, 2020
Testing: Views:
TODO: See if you can get this test structure to work:
catalog/
/tests/
__init__.py
test_models.py
test_forms.py
test_views.py
- Next setup checklist_item:
TODO: Setup link in checklist/templates/checklist/index.html to point to individual checklist first so you can see the error message.
Build template with:
checklist item
Add view method:
def checklist_item(request, checklist_item_id):
context = {}
return render(request, 'checklist/checklist_item.html', context)
Add url for checklist/urls.py
urlpatterns = [
path(' ', views.checklist_item, name='checklist_item'),
path('', views.index, name='index'),
]
That's how you pass the number.
-- hr
Making the forms:
Create: example/checklist/forms.py with
with:
from django import forms
class UpdateChecklistItemName(forms.Form):
checklist_text = forms.CharField(
help_text="Update it."
)
TODO: Make a page that you can target with edit.
Things you did to work through the order of:
add this to checklist/views.py
from checklist.forms import UpdateChecklistItemName
switched views to have this (needs work)
def checklist_item(request, checklist_item_id):
checklist_item_object = get_object_or_404(ChecklistItem, pk=checklist_item_id)
context = {
'checklist_item_object': checklist_item_object,
'form': UpdateChecklistItemName(
initial={'checklist_text': checklist_item_object.checklist_text }
),
}
return render(request, 'checklist/checklist_item.html', context)
TODO: Clean up the form.
Add this to views:
from django.http import HttpResponseRedirect
from django.urls import reverse
~ fin ~