Locked learning resources

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

Unlock This Lesson

Locked learning resources

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

Unlock This Lesson

Display a Single Project: Recap

Congratulations on making it nearly to the end of this section! Here’s what you did:

  • Used the Django ORM in the shell and in views.py
  • Learned about primary keys (pk)
  • Used angle brackets in your urls.py path objects in order to be able to capture specific parts of a URL and pass them forward as arguments
  • Used an int: path converter to verify the URL path’s datatype
  • Defined proper namespaces for linking and reverse URL matching
  • Passed objects inside URL template tags
  • Debugged the NoReverseMatch error
  • Learned about template inheritance
  • Styled with Bootstrap
  • Made the site mobile responsive
  • Used the Django admin interface

On top of all that, you also have a functioning portfolio site that you can show to potential employers!

00:00 Hey, and welcome to this nearly final section of the course where, in old tradition, we’re going to recap what we did in this section. We started off using the Django ORM—first in a shell and then, later on, we also built it in the views.py. We used it, in this case, to access one specific item from our database, and we did that using primary keys.

00:23 We learned about this pk variable that you might see around in different Django projects and how it relates to the id column that gets automatically generated.

00:32 The next thing we did is we used angled brackets inside of our urls.py path() objects

00:39 in order to be able to capture specific parts of a URL. Then, we were able to pass those forward as arguments, eventually. What we did before actually passing it on is we used the path converter—in our case, an integer path converter—to make sure that what the user enters in the URL is actually going to be useful for what we’re passing forward to the view. In our case, we wanted to make sure that this is an integer and not some kind of string.

01:09 Then, we dove a bit into how does this linking between different templates work. So, we made sure that we know how to define proper namespaces. That helps us both in linking templates together, as well as reverse URL matching.

01:23 And what’s necessary for that is to define the right things in urls.py. We need to define the app_name variable, and we need to define the name keyword for each of our path() objects.

01:36 Another thing we took a look at is how do we deal with this if our path() requires arguments. So, when we’re using these angled brackets and then we want to link forward, we also need to pass that argument so that we can actually find the correct URL.

01:51 Yeah, we talked about how to do that correctly.

01:55 We also took it as a chance to debug the NoReverseMatch error, an error that you might see very often. We narrowed it down to looking at two files, specifically: urls.py and whichever template you’re using .html. Make sure that two important things are squared out.

02:13 So, we want to make sure that Django can identify the right view. We need the namespace for this, so, app_name and the name of the path(), as well as that in case we’re passing arguments, that this is done correctly.

02:26 So, check out this debugging video again, for the process of where to look and how to make sure that—most likely—you’re going to resolve your NoReverseMatch error if you follow these steps.

02:37 Then, we went forward to take a look at template inheritance. We did this following the DRY principle, which means don’t repeat yourself. So, we tried to get rid of a lot of repetitive HTML code that we had in our project by just creating one parent template that the other templates are inheriting from. The two important parts for this is we need to create this {% block content %} with {% endblock %} that opens the space where the child templates can put in the HTML.

03:05 We also need to extend the name of whatever is our parent template. Two simple steps allows us to get rid of quite a lot of repetitive code.

03:16 After creating this base template, we also took a stab at making our style a bit better. We created a Bootstrap header inside of base.html that would appear on all of the pages that we create.

03:29 We also took a very simple step to make our site mobile-responsive by just adding this line of code inside of our base.html. And just to give you a heads up, this doesn’t necessarily always work for each page.

03:43 This just works because we’re using Bootstrap, and Bootstrap is designed for being mobile-responsive. That’s why adding this line of code actually helps us to make it look so good also on a smaller device. Finally, we took a look at the Django admin interface.

04:00 We had to first create a superuser to be able to access it. Then, we needed to register our models, the Project model, in our Django admin.

04:10 We did that in admin.py in our project. After we had done all this, it allowed us to very easily add, edit, or delete database entries in a very convenient way. You can see here, the mobile-responsive admin interface and how I can simply add things here, or edit it, and keep your database up-to-date, essentially, which is crucial if you want to have a good portfolio that you can also show to people. And that’s it! Congratulations!

04:40 You did a good job. If you went through this whole tutorial, you now have a functioning portfolio site that actually looks pretty good, that allows you to keep it up-to-date pretty easily, and I hope you go ahead and add all the cool projects that you’ve built already. Put in a screenshot, put some description, and if you show this project to some potential employers, I’m sure that they’re going to be impressed.

05:04 So, let’s take a quick look at what we built.

05:09 What you should have so far if you followed everything is a project that looks like this. We have a navigation bar up top. Currently, this just takes us to our project index page where we have all of the projects. They’re listed up like this.

05:24 We can click on those to get to a details view of each entry in our database and click up here to get back again. Click here, it takes us to a different one. Everything looks pretty nice already.

05:37 We have some description about the project, technologies that it was built with, and an image.

05:44 We also have the admin interface that Django comes built-in with,

05:50 and it allows us to add a project or delete a project very easily…

06:22 which then also gets reflected right away on our project page.

06:30 So, that’s it for today. I’m Martin and I hope you enjoyed this mini-course on getting started with Django, building your portfolio app. I want to thank you for going all the way through the course, and I want to make sure that you also head over to the final video where I’m going to talk a little bit about next steps on what you can do to make your portfolio look even more awesome than it already does. All right, see you in the final video.

Avatar image for Martin Breuss

Martin Breuss RP Team on July 26, 2021

Hm… @KatMac how does your portfolio/urls.py file in your management app look like where you’re routing to projects/urls.py?

When you look at the error message you’re getting, it seems that there’s an additional /projects part snipped off your URL path:

Page not found (404)
Request Method:     GET
Request URL:    http://127.0.0.1:8000/projects/

Using the URLconf defined in portfolio.urls, Django tried these URL patterns, in this order:

    admin/
    projects/ /projects [name='projects']
    projects/ <int:pk> [name='project_detail']

The current path, projects/, didn’t match any of these.

The first part shown is the URL you’re expecting, but there’s another one after it. It looks just like the <int:pk> that Django snips off if there’d be a number instead.

So it seems you’ve defined the routing from portfolio/urls.py to projects/urls.py already with the path "projects/", which is why you’d get a double projects/ URL here, with the second part snipped off.

What happens when you keep an empty path in projects/urls.py:

# projects/urls.py
app_name = "projects"

urlpatterns = [
    path("",   views.all_projects, name="projects"),
    path("<int:pk>",  views.project_detail, name="project_detail")
    ]

Let me know whether that fixes it, and if so, give me your interpretation on why it does. :)

Avatar image for KatMac

KatMac on July 27, 2021

It worked. Thank you. The empty string indicates the root path?

Avatar image for Martin Breuss

Martin Breuss RP Team on July 27, 2021

Great! :)

The empty string indicates that there’s nothing following the initial path that directed people to this endpoint.

So, if you have “projects” already in your other urls.py file, then the empty path here means that there’s nothing more:

http://localhost:8000/projects

If you have another “projects” string in there, as you initially had, then the matching URL would be:

http://localhost:8000/projects/projects

Each string in a path matches a string in the URL, then chops it off (and optionally passes the rest onwards to another URL config, if it’s set up like your portfolio/urls.py file). Does that clear it up? Glad it’s working now : )

Avatar image for KatMac

KatMac on July 28, 2021

Thank you. I learned so much from this video. Please do more, a lot more

Avatar image for Vasanth Baskaran

Vasanth Baskaran on Aug. 23, 2021

Thanks Martin for this course :)

Avatar image for rya1992

rya1992 on Feb. 26, 2022

i am happy because i started learn django with you not from random videos on youtube you make it easy and sequentially i really love django right now i applied everything in this course except bootstrap part sorry i don’t like styling even in my real project :)

Avatar image for Stephen Brodigan

Stephen Brodigan on May 24, 2024

Martin, thank you for this fantastic course, I had resisted working with Django, largely because I would not have a clue where to start, your course broke down all the trepidation and I love Django.

Avatar image for Alex

Alex on March 28, 2025

Thank you a lot, Martin, for a tremendously fun and engaging course that has really whetted my appetite for more Django :)

Avatar image for Garry

Garry on May 16, 2025

Thanks Martin, your course was a useful refresher after many years away from Django. Hope there are some more Django courses in the pipeline from Real Python.

Avatar image for jarmo2

jarmo2 on July 27, 2025

Still worth following the course in 2025 :)

Become a Member to join the conversation.