Join us and get access to hundreds of tutorials and a community of expert Pythonistas.

Unlock This Lesson

This lesson is for members only. Join us and get access to hundreds of tutorials and a community of expert Pythonistas.

Unlock This Lesson

Hint: You can adjust the default video playback speed in your account settings.
Hint: You can set the default subtitles language in your account settings.
Sorry! Looks like there’s an issue with video playback 🙁 This might be due to a temporary outage or because of a configuration issue with your browser. Please see our video player troubleshooting guide to resolve the issue.

Create a Template

In this lesson, you’re going to create a template for your Django app so you won’t have to paste all your HTML directly into your views. Django has already provided you with the import statement you’re going to need for this:

from django.shortcuts import render

Now that you have render(), use it in your function with the template name 'projects/index.html':

# Create your views here.
def project_list(request):
    return render(request, 'projects/index.html')

00:00 In this video, we’re going to go ahead and create a template for our Django app, to improve on not having to paste all the HTML directly into our views.

00:12 This is what we’ve been doing so far. I took away this whole bunch of HTML that we had before and just put back our little message. But what we’ve been doing is essentially pasting HTML directly into our view.

00:25 Now, we want to avoid this and instead, we want to render a template. So, Django gives us already in the views— because this is the common way of doing it—it already gives us this import statement that we’re going to use.

00:39 I’m going to get rid of this, and instead, I’m going to return this render() function. Let’s take a look at this again.

00:49 We’re going to need a template_name and then there’s some other things that default to None—so we’re just not going to worry about those for now—but we’re going to need to pass a request and the template_name.

01:02 So, the request I mentioned before is this Django object that keeps getting passed around because it contains a lot of helpful information.

01:10 So I’m just going to pass this again to render(), as requested, and then we’re going to also pass in a template_name. I’m going to name this template, let’s say… We’re inside of our projects app, so I’m gonna say 'projects' and then 'index.html'.

01:36 Okay, so we don’t need this anymore, but instead, now we’re rendering a template. What’s our server doing? Something changed, reloading, no complaints. Let’s take a look into the browser. What if I reload this?

01:56 Ooh, look at that. We get a new friend! This one’s called TemplateDoesNotExist at /projects/. So, it’s telling us very clearly that this template does not exist.

02:10 We’re going to have to do something. Where is it looking? It’s looking at projects/index.html. Okay, cool. So, this gives us already some kind of hint on how to do this, but the main thing is that we’re going to know Django understands we’re pointing to a template. It can’t find this template.

02:28 It’s just not there, so let’s go ahead and create it.

02:35 The standard in Django for doing this is that inside of your app, we’re going to create a new folder called templates.

02:47 Then, inside of here, another new folder that we give the same name as our app. In this case, it’s going to be projects. You might wonder, “What’s this weird double folder structure?” and I’m going to talk about this in just a moment.

03:01 Let’s finish up solving this error message before. So we have now projects/, this is this folder, okay, projects/. And then we need, inside of there, index.html. So, making a new file:

03:17 index.html. Cool! So, PyCharm already creates this HTML basic structure so that we don’t have to worry about this. Otherwise, you just type it out. In this case,

03:33 that’s what we’re going to say here, and take a look what our server is saying. It’s doing fine, a GET request, no complaints here. So we head back over to our browser and reload the page, and we’re still getting a TemplateDoesNotExist.

03:52 Let’s figure out why is that the case. And the reason for it is that we have not registered this templates/ folder. So, sometimes Django does this by default, that it finds all the templates/ folders inside of all the projects, but if you’ve set it up like this and you keep getting the TemplateDoesNotExist error, as we do right here, we’re going to have to go forward and register it.

04:21 This is our second excursion into the settings.py file. Similar to how we have INSTALLED_APPS, we can zoom around and then we find a setting that’s called TEMPLATES. Inside of TEMPLATES, there is a key called 'DIRS' (directories), and this is where we want to register our new templates/ folder.

04:45 For making this easier, I’m going to utilize a variable that we have in here called BASE_DIR (base directory). It’s defined up there and it just tells us the root of our Django project, so I can utilize that to from there navigate to a different path.

05:02 It makes it a little easier. So, heading back to—here we are—heading back to this 'DIRS' folder, I’m going to add here os.path.join()

05:18 just to create a proper path—and then here, I’m going to say BASE_DIR and hook it together with what we’re calling 'templates/projects'sorry, BASE_DIR, 'projects/templates'.

05:54 So, let’s see whether that solved our issue. We head back over here, reload this. Sometimes this happens, you just have to wait for the server to restart. Try it again, and here we are.

06:08 So now, Django can find our template because we told it where to look. We just told it to look relatively from our base directory inside of our projects app, and then inside of the templates directory. Cool.

06:21 So, if you set up everything as you expect it and it’s not finding it, head over to your management app inside of the settings file, find the 'DIRS' and then just add—you can just copy-paste this, and then just change whatever is the name of your app. It should be able to find it afterwards.

06:40 All right! So, now we could go in here and add whatever HTML we want, and we have a nicer separation between the views, where we’re now simply just pointing to this template, an actual HTML file Django template where we can write tons and tons of HTML, as much as we want to, as well as some Django templating language, which we’re going to look at later on. Before we move on, though, in the next video, we’re going to take a quick look about why do we want to have this weird double structure? We’re inside of projects/, we make a templates/ folder, we make another projects/ folder, and only then we place our template in there.

07:22 So, see you in the next video where we’re going to talk about why this is a good thing to do.

Gascowin on Oct. 19, 2019

May i ask; why is it that in views.py you have

def project_list(request):
    return render(request,'projects/index.html') 

instead of

def project_list(request):
    return render(request,'templates/projects/index.html') 

? My understanding here is that we are passing in the path to the file index.html and since views.py and the templates folder are in the same directory we would need the path ‘templates/projects/…’ instead of ‘projects/…’ Thanks in advance.

Martin Breuss RP Team on Oct. 19, 2019

Great question! You are completely right in that we are pointing Django to the location of the index.html file.

The reason we don’t need to spell out templates, is that Django by default searches for your HTML files in a templates folder, where it aggregates all the templates from your whole project.

I talk about it some more in the video about Django’s Double-Folder Structure, but essentially the reason is that templates is defined as Django’s default location for templates and therefore doesn’t have to be explicitly mentioned.

reblark on Oct. 30, 2019

I have checked my code and tried this several times but consistently get the error that “projects/index.html” does not exist. But it does and it is in the templates/projects folder. I thought at first it was like your experience in the video that the file had not been registered, but surely it’s registered after all these attempts. Here is the template-loader-postmortem: Django tried loading these templates, in this order:

Using engine django:
django.template.loaders.filesystem.Loader: /Users/reb/Django/Django-CA/projects/templates/projects.index.html (Source does not exist)
django.template.loaders.app_directories.Loader: /Users/reb/Django/Django-CA/rbvenv/lib/python3.7/site-packages/django/contrib/admin/templates/projects.index.html (Source does not exist)
django.template.loaders.app_directories.Loader: /Users/reb/Django/Django-CA/rbvenv/lib/python3.7/site-packages/django/contrib/auth/templates/projects.index.html (Source does not exist)
django.template.loaders.app_directories.Loader: /Users/reb/Django/Django-CA/projects/templates/projects.index.html (Source does not exist)

The file is there!

Dan Bader RP Team on Oct. 30, 2019

@reblark Looks like you might have a typo in the code somewhere where you’re referencing that index.html file:

/Users/reb/Django/Django-CA/projects/templates/projects.index.html 

Instead of projects/index.html it says projects.index.html (. instead of /).

reblark on Oct. 31, 2019

Hi Martin, Again, thanks for the response and suggestions but I need to tell you a little story. I looked at my code many, many times this morning and thought it was right. I could not find a mistake. So, I took a break and on the break, I tripped and busted my knee cap. I spent the next several hours in the ER. I came back discouraged, but after resting awhile, started this video over again. In the very first code presentation, views, I found the mistake. I couldn’t believe it. I made a simple change and ‘voila,’ it worked. I don’t know what to make of this. Maybe I just don’t have what it takes to be a programmer. I make too many mistakes and too many times I just can’t see those mistakes no matter how hard I look. However, if I continue, I believe that programming well will teach me something and improve my discipline. Thanks again for your graciousness.

Martin Breuss RP Team on Oct. 31, 2019

Don’t give up! Everyone runs into these situations where you just can’t find the error. Taking a break and coming back to it in a bit is often the best way to tackle it. I’d suggest to avoid trips to the ER, though if possible–there are definitely more fun ways to take a break!

Glad you figured out your error, and most importantly I hope everything’s alright with your knee!

Keep up the work if you find it interesting. Learning to program can definitely be a great asset in many ways. :)

reblark on Nov. 1, 2019

I don’t believe this. The response from Dan Bader was exactly right. It took me a day to find it, but I did find. I just saw Dan’s response for the first time and I am so impressed that someone actually answered my question. I think in my long missive whining about MOOCs I mentioned a company in Florida that was bought by Pluralsight who had a guy who as absolutely great at answering questions. I’ll be doing more courses with Real Python. I might learn something.

eclaudio on Dec. 2, 2019

I just attempted to push this group of files up to GitHub and had over 4518 files that are about to push. Is this correct? And if not, what did I do wrong?

Martin Breuss RP Team on Dec. 3, 2019

Hello @eclaudio. You probably committed your whole Virtual Environment to GitHub. To avoid pushing all these files, you should add your .env to your .gitignore file.

eclaudio on Dec. 3, 2019

@MartinBreuss 1. Thank you 2. the detail is great.

If we are using the above project as the file structure, where is the best place to create my .gitignore file?

eclaudio on Dec. 3, 2019

You Guys ROCK!!!

Martin Breuss RP Team on Dec. 3, 2019

.gitignore should be always right in the base folder where you initiated your repo with git init.

And thanks! You rock 🚀! :))

eclaudio on Dec. 4, 2019

@MartinBreuss yup it worked thank you!!

Martin Breuss RP Team on Dec. 7, 2019

Great that it worked! I’d suggest to keep the .gitignore file one level higher, in your django-portfolio folder. That should be the root folder of your project. However, it looks like it’s now on the same level as your manage.py file?

josemariapolanco on Dec. 25, 2019

Why can’t we specify projects.templates inside of TEMPLATES in settings.py? It looks a lot cleaner than what’s suggested in the video.

Martin Breuss RP Team on Dec. 29, 2019

You can specify what templates Django looks into in settings.py in the TEMPLATES list in the DIRS list. Here’s more to read about it in the official docs. Generally, there’s a lot you can customize when using Django, and that includes how to handle templates. If it feels cleaner to do it differently, do give it a go. :)

Rodrigo Vieira on Jan. 21, 2020

I could fix the “Template Not Found” error by just rerunning the ./manager.py runserver command, without registering the templates directory in the TEMPLATES list in the settings.py file.

Brandy Wright on March 3, 2020

The tutorials and state of the files on screen video dont match. I believe it is because on one of the first tutorials in this course, I saw the .zip of the project.

So some stuff already works for me and I’m not getting the errors (errors are the point of this chapter).

The strange thing is parts that work when the file or text is NOT present.

For example: templates was already registered but the ‘DIRS’: was just []. The template still loads fine.

I added to it [os.path.join(BASE_DIR, 'projects/templates')], its still working. I’m curious about this…

Martin Breuss RP Team on March 5, 2020

Hi @Brandy Wright. The .zip file of the project already contains the finished code. To get the full experience of actively running into all the errors we’re covering here, I’d suggest to avoid that file and build the project from scratch. Just follow this tutorial step-by-step and you should be fine :)

Template DIRS

As for what you noticed with the DIRS list being empty in settings.py: Django automatically registers template directories in registered apps, when they are called exactly that: templates. So there is no need to add these folder paths to settings.py. You can define different directories as template directories, but then you will need to add them to the DIRS list.

I personally think it makes sense to be explicit and even record the template dirs of your apps in here.

Tomas Menito on March 9, 2020

Hi, great tutorial!

I have one quick question that is, instead of:

os.path.join(BASE_DIR, 'projects/templates')

Would it be better to use:

os.path.join(BASE_DIR, 'projects', 'templates')

To be more generic about os paths?

Thanks

Martin Breuss RP Team on March 9, 2020

Hi @Thomas Menito and thanks for your comment! Yes, you’re absolutely right. With os.path.join it would be better to use:

os.path.join(BASE_DIR, 'projects', 'templates')

That makes it more general and avoids hard-coding the path separator, which might be different on different operating systems. Thanks for your 🦅👁 and for commenting here! 🙌

Andrew on May 26, 2020

Hello, In os.path.join(BASE_DIR, ‘projects/templates’) why don’t you have ‘projects/templatets/projects’) ? How does django know that it needs to go inside the second folder projects to get to the file index?

Martin Breuss RP Team on May 28, 2020

Hi @Andrew - great question :)

If you look a bit closer in the TEMPLATES settings dictionary right there in settings.py, you will find another option called APP_DIRS that is by default set to True:

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [],
        'APP_DIRS': True,
        # more stuff
    },
]

This setting ensures that Django automatically considers all files that are located inside of a folder called templates/ inside of each of your apps that you have registered in your project. You can experiment with setting it to False and notice how the templates are not found anymore.

eawongtheprogrammer on Aug. 30, 2020

I get an error:

NameError: name 'os' is not defined

I have have to insert import os at the beginning of settings.py

Is this normal?

Martin Breuss RP Team on Aug. 30, 2020

Hi @eawongtheprogrammer, the import for the os module should already be at the top of your settings.py file by default, if you used the way to create a Django project as described in this course.

You can check at this video at the timestamp ~4:50, where you can see that the import is necessary, but also should already be in your file.

Maybe you created your Django project in a different way, or maybe you accidentally deleted that import?

mmcdonell on Sept. 19, 2020

@eawrongtheprogrammer The import was never there. I ran into the same issue. I am now getting the following error after adding the import os:

NameError: name 'dirname' is not defined

Has something changed in the Django setup? Please adivse. Thanks.

Bartosz Zaczyński RP Team on Sept. 21, 2020

If you imported the os module directly, you must refer to dirname using its fully qualified name:

>>> os.path.dirname('/path/to/file.txt')
'/path/to'

Alternatively, you could import the function itself:

>>> from os.path import dirname
>>> dirname('/path/to/file.txt')
'/path/to'

Yes, Django switched from using a somewhat old os module in favor of the pathlib one, which we have a great tutorial about. (Martin uses Django 2.2.1, while the latest version of the framework is 3.1.1.)

mmcdonell on Sept. 23, 2020

To solve the error above I migrated to the updated version of django which is outlined in the django documentation docs.djangoproject.com/en/3.1/ . After I picked up from create an app to make sure I still had the same parameters that were displayed in the tutorial. The following BASE_DIR variable should be set to:

from pathlib import Path

BASE_DIR = Path(__file__).resolve().parent.parent

the template DIR value should be:

'DIRS': [BASE_DIR / 'templates' ],

benirvingnz on Sept. 26, 2020

Thank you mmcdonell - very helpful!

Become a Member to join the conversation.