Django 201: Intermediate Django Development | Kalob Taulien | Skillshare

Django 201: Intermediate Django Development

Kalob Taulien, Web Development Teacher

Django 201: Intermediate Django Development

Kalob Taulien, Web Development Teacher

Play Speed
  • 0.5x
  • 1x (Normal)
  • 1.25x
  • 1.5x
  • 2x
44 Lessons (3h 41m)
    • 1. Welcome to intermediate Django (Django 201)

      2:49
    • 2. Creating a virtual environment

      4:42
    • 3. Starting a new Django project

      2:38
    • 4. Adding a .gitignore file

      3:02
    • 5. Your first app

      2:57
    • 6. Your first model

      3:16
    • 7. Registering your model wth the Django admin

      2:39
    • 8. Adding a DateTimeField to your Posts

      2:08
    • 9. How to list all the posts on your Home Page

      7:03
    • 10. Setting up your template locations

      2:49
    • 11. Your first template

      1:24
    • 12. Your base template

      2:44
    • 13. Template blocks

      3:39
    • 14. Template loops

      3:57
    • 15. Installing Django Allauth

      9:43
    • 16. Global login and logout links

      2:33
    • 17. Extending Allauth templates

      8:21
    • 18. Adding Tailwind CSS

      6:46
    • 19. Tailwind style posts

      5:33
    • 20. Adding a Profile app

      9:00
    • 21. Adding a thumbnail package

      5:46
    • 22. Adding media settings

      4:57
    • 23. Adding authors to posts

      2:48
    • 24. Adding author data to templates

      5:12
    • 25. Post detail view

      4:12
    • 26. Post detail template

      4:14
    • 27. Include tags

      5:04
    • 28. Post CreateView

      5:58
    • 29. Forcing authentication

      1:41
    • 30. Creating a new post

      6:03
    • 31. Tailwind CSS modal

      4:52
    • 32. Enabling static files

      5:42
    • 33. Adding JavaScript

      3:08
    • 34. Toggling the modal using JavaScript

      2:37
    • 35. Ajaxing new posts

      17:07
    • 36. Profile detail view

      5:48
    • 37. Profile detail template

      4:59
    • 38. Total posts

      3:24
    • 39. Adding a follow button

      3:40
    • 40. The follower app

      4:57
    • 41. Following and unfollowing

      17:31
    • 42. Dynamically follow and unfollow

      3:41
    • 43. Dynamic HomePage Posts

      7:29
    • 44. Your project

      2:37
  • --
  • Beginner level
  • Intermediate level
  • Advanced level
  • All levels
  • Beg/Int level
  • Int/Adv level

Community Generated

The level is determined by a majority opinion of students who have reviewed this class. The teacher's recommendation is shown until at least 5 student responses are collected.

37

Students

--

Projects

About This Class

Welcome to Django 201: Intermediate Django Development. 

In this class we're going to be building a large social-network style web application together called Today I Learned, and the premise is to share what you've learned in small bit-sized pieces, much like Twitter. 

Unlike Django 101 where we created an Instagram-like clone, Django 201 is about a Twitter-like clone.

We'll be starting at the very beginning with virtual environment, starting a new django project, and creating a new app inside of it. Then we'll be extending the default User to have a Profile where we can add extra account details and upload an image. 

We're also tackling Django Authentication in this class. Yes, you'll be able to let users sign up, login, and logout.

We'll be using include tags, 3rd party packages, exploring 3rd party packages, and creating new posts on the fly. 

But most importantly we'll be mixing JavaScript and Django so people can dynamically create new posts without ever leaving the homepage. 

I have purposely left some bugs in the code for you to work on in your final project. This is AMAZING real-life experience because a lot of companies will give you code to work on as a "task" in your interview process, and you'll need to be able to pick it up and run with it. This class will prepare you for that. 

Class requirements:

  • You need to know Python and Python classes
  • You need Python installed on your computer
  • It's best (but not required) if you've taken Django 101 prior to this course

How to get the most out of this course:

  1. Code along with me
  2. Check the Project area for details on where to get all the source code, step-by-step. 
  3. Do the project and share your results with the class

Meet Your Teacher

Teacher Profile Image

Kalob Taulien

Web Development Teacher

Teacher

Hi everybody! I'm Kalob Taulien.

 

Here's the TL;DR (short) version about me:

I have been coding since 1999 and teaching people how to code since 2013 I have over 350,000 web development students world-wide I'm on the Wagtail CMS core development team (Wagtail is Python's #1 most popular website making system) I try my best to answer EVERY question my students have  I love teaching — it's definitely one of my natural talents  Also I love goats! (Great conversation starter with me if we ever get to meet in person)

Below you can find all my Skillshare courses. The categories go from easiest to hardest, except for the Misc. Coding Courses at the very end. 

If you're brand new to coding, start with BEGINNERS WEB DEV.&nb... See full profile

Class Ratings

Expectations Met?
  • Exceeded!
    0%
  • Yes
    0%
  • Somewhat
    0%
  • Not really
    0%
Reviews Archive

In October 2018, we updated our review system to improve the way we collect feedback. Below are the reviews written before that update.

Your creative journey starts here.

  • Unlimited access to every class
  • Supportive online creative community
  • Learn offline with Skillshare’s app

Why Join Skillshare?

Take award-winning Skillshare Original Classes

Each class has short lessons, hands-on projects

Your membership supports Skillshare teachers

Learn From Anywhere

Take classes on the go with the Skillshare app. Stream or download to watch on the plane, the subway, or wherever you learn best.

Transcripts

1. Welcome to intermediate Django (Django 201): Hello and welcome to Django 200 one. In this course we are going to be creating a fairly large application called Today, I learned. And in this application you're going to be able to login logout, except new users, new sign-ups. You'll be able to create new posts using ajax. For example, hello world with a little bit of a heart there. And it shows up hello world. You can even view that page as a detailed page and then share that link if you wanted to, there's going to be detailed accounts. User profiles is another term for that. So user profiles you can follow and unfollow people as well. And there's a little bit of homework in-between a several lessons there. So you can do a lot of the work that you can follow me exactly. But at the end, I'm also going to push you to do a little bit on your own as well. Now the nice thing here is that it's also going to accept a new create view. So let's go ahead and open in new tab, new create view, helloworld to, to, to, to, to. And it shows up with hello to 2.2.2. Now I've purposely introduced a few bugs here and there that I want you to solve as well. And this is going to be great hands on experience for when you are a professional, real-world developer and you're writing Django every other day. Hi, I'm Caleb telling and I've been writing Django for a number of years now. I am a professional web developer and I write Django every single day for a living. I've worked with clients such as nasa, the NHS, Mozilla, Samaritans, and many, many other organizations out there, both locally and really large organizations as well. So who is using Django? The likes of instagram, YouTube, Pinterest discuss a lot of big, big companies are using Django and it's backed by Python, which is really, really powerful. Now to get the most out of this course, you're going to need to know Python. You're going to need to know some pretty strong Python. Because we're going to be writing class based views. And class based views basically just means we're going to be writing object-oriented programming. We're going to be writing classes. And so you're going, you're going to need to know how classes work. And if you're a little bit familiar with classes already, but you're not super familiar. That's totally okay. You'll get a lot of practice with classes in this course. We're also going to get started from scratch. And I'm not going to assume that you know anything. So if you've never taken a Django course, discourse is still for you. It might be a little bit fast paced and you might still want to go and check, oh jingle, one-to-one. But this course would still be viable for you. Hello, I'm Caleb Italian and thank you for checking out my Django 200, one class where we make a Today I learned app and I hope to see you inside. 2. Creating a virtual environment: Okay, welcome to the first lesson in Django 201A. First things first, we need to create a virtual environment. So there's a few different ways we can do this. But before anything, let's go ahead and create a new folder. So what I'm gonna do is create a new file here. I'm going to call it test.py save. I'm gonna go to my desktop and call this django 200 one. Now that looks like it did nothing but what I can do now is file open. And I can actually select this entire folder. And it will open it up on the left here. Now we can see test.py. I'm gonna go ahead and delete that. I just wanted to open up the folder on the left. And what I can do now is go to View and I want to open up my terminal. Nope, that was the wrong one, but terminals right there. And there's a few different ways we can do this. So let's go ahead and just collapse that. And I'm gonna make this just a touch bigger. Zoom in, in all the way. And so we can do really anything in terms of virtual environments. We can use a PIP add-in, which I'm going to use in this particular module in Django to one. Or we can use Python dash m, then we can use a regular V and V a. Then we could use Docker if we wanted to or vagrant or virtual env wrapper. What I'm going to do is simply, simply use pip to begin with. So if you don't have Pip, Pip, show Pip env. And you should already have PIP and everything because you're a Python developer and you know a little bit of basic django already. So you should have PIP. Now, I have pip 20, f.11 dot 15 installed. It doesn't really matter what version you have as long as it's a more up-to-date version. If you don't see this, if it looks like something like this, this will say missing, you're actually infect, won't say anything. So if you don't see anything, what you need to do is pip install Pip env. And it already says for me that the requirement was satisfied. So now I can go ahead and I'm gonna clear this type of pip install Django. And this is gonna go ahead and create a new hip environment for me. And it's going to install Django, as we can see at the bottom left there it says installing Django and it's little loading Thing and locking thing. And now to get inside of a virtual environment, we can do Pip env shell. And now you can see that we're inside of our Pip environment. We can do pip show Django, and this is going to show us her Django site. Now this is just creating a virtual environment. We're not going to set up Django just yet. We're going to do that in the next video. Now to get out of your control C or Control D should just simply get you out of there. Otherwise, if you want to, you can always do Python dash m dot VN. And this is a another way. Yeah, look at that. Vs Code is smart enough to realize what was going on there. Then we can do and not Python, but we wanna do source dot v1, that's the folder name. And then activate and that gets us inside of here. And then we can do pip show Django for instance. Now, I'm not going to be using a venn, I'm going to be using Pippen, but you can use whichever one you like. Or again, you can use Docker or any other system that you prefer really. So I'm just gonna deactivate to get outta here. Pip, Pip shall gets me back in and just to prove that I have genuine cell that can do pip, show Django. And that actually didn't work. I think Yahya, we did something funny here. We need to because I've got two virtual environments in here and they're conflicting. I'm actually really glad that this showed up is I mean, I'm going to have to delete one of them. So what I'm going to do is RM dash R, F dot. And then to just get back in and we just do preventive shell. Now if you run into a thing that says something along the lines of it's already activated. And you can't get into it. We can do is you can simply close down your terminal and reopen a new one. And then you can do preventive shell and that'll get you back in there. Sometimes VS code does this weird little thing where it thinks it's still inside of a virtual environment, but it's actually not. So that's just the way around that. So make sure you have a virtual environment installed. In the next lesson, we're going to go ahead and actually start a brand new Django project. 3. Starting a new Django project: Alright, let's go ahead and actually start a Django project. So if I just show you the files that I have on the left here, there's not a whole lot here. I have a Pip file and a Pip file, dot loc. First of all, let's go ahead and get inside of our Pip environment. Doesn't matter what kind of environment you're using, you just need to be inside of it. I'm using Pippin just because it's nice and easy. And inside here we want to do Pip show Django. Now if you don't have Django, you can always do pip install Django and that will just simply install Django for you. I'm not going to run that because I already have it. No, scratch that. I'm gonna show you what it looks like. It does nothing because I already have it. So because we have Jenga, what we can do is Django dash admin, start project. And this is going to be TIL sends for today, I learned TIL. Now you can see it created a folder for us over here called TIL. It's got managed up high and stuff in it. Actually, what I wanna do is I want to throw this into this same folder that I'm already in with my Pip file and Pip file dot loc. And so what I'm gonna do is the same command, Django, dash admin, start project TIL space and then just hit a dot. And that's going to install a project right into my folder here. So it's not going to create a folder called TIL, it's going to create a project called TIL inside of my current folder. So let's manage dot pi is beside my Pip file and compiled dot loc. Just like it because it's one less. What's the word I'm looking for? Directories one thus directory, it's one less folder to deal with. Now at this point in time, we can go ahead and we can run our app. We can do all sorts of things inside of Django. So for instance, we can write Python managed up.py run server 0.0.0 dot 0 colon 8 thousand. And this is going to tell me we have an applied migrations now you should be somewhat familiar with Django already. In which case you should be able to run your migrations. You simply do Python manage dot py migrate, and this is going to run your migrations. This just simply provisions this db.json file for us. And then we can run our server again. We don't see that error anymore. And let's go ahead and open up localhost colon 8 thousand in any browser that you want. And you should see this page. Once you have this page, you are up and running with Django, Simple as that. In the next lesson, let's go ahead and add some Git ignore, ignore stuff so that if you are using Git and you've probably should be using Git and GitHub, GitLab or Bitbucket or some sort of versioning control that we don't accidentally commit files like our db.json highlight three file. 4. Adding a .gitignore file: Okay, so if you're using Git, We can do a git status here and we can see that we have this file db.json late three. Now if you just started using Git, you're going to want to use git init. And that's just going to create a Git repository inside of, well, not really a full repository, but it's going to activate get inside of your project for you. And instead of what you see here and get status of just db.json H3. You're gonna see every file in here, including your db.json H3. And what we want to do is we want to ignore this file and a bunch of other files as well. So what we're going to do is over here we're going to create a new file called a dot to GIT IG and ORE dot git ignore. And if I just slide that down a bit, I mean doesn't a paste a bunch of stuff in here. And you can get this right from the source code is while there's too many lines to go through and is really just covering a lot of different scenarios. Python will often create a directory called pi cache. For instance. We don't want to have as disk, var wheels, all sorts of stuff in here. This is just standard HTML and Python settings and all sorts of stuff, but we can see Django stuffing here. Here's a little more applicable to what we're writing. Db.json x3 and db.json three dash journal. That's going to make sure that these files are not accidentally added to a repository. And so if I screwed this up again, let's save that gitignore and I can do a git status. We're going to see gitignore isn't here, but db.json a3 is not in there. And that's exactly what we want. And so in the future we're going to be ignoring all sorts of stuff. But for now we just really want to ignore the most common things like vents. We absolutely do not for any reason, want to commit our entire virtual environment, a Pip file and IPIP file.js. Ok, that's totally fine. There's not a whole lot of content in here. And that's going to be totally okay. But if you were to use a virtual environment, a v and v, then you would have a whole folder with lots of files in there that you could accidentally commit. And that just means that someone's enough to download a lot more, a lot more content from GitHub later. And that's just a pain in the butt. And they're going to run into the versioning issues and it's just, it's difficult. So instead, we get ignored these things. And one of the things we wanna get ignored, and you can see this is actually grayed out now is my db.json a3. And that's just because when I create a user, it's going to have my password in there. It's going to be encrypted, but it's still going to have my encrypted password in there and that's no good. We don't want to we don't want to put that up anywhere. We want to keep that on your computer, keep it nice and safe. Snow. You can go ahead and do git, add dot git ignore. And then you can do git commit and then your message. And I'm going to commit this. And in the next lesson we're going to move forward with setting up our first app inside of Django. 5. Your first app: Alright, let's go ahead and get our first app started. So if we do this, we do Pip nth, shall we get inside of our virtual environment, we run Python managed up high run server, 0.0.0 that I've missed one dot 0 colon 8 thousand anyways, this is going to allow us to, if we open up my browser here, Firefox, you can see that our sites up and running. And if we go to slash Admin, we should be able to login. We don't have a logging yet, so let's go ahead and create a login. So let's go back to our terminal. And what I'm gonna do is I'm gonna open up a split here. So I'm going to have jangle running on the left and I'm going to write and run some commands on the right. So I can do pip shell to get back inside of here. And I can do Python manage dot py, create super user. And what this is going to do is ask me for a username. I'm gonna say my user name is Caleb kilobyte. Caleb dot IO. Password is going to be whatever my password is going to be. And this is exactly why we don't commit our SQ lite files is because I just typed a password in there. I don't want someone to be able to try to crack that password. Whether it's a good password or not, doesn't matter. I don't want someone knowing that password. So let's go back here. And I created a user called Caleb with a password of Guam and I'm gonna say it. And this gets me into my Django admin. And now we can manage groups and users, go into users. And I can manage just myself. I'm the only user in there. Okay, so we now have a Django Admin created, a super user created. Let's go ahead and create our first app. So to create our first app, what we need to do is maybe I can just, yeah, let's do that over a bit. Python managed dot py, start app, not start project, it's start app. And then we're going to call this app feed. If we do a git status in here we can see we have a new folder called Feed. We've had new folder over here called feed, and it has all sorts of stuff in it. Let's go ahead and open up. Today I learned and our settings and inner settings, we should see installed apps in here. Installed apps and just underneath the Django stuff, we just want to type feed. And that's simply matches the folder name here. And in fact, if we go into apps, we can see apps dot pi inside of our feed. The feed config or the APP_CONFIG. Its name is called feed, and that's why we're calling it feed inside of installed apps. Now with this KMZ files such as your views, test models, Apps, admin init, migrations with an empty init file inside of it. That is a standard Django app. At this point, you should still be pretty familiar with this. I'm gonna go ahead and commit this and then we're going to move on to the next lesson where we create our first model called post. 6. Your first model: Okay, welcome back. So over here I have a lot going on on the screen. I have all of my files on the left. I've got my editing at the top, top right, in the middle bottom here we've got Django running. And in the bottom right is where I'm going to be executing a bunch of commands and at any point in time, I can simply move this over. And if you're just tuning in now and you're like, oh, hey, how did, how did you get that? All I have to do is to get a split screen is click this little split icon over here, over here. So in this lesson, what I wanna do is I wanted to open up models dot py, and I want to create a brand new model. Now this model is going to be pretty bare bones to begin with, and we're going to add to it a little bit later. So let's do class Post models, dot model, and this is going to create a new model, a new database are not a database but a new table inside of our database. And we want to give it a text is equal to models dot, not TextField, char field. And because today I learned in this application is going to be a lot like Twitter, where you can really only post a certain number of characters. So let's say you can only post something like 240 characters. Maxlength is equal to 240. And the idea is you simply post what you learn today and everyone else can follow and see what you learn today and you can tell the rest of the world, but what you learn, all sorts of stuff like that. So let's go ahead and create a new actually, let's skip that for a second. I want to show you how this works. So we have this new post model. Let's go ahead and type. First of all, let's, there we go. That's a little more visible there. Python managed dot pi and I'm inside my virtual environment right now. Make migrations. And what this is going to do is create a new migration file called initial. And what this is going to do is it has no dependencies. And this is simply going to say, hey Django, when you execute this file in Python, creates a new model called post, and it has fields id and text on it. So every post has an ID by default, even though we didn't specify it. And we said text models char field length of 240. Text models char field length of 240. Now, that's cool. That just made the file that didn't actually do anything. Let's go ahead and type Python managed dot py migrate. And what this is going to do is execute that file for us. And you can see it says applying feed 001 underscore initial, everything was okay. Now a little bit later when we look at these posts in our admin, we're going to see that this shows up as a funny name. And when we get to that point, we're going to change what that name actually looks like. But for now, I said we're going to tackle that right now. We're actually to tackle that in the future when we get to it. So we're going to tackle things one by one as problems arise. And that's a lot like how we code in real life. So that's all we need to do for this particular lesson. All we needed to do was create a new model called post. Give it a field called texts where the max length of 240. And then we, Python managed up high make migrations. Python managed up high migrate. And we are good to go. 7. Registering your model wth the Django admin: All right, let's say we want to be able to manage posts through our Django admin. So we go into our Django Admin and we don't see posts in there and we need to now register that. So what we can do is in our feed, in our admin dot py and it has to go in this file. That's where Django is looking for. It is first of all, we're going to say from DOT models import post. And we're saying dot models, dot being this particular folder, models being the module or file. So let's close that up. Admin dot py saying this particular folder in here, and then look for models up high in here, import, post. And then we can register the model in here. And registering is really easy. We're going to use a simple form of registering. So we're going to do class post admin, admin, dot Model, admin, admin, if I can spell that right. And we're not going to add any settings to it. Not yet anyways, if you wanted to, you can definitely go take a look at the Django docs and add your own settings. But for now we just want everything to be accessible and there's only one field on this particular post model anyway. So it's not like we can really do too much. So now we need to say Admin Register site and we need to connect these two. We need to connect post with post admin, and that's going to connect our post. Here is Models.swift model with our posts Admin, which is using admin, dot Model admin. So let's go ahead and save that. And let's refresh in here. And there we see feed and posts, and we don't have any posts in there yet. Let's go ahead and create our first post. Our first post, save. And it says post object one. This is the weird thing I was talking about in the last lesson there. Let's go ahead and actually change that. And that's really easy to do. All we have to do is open up our model.predict. And inside of our post class here remember indenting is important. Deaf TR, self, this is two underscores side-by-side. So underscore, underscore, STR, underscore, underscore self because every, every method on a Python class always takes self as its first parameter by default. Then we can say return self dot text. And let's truncate that to be like the first 100 characters. And just wait for Django to reload. And there we go. It says our first post. And let's make that edited. Save it. And it says edited. There we go. We have now registered our post model with the Django admin. 8. Adding a DateTimeField to your Posts: All right, welcome back. We have a post and we have some text in it. We don't know who wrote this post, we'll add that later. And we don't know what time the post was created at. So let's go ahead and first of all, add the date. Adding the data's going to be a lot more simple. So what we wanna do here is just open up Visual Studio code. And in our models dot py file, where our post is our post class, we wanna say date is equal to models dot datetime field, not a date field. A date field is going to be just the day. A datetime field is going to be the date, the year, month, day, plus the hour, minute seconds. And in here we wanna say auto now is equal to true. So every time we create a new post, simply say that that post was created at this particular time. So we don't have to do anything. Django is going to handle that for us. We don't have to worry about updating it or inserting it or doing any sort of database work. Django just magically handles it for us. Now, that's all nice. And there would be lovely if all we had to do was refresh our page. But if you refresh your page and you see something like no such column, that simply means you need to change some things in your database. And to do that, we make migrations. So Python managed up high, make migrations. And Python managed dot py migrate. And let's go back to our code here. Not our code, but our browser and it works. And our date field doesn't show up. And our date field actually isn't supposed to show up. Our date field isn't supposed to be editable. Auto now makes it non editable by default. And that's OK. We don't want to mess with the dates. We don't want to be able to edit the dates at all. We as soon as we create a post, we want that date to be automatically filled him. And so that's how we add a new field to this post model or to any model really, you add a new field. Something is equal to models dot some sort of field. And then you do Python managed up high make migrations. Python manage the pie migrate. 9. How to list all the posts on your Home Page: Alright, let's go ahead and create a home page. Because if we go back to our page and let's just get out of the admin. We just see Django has default homepage. Now this actually requires a couple of different steps, but to begin with, let's create our view and sort of the idea with Django is a lot of things need to be done at the same time. And the problem with watching a video like this is that we have to do things one at a time, so it doesn't always make sense. But the next few steps, we're going to basically just do all of this in any particular order and it's the end that's important. As long as we have things like URLs, views and a template, it should all work for us. But we can't really get it all to work without all three components. So what we're going to do here is we're going to open up our feed and go into our views. And I'm just gonna get rid of this render because we're not going to use a function based view, we're going to use a class based view. And so we're gonna say from Django dot, dot generic import ListView. And we also wanted to, from DOT models import our post. And because we're working with the post, we usually want to import it. Now let's go ahead and create a homepage class home page. And this is going to be our ListView. And what this is going to do is simply render out a template with all of her posts in it. So let's say the HTTP method names is going to be Get. That means people can't post, they can't delete the can't do anything like that. All they can do is get a page request and see the posts. That's all the homepage does. Template name is going to be, let's just call it homepage dot HTML. The model we want to list is post. Our context object name is usually I believe it's object by default, but I always overwrite this to be posts. And this is what we're going to use in our template, which we're probably gonna tackle in the next video To be honest. And then the query site is simply going to be Post.all objects dot all. And we want to order these posts order by the Id Descending. And let's make sure that we only get like 30 posts at a time. So a page isn't load a million posts because that could a take forever to load B really hurt your database. So this is all we're going to do in our views up high. There's nothing else in here, as you can see. Next, we need to create a new file in here in our field. Let's call this URLs dot py, URLs dot py. And in here we're going to create a new series of URL. So it's do from Django, dot URLs, import path, and from dot import views. It's also given us an app name and I'll describe this in just a second. Because it seems like I'm going pretty fast here, but this is pretty much Django 101 stuff so far anyways, you should be quite familiar with this. Url patterns is equal to, and then it's got a path, a place that it goes. Views dot. What was that view, whether we call it and just homepage dot as view. Name is going to be note and that's not right. Name is equal to index. So in this file, all we're doing is we're importing path so that we can use it here. We're importing all of our views. We really only have one view. We could import just the one view if we wanted to, but we're going to import to the entire views module so that we can use view as dot Homepage and then render that template as view, which is then going to connect over to our template name, just homepage dot HTML. And our app name, which is what we're going to be using for name spacing is feed, and that just matches the app name that we're using in her folder name. It also matches this one in here. And that just sort of keeps name spacing or URLs and nice and easy, we'll talk about namespaces and why it's more important. And when we get more or less to the template side of things, last but not least, we have to open up TIL slash URLs dot py. And in here we can see all we have is our admin. So if we wanted to, we could completely disable the admin by simply deleting it. And the admin would no longer be accessible to any users whatsoever. But that's not what we want. What we want, what we want is we want a path that goes to our homepage. And we want to do this thing called include. And we want to include like our feed URLs or something like that. We haven't imported that yet and we want to give us a namespace of feed. And again, we'll talk about namespaces and a little bit. So first of all, we need to include and well, that's not wrong. We need to import include and we need to import feed URLs. So let's do from Django.com dot URLs, import include. And if you're ever wondering, hey, Caleb were on earth, are you getting these? How do you memorize all of these? First of all, a. I don't have a lot of these memorized. I just write these enough that I know some of them and be the ones that I don't know. I honestly just look at the Django docks. The Django docs will tell me everything I need to know. So I miss and move that up a line. And let's do from the feet app import URLs, the whole file as feed URLs. And that just matches here, so I just renamed that import. Let's go ahead and save this and see what happens. No module named Django, conf dot URL, I think it's supposed to be. Where are we? Urls? See, I get things wrong to no module named Django dot URL, what file or you feed URLs. We're just going to tackle this Fi dot URLs. Django, URL's. Django is just gonna keep telling me that there's a problem in here. This is probably one that you caught. Query set minus no, that's not right. Query set is equal to and drink was just gonna keep telling me that there's problems until there's no longer a problem. So let's go ahead and go back to our page and refresh. And we should, if everything is working right, see this template does not exist. It's looking for homepage, it's looking for feed slash post list and is looking for them in Django contra off templates homepage, Django contract admin templates homepage, Django Admin templates feed, Django contract templates feed is looking for these files and these folders. It doesn't know where to actually look. So in the next lesson, let's go ahead and tell Django by changing some of our settings, that it should look in a particular folder for us. 10. Setting up your template locations: Okay, in the last lesson, Django was unable to find our homepage dot HTML, probably because a, we haven't created that file, so it can't find it anywhere, which is fair. And B, it's looking in weird places. And so Django lets us sort of set this up on our own so that we can set it up on our own so we can do whatever we want with it. We have a lot more flexibility this way. So what I wanna do is I want to open up this file called Settings dot py. And where is my base directory, base, base, base directory. So we need to do a few different things in here. First of all, we want a project directory, so let's do project dire. And this is going to be a weird thing that you probably don't need to remember because you're not actually going to write this out too often. You can just get this right for my source code if you want. So we're going to do OS dot path dot dy or name OS dot path dot dire name OS dot path dot absolute path, underscore, underscore, and then this particular file. Let's just make sure I don't have any typos in there. It should show up twice. And it doesn't because that's dire name like Dire Wolf. And there we go. Now we're using OS. We need to go ahead and import that. So let's do import OS at the top, that's just underneath all my doc strings here that comes default with Django. So that's going to work for me. If I hit save, there should be no complaints in the Django console down there. And then we need to find our templates, templates, templates, templates, and look at that templates. And then we have this option here called diaries, these directories. And what we want to do here is say OS dot path dot join that project directory, which is going to be exactly where this whole project lives. Til that's this particular folder over here, slash templates. And that's going to be looking for a sub folder in here called Templates. Now let's go ahead and open up our browser and let's see where Django was looking. Now. Jenga was looking for desktop Django 2-1 TIL templates, homepage dot HTML. Let's go ahead and like make this exist real quick. We're not gonna do a lot of work with it. Let's just make it exist. So let's create a new folder called templates and the new file called homepage dot HTML, stuff in here. But literally all I am going to write. Great success that is working for us. And so in this lesson, we successfully got our template system up and running. In the next lesson, let's go ahead and actually work with the template a little bit more. 11. Your first template: Oh right. In the last lesson we made our first template homepage dot HTML. We put some stuff in there, which is great, and we put it in the TIL Templates folder. What we could actually do is inner views. We could change where we want to find this. We could go feed slash homepage dot HTML. And what this is going to allow us to do is move this out of our standard generic templates folder, which was needed and will be needed in the future. But we're going to move this again. So let's do new file in our feed app this time, let's call it templates. This is going to be a directory name. We're going to give it the feed namespace, the page dot HTML. More stuff in here too, to-to, to-to, to-to. And let's see where Django was looking for this template first. More stuff in here too to 2.2.2, despite there actually being two homepages. So what we can do is we can go ahead and get rid of this homepage. And this is where our homepage is going to live. And that's all we're going to do for this lesson. We'd simply wanted to move our template over to the feed app and then just sort of keeps it nice and clean so that the entire feed is somewhat self-contained. 12. Your base template: Alright, at this point we have a homepage and we need to add a bunch of stuff to it. And so in VS Code, we can do HTML5 colon note HTML colon five, nailed it. And it's going to fill out a bunch of stuff for us. Our page here, homepage. And when we refresh the page, it says our page up here And homepage. And in our source, fewer page source. We can see all this HTML. Zoom in there. That's exactly what we wrote. Now, that's fine. Until we need a second template which we're going to need down the road. And so at this point, what we want to do is we want to use this, all this code as a quote unquote template. And really just only changed the small pieces that we need, like our title or just the content in our homepage. So what we're going to do is in our t UP TIL Templates folder, create a new file called base dot HTML. And let's do HTML colon five, base file, and let's call it base file in here. Now, again, this is not going to do anything even though we have the file. Django just doesn't know what to do with it. It's just sort of hanging out. Not being used is not being called by any other templates or any views or anything like that. Instead, what we can do is in our homepage, we could say extends base dot HTML. And if we refresh this says base file in here. This is extending our homepage. So let's just delete this first second refresher page. We see nothing shows up as Viewer page source. There's nothing in there. Undo that, save, refresh base file in here it says base file and the title as well. That's exactly what we changed this viewer page source, and we're now using two template files at the same time. And so what this is doing is saying, hey, Django, take that base dot HTML file that you're currently using and use it. And so I want you to take all of that stuff in there and I want you to extend from it. This allows us to eventually inject small pieces of code into this page. So on every page, we don't need to write HTML. Lang is equal to English. We don't need to rate. Meta. Charset is equal to UTF-8. We don't need to write meta named viewport content width is equal to devise dash width initial dash scale is equal to 1 because that's painful to write on every single file. Instead, we can write at once. We can write it once, and we can extend from that file. And in the future, what we're going to do is I'm gonna show you how we can just change a small bits of it so that we don't have to write the same junk over and over again. 13. Template blocks: Alright, let's look at adding blocks. Let's, let's just close all this down as if we're just opening this project from the beginning. Let's open up TIL templates based on HTML. And that's also opened up Feed templates feed homepage. And so we're homepages extending from this base dot HTML. And what I can do is split this as well to event base on the right and we've got homepage on the left. And let's see if I can make this just a tad smaller. Hopefully this is still viewable for you. Hopefully you're not on a very small phone. But yeah, moving forward, let's say we wanted to change this title. What we could do is we could add this thing called a block. And so it looks like this block, title and block. And that's all we're going to do. Now we're going to actually literally copy that. So let's right click, copy it over here, paste. And we can change the title of this page, changed the title, title of the homepage. And we can see, oh, unclosed tags, what are we looking for here? And that is because I in fact have a typo right here and right there, I need to have a percent sign. And so this is what a block looks like. In fact, that's a good, good time. Good reason, I guess for me to explain the syntax error. So we use a curly brace or, or a moustache brace, percent sign the keyword block and then the name of it. And that matches right in here. Then we close that with the exact opposite. So a percent sign and then the closing curly brace, put our content on the inside, all of this stuff. And then we say with the same opening, closing syntax end block. And so this opening tag matches this opening tag and the closing tag matches this closing tag. And if you ever wanted to default, you can say default title in here. Now let's go ahead and add some body. So in our page we want to do something with this body. So we can say this is extending from base dot HTML, but let's also create a blocking here, a block, let's call it body and end Block. Make sure I got that percent sign. And so I don't make that same mistake twice. Copy, paste it over here. And this is going to be from the homepage. That didn't work for me because I didn't save base dot HTML. Let's try that again. And hopefully this boom is correct. Boom, there we go. It actually worked. This is extending from base dot HTML. So this is going to be put on every single page. And this is going to be from the homepage. And that's what I wrote here. And so if I ever wanted to change it, I can do that here. Now in the future, if you have other views rendering other templates, instead of writing all of this stuff, all we have to do is extend from base and just inject the block title and the block body or any other blocks. Thing is though they have to match. You have to have a block body. You have to have a block body. You have to have an n block, you have to have an n block. And Django will tell you if there's any sort of syntax error to can't figure things out. It'll, it'll tell you right up front and and be very honest and blunt with you. So it will at least let you know what's going on. 14. Template loops: Alright, let's go ahead and loop through a bunch of our posts here. So if we open up our browser, it just says this is going to be from the home page and it's not actually looping through any of our posts. We need to add these posts in here. Now, we did some of the setup a little bit earlier where we had our homepage is a ListView. It's going to list all of our posts and the query set, the things that we want to post or loop on our page is going to be all the posts dot objects dot all ordered by the most recent and only show 0 to 30. So the first 30. And then we said this context object name is going to be posts. And that's important. Because if we go to our homepage here, what we can do is in our body. And let's just get rid of this. We can say for post in posts, that's where we're getting that, that context object name, context object name is equal to posts. And that shows up right there. And this is just a regular loop, just like in Python or JavaScript, irregular for loop. And then we say end for. And in here we're going to have access to every post. Now, that post is going to loop through everything in this query set. All of those posts. And each one of those is going to be one of these up post model. So it's got text and has a date in it as well. So let's go ahead and just show Post.all. And it was written at post dot date. And look at that are first post edited is what it was called written at on rather should've said on January sixth, 2021, as clearly not the right time as my computer time is different from my server time. That's okay. We can set the server time later. That's not a big deal. I let's go ahead and open up our admin and let's create a couple more posts. And so let's just go to slash Admin. And this is the second post, save and add another. This is the newest post, save and yeah, that's it. So we have three posts in there now. And we can see that it shows up in here. This is the newest post. This is the second post and our first post. Let's go ahead and actually separate that with something like a horizontal rule. There we go. So now it actually shows our posts. That's literally all we have to do at this point because we did all the heavy lifting in our views. We set up our model so that data can be stored in our database. In our views we said, hey, this is going to be a ListView. Use the template called feed slash homepage that's going to be in here. So it's sort of self-contained. The model that we're going to get list through is the post model. The context object name is going to be used right there. And the query set, what we want to be able to loop through is this. And that's going to get all of our posts dot objects, dot all of them. And again, it's going to order them reverse ID. If we wanted to order them from oldest to newest, we would say order BY ID. Instead we're going to say order BY ID, oldest to newest or none. No, that's the wrong way of saying it. Newest to oldest. That's better. And then just chop that, get the very first one all the way to the 30th one. And in our template we just simply said for every post in those posts, show us that text that comes from our model's text. And it was written on this date. And that's all there is to it. 15. Installing Django Allauth: Alright, let's go ahead and add, well, some form of login system to our site. And this is going to take up multiple steps. And if I just open up a Firefox again, there is no login page is no logo page and no password reset page. And there is a really, really easy way to do this, and it's a third party package called Django all off. And so what we're going to do is install that. And now, so inside of our Pip environment or PIP N for we can do pip install, Django dash, all off. Or if we wanted to, Let's get out of this. We could do pip install Django dash, all off. And this is gonna go ahead and install Django all out for us. So we just give that a quick second and that's going to add it to a PIP lock file for us. And let's go ahead and get back inside of our Pip environment and we can do pip, show, Django all off. And I need to type Pip in front of that. And there we go. We see Django all off, 0 dot 0. Now there's a few things we need to do to actually make this work. All we did was say, hey, in this virtual environment, Django oth exists. What we need to do as well is we need to go into our Settings dot pi, and where is our installed apps in R installed apps, we need to add a couple of things here. So for Django off to work, first of all, we need Django contrib sites enabled. So Django contrib sites, and that just allows Django to be multi-site. Then we want to install all off and doesn't really matter. I usually put all of my my third-party packages and my local packages or modules such as feed below the Django stuff. So we want to put all often there. We also want to add all auth dot account. And just for good measure, because all off also supports logging in with Facebook, GitHub, Twitter, et cetera, et cetera. Let's do all auth dot social account. And then somewhere near the bottom, let's go ahead and add a bunch of all our settings. So first things first, there's a lot in here. We need a site ID, and we only have one site, so we're going to use site ID one by default. Then there's going to be a login URL. And we just want to set that to be slash login because that's pretty standard. Then there's going to be a login, redirect URL, and that's once you login, where's it? Suppose I take u, we really only have one view right now, that's our homepage. So let's go back to our homepage. Then there's account authentication. If I can spell any of that right, authentication method is equal to. And how do we want people to authenticate their accounts? We wanna say, send them an email account, confirm. Email on get is equal to true. And what this does is when you click that link in your email. It automatically confirm or do they need to press a button on your on your websites to confirm? We're gonna say they don't need to press a button just on a GET request is good enough. I count email required is equal to true. Because we want everyone to have an email account, because we're creating a service where you want to be able to email people or at least reach out to people. Let's do account email verification is equal two, optional. Do they have to verify their email? And so this can be optional or this can be mandatory. I'm gonna make this optional for now. Account login on. Email. Confirmation is equal to true. We're just gonna power through a few of these account log out on.get. Does that mean someone has to actually fill out a form like in their HTML? Is it going to be wrapped in a form to sign out? Technically, this should be false. That would be the proper way to logo, but we're going to do the, the cheating way. And we're gonna say, yeah, they can just simply click a link and it works. Account login. And password reset is equal to true. And there's a few more. But before we get through all of these, let's go ahead and type in Django all orthodox into the all-knowing Google machine. And there's all sorts of settings in here so we can go over to our configuration. And this is pretty much what I'm configuring right now is just a bunch of these. Not all of them, but a bunch of them. And so at any point in time, if you ever like oh, I don't remember what Caleb row devote authenticated login redirects. You could actually just go straight to the docs and it'll tell you exactly what it's supposed to do and what the options are. Is it true or false? Is it optional? Is it mandatory, et cetera, et cetera? Account logout, redirect is equal to. Let's go back to the homepage. So when you log out, where's it going to redirect the user to? I like this one. Account preserve. Username. Casing is equal to false. And so the username, such as cane, should be the same as Cain, all uppercase. And that's what we're saying here, that those are not two different usernames. Account session, remember is equal to true. And what this does is if you set this to false, the login form is automatically going to have a button and then it says, remember me for two weeks or whatever. We don't really want that. We're using to say, remember forever, account sign-up, password. Enter twice as equal to true. We want people to enter their password twice just so that they don't accidentally locked themselves out of their account because that's inconvenient. And frankly, the user's going to blame your website for that even if they spelled it wrong. Account username min length is going to be, let's say to the shortest username has to be two characters. And lastly, and maybe most importantly, authentication backends is going to be, and we're going to write a tuple or a tuple in here. And we're going to add two. And your Django dot contrib dot auth dot backends, dot modal backend. And that was all uppercase. That should have been all lowercase. There we go. And the second one is all auth dot account dot. Auth backends. Then cation backend. Let's go ahead and save that. And it says that we have 700 applied migrations. So let's go ahead and let's do Python managed up high, migrate, cool, that applied all of them. Now last but not least, even though all of this is set up properly, let's go over to our URLs in our TIL app. So our TIL where Settings live. Instead of going to settings, we're gonna go to URLs. And we're going to add a new one in here. And we want to simply add URL. Blank. Include all auth dot URLs. And this, this isn't imported yet, so let's go ahead and import that. And that comes from Django Con fewer ells, where Include comes from URL. And let's save that and see and there is some sort of issue there. Yep. That's why we usually do trailing commas. Cuckoo, cuckoo, cool. Okay, so this is all up in operational. When we go back to our page here, we're going to see nothing happens. But what we can do now is we can go to slash login. And that's actually not going to do anything either because I'm already logged in as the admin. Let's do new private window. And let's go to slash login. And now we have a login page and we can simply enter the email address, enter password. So I'm going to enter in my email address. Caleb said Caleb dot IO and my password. And I'm gonna click sign in. And now I'm logged in. And if I wanted to, I could go to slash log out and it logs me out. Now at this point time we actually have no idea if we're logged in or logged out. That's totally fine. All we needed to do was set up user login and logout. And if we wanted to, we can simply go to slash sign up. And now people can sign up with your email address. They can put a username and enter their password in twice and automatically sign up. So close that for now. And that's really all there is to this particular lesson. 16. Global login and logout links: Let's go ahead and add a global log out and log in link. So it's going to be global. So we want that to be in our TIL templates base dot HTML. This is our global file. And so what we can say here is we can check to see if someone is authenticated or not authenticated, as in, are they logged in or not? And we can simply do that with if request dot user_data is authenticated. And we can say else. And let's do this. You are logged in. I don't really mind that there's a typo in there. You are logged out and let's just see what this does. So it says I'm logged in. If I open this up in new private window, it says I'm logged out. Says that right there. If I go to slash login and log in with my username and password. And now it says I'm logged in. So now we have authentication, we're logged in, we're logged out, Cuckoo, cuckoo, cool. Now we need to add these links. So all we need to do is say, instead of saying you are logged in because that's not helpful, someone's gonna know if they're logged in or not. We can add a link. A HREF is equal two. It goes to some place, log out because the user is already logged in. They want to logout link. If they're logged out, they want a Login link. So a HREF is equal to some link. We don't know yet. Login. And let's make this ugly because will make us prettier in a little bit. Let's just add that HR in there, that horizontal rule. Okay, so now I'm logged in because it's showing me the logout link. Let's go ahead and add that link, because currently that's going nowhere. We do this. We say URL, account lockout, and this is just coming from Django all off. And we can do the same thing down here with account login, URL, account login. And now it looks like it did nothing but if I click logout, it logs me identify click login. It's gonna ask me to login and I log back in. And I can log back out. So now we have a login logout system and our template actually allows the user to login and log out whenever they want. 17. Extending Allauth templates: Alright, let's take a look at extending all auth templates. As in when we're here, we go to, let's go back into login page. Look, we have messages and we can dial those if we wanted to. Anyways, let's, let's say we don't want this menu to show up and we just want the sign and show up. How, how do we go about doing that? Well, first of all, let's do this django all off GitHub. And we can go into off templates. I count. And we have all these different links in here are not links but files. So like let's take the login one. And that comes from, remember this is important, pathing is important. So it comes from account slash login. So let's say we wanna overwrite that. Let's go to TIL templates. Let's create a new folder called account and a new file called login dot HTML. And let's just say this is overwritten. Let's go back to our sign-in page and it says that it's overwritten. And so what we can do now is we can copy all of this code. Let's go down here and copy all of that code. Throw that back in here. Make sure that we are actually extending properly. And basically we just put it back. And if we wanted to, we could do this with every single file. That jangle off gives us. And sometimes there's a case for that and sometimes there isn't. Now, that can be a little bit painful because there's a lot of templates in here, like password change, password reset, password reset done password reset from key, password reset from key done password set, you know, on and on and on. And we can do that over and over and over again. It's going to take you a couple hours and it's gonna be kinda painful and there's a better way to live your life. And so do we have a base dot HTML in here in our templates templates account. I don't think we do know we do. It's right there. Perfect. There is a better way to live life. Okay, look at this. So in our Django all auth account folder, there is a file called base dot HTML. And this is getting into some fancy templating here. But what we can do is basically overrate all of them all at once. Instead of doing one at a time when we could overwrite a bunch of them. It's still going to have the contents, but it's going to be able to extend from our base dot HTML, which is going to be very, very important. And you can see that if Doo-doo-doo-doo account based on HTML, if let's open up our base dot HTML and see this. This is our base dot HTML file. And let's change it from, instead of extending from a base account slash base dot HTML, let's just extend from our base dot HTML. And let's go see what this looks like. This is our base dot HTML login that worked, that worked for us. Now, the problem here is that it's using block content. And I said, we're going to call this block body. Now we could change this to content and then change it over in our Homepage if we wanted to. Or we could have 21 or we could nest them if we wanted to. So let's go ahead and simply nest this. Because that's probably going to be the easiest way. Block, content and block. And look at that. We have all the sign-in stuff. Now what's nice about this in particular is that if we go back into our login, which is extending from base dot HTML, we don't have the block head title. We don't have, what else do we have in here? Get providers. So as block content, can we close that? We can't close that one. Indenting is weird on this. Yeah. Okay. So I guess we're going to stick with that example. There is no head title. And in fact, you can see up here the default title exists. It's falling back because there is no head title set. In our base dot HTML. We only have title, we call the title, they called it head title. So what we can do instead of doing all of this, is we could go back and just overrate their base file. So let's go to account and let's take their base file. And let's just forward it to our base file. And so we could take all of this if we wanted to. Let's take the raw version of this. And let's create a new file called base dot HTML. And okay, so this is a lot like our base dot H2O and this is actually no good. What we wanna do is we wanna get rid of the extra stuff so we don't need extra head, extra title. We don't need any of that stuff. Let's go down here, let's get rid of extra body. We don't need that. They do actually have a block and you're called body, you look at that. And so what we can do is block body. If there are messages, you'll messages menu. Do we wanna show that menu? No, let's not show that menu. Let's get rid of that. And let's maybe make this just a tad nicer looking because that indenting is strangely bad for such, such a popular library or such a popular package that indenting is just amazingly terrible. Okay, let's get rid of that stuff. And so we're just overriding their base dot HTML file. And what we want to say now is extend or extends our base dot HTML file. Not the account slash base dot HTML, but our base. It's going to look for our base first, and then it's going to look for the account slash base dot HTML file. Let's go ahead and save this and let's see what happens. Login is actually a bad example because we've overwritten that. Let's go to forgot password and lock. It keeps our base dot HTML stuff in here. Let's say we wanna sign up. It keeps our base dot HTML stuff in there as well. So let's do to-to, to-do. Let's get rid of that because we don't require that anymore. And let's maybe add a link back to the homepage on every page. And so let's just add a quick little link. A HREF is equal to slash, that's just our homepage. I'm going to call this home. And we don't need to use a URL here just because the homepage is already, the homepage is going to be the same whether we use a slash or whether we use a Django URL. So we have home, we have login and let's go ahead and ditch this login one. Let's go login. Delete, move to trash. And it looks pretty much the exact same. That's perfect. That's what we want. We want this to be working this way. And so at this point in time, we have now extended all auth templates. And again, there's really two ways we can do this. If I just go back here, we can either do it one by one. And in some cases you might have to, you might have to overwrite an entire one. Just cause just because reasons, your reason might be different from my reasons. So I can't tell you an exact reason, but it is possible to do that. Or you can say just overwrite all of them by, by overriding their base file. So I just took their base file if there are messages, show some messages in here and extend from our base. So what this chain of command now looks like is login is going to go to account slash base, that's Django off account slash base dot HTML. And instead of showing all of this stuff, we said, just show what we want to show and extend from our base dot HTML, which is then going to inject everything in here. And just like that, we have the ability now to theme our page. Any way we please. 18. Adding Tailwind CSS: Alright, let's go ahead and add some tailwind so that our side actually looks a little bit nicer. And we're going to actually steal, some component will not steal, but we're going to use some components that some other people have gratefully open-sourced. So first things first we need to go to our base dot HTML. And in here we need to add some sort of CSS. And in order to do that, let's open up a new tab and go to tailwind CSS. Man, let's go and get started and installation. Is there a CDN? And here we don't want to do the whole front end stuff, we just want the CDN. And this is no way to live your life in production by the way, because tailwind is a very, very big css file. But while you're doing some local work, this is totally okay. Now let's go ahead and see what our page looks like now. Okay, that did a lot of resetting. A lot, a lot of resetting. So that's actually not a bad thing. We can now add anything we want to it. So let's go ahead and add some sort of navigation. Now I know for a fact that I want this particular navigation and all I did was define this to type into Google tailwind components. And I found a navigation that I really, really like. And so this is our navigation in here. And what I wanna do here is show the code. This is all the code. We're gonna copy all of that. And let's do this. Let's push everything to the bottom for now and put that up top. And what else do we have in here? Do we have anything else? So we have CSS that comes with it. Let's move that up into our head where it's supposed to be. And let's just say the component is from this particular link so that they are getting credit for it. And let's see what this looks like. Okay, not bad, not bad. We don't want all of these. And we want some sort of content area in here as well. So let's start looking through some of this logo. Lets change that logo from logo to TI. L stands for today I learned dashboard. This link can go straight to the home. What else do we need? We need login logout links. So let's get rid of all these ones between the home and the logo. Logo, logo, logo. And all I did was just delete a bunch of stuff there. So now we have logout login or not login, but home analog out. Let's add login because we don't know if we're currently logged in or not. That's how people tell they say the C a logout link and if they see a logo length to go, I'm login. And if they see a login length, they go up, logged out. Okay, so we can do that pretty easily without logout link. What we can do is if request, that user is authenticated. And if so, if the user is authenticated, show a logo link. And where does that logo lingo? We wrote this in a couple of less. We wrote this couple lessons ago. Log out. Let's just go ahead and steal that. Put it right in there. Then we can say if the user is not logged in, otherwise they're logged out. We could take this whole thing, copy and paste. And instead of account log out, we want login, and we want this to say login. Login. And this goes down to our login page. It looks like we did nothing just because we don't have a content area in here yet. Let's also change this icon. Because when we login and logout currently the icons or the exact same. So do we have an icon in here somewhere? Yes. I class Bx, Bx log out. Let's do log in. There we go. If they just switched it and see that again, undo. And I'll refresh just over here on the left side. There we go. That worked pretty good. You can always just Google as well, box icons. Let's do this. Box icons, CSS, Box icons. And so we could use any icon that we want in here. That's pretty cool because nice icons too, I like these, might use these in a future project of mine. Okay, so that's all good. We now need a content area. And so at this point, we might want to zip up some stuff here. So we've got flex coal with 56, yada, yada, yada. Let's try this. Div. My stuff here. Got third is, let's add some padding. S, p, dash 16. Ary are that looks a little better and then we can throw all of our content in there. So let's scroll back on down. Let's get rid of the stuff we're no longer using. We're using the login logout links. We don't need that anymore. We don't need that HR anymore. We do need the body and content in there. And we can just move that up. There we go. So this doesn't look really great. Honestly. I'm not going to show you exactly how to style it, just because that is a complete front end course on its own. But what you can do is you can always just inspect and say, hey, I need to style these eight ones differently. And you can use tailwind H 1s, or you can overwrite the template and add classes to it if you wanted to. So let's go ahead and login. And I'm now logged in and I can now go and log out if I wanted to do. And so we'd simply added tailwind with a nice little navigation on the left. Don't forget at any point in time, you can always check out the source code I had been making commits along the way. So you can see just the code that I changed in each individual lesson. 19. Tailwind style posts: Alright, let's go ahead and make our posts look nice using tailwind CSS. Now I already found a particular component that I really want to use. And so what I'm gonna do is just on my other monitor here, I'm going to copy this link and go on down and paste. And this is what I want a post to look like. Now, here's the thing is we're not going to have all the details right away. We're gonna have to go and add those in in just a little bit. We don't know who wrote what, we don't know if there's a detail link. We don't have categories, we don't have titles either. It's literally just text. So how do we, how do we add this? Let's go ahead and just copy all of that code. And you can see the code in here. I simply copied all of it. And what I can do is go into my feed and where is every post being posted or where is it being displayed? It's being displayed on our homepage. So let's go ahead and make some room for us to work here and just paste that in there. And let's say that this component is from that link. Now, this is going to change these three ugly posts to look all the same, but we can modify those. And this actually doesn't look too bad, does it? I, I like this quite a bit. So what we can do first of all is that date, let's change that date. We know that every post has a date item on it or a date attribute, Date property. Okay, that works little bit better. Let's say we didn't want it to say the full date though. We can do pipe date. And let's do f, j, y, g, a. And I'll show you where I got that in just a second. So where I got that weird f, j, y j is from PHP.net date. And let's go to the date manual. And I might be on the wrong page, but I found an example that works nonetheless. So here's the one that I used. And that's actually one that I tend to use on a lot of my projects minds very similar. You can use m, d, y, and it shows you like month, day, year, you can, it will show you examples of what all of these can-do. So it's just the PHP date manual. As to date formats. It's let's see what this does. Yeah, no, not quite. The first example was better. Let's just pretend I didn't show you that one. Alright, we don't have a title. So let's go ahead and get rid of that title. Where are you and we also don't have categories. I'm going to comment out that category. So it doesn't say design in there anymore. We don't have a title in there as well, so let's go ahead and cut out that title. And our post is supposed to look something like this post dot text. And that actually made her posts a lot smaller than anticipated. So what we can do here is let's see. And the whole thing, let's just give this a width. We can do this in tailwind as well, but this particular module, Django to1, is not a tailwind course, has, so we're focusing more on the Django side of things than we are the tailwind side of things. But we still need things to look at, and we still need things that look nice. Let's go ahead and save that, and that looks a little bit better. Let's add some to-dos in here. The Read More. We don't know where that's supposed to go yet. So before we finish up this module, we're going to look through basically all of our source code for a to do, to do add a detail URL. And what else do we need in here? We need this user to be different. And so let's add another to-do in here. To do, user needs a link to their bio and a photo and thumbnail and name. So it's quite a bit. So instead of like saying, hey, yeah, we're going to do this all in the next lesson, cuz that next lesson could probably be 304050 minutes long. I don't actually know. Instead, we're going to create a little to-do, which is just saying, Hey, future us. Whenever we see this, just know that we need to take a look for this to do or we need to find it to do. And we need to read what it's telling us to do. So it's a way of keeping track of your code. Now if your to-do doesn't highlight like mine, that's ok. I am using an extension called todo tree, called yeah, it's called todo tree is by grunt, ugly. So for now, honestly, this is working. This isn't working as well as we want it to work. All we really wanted to do was add a nice little component in here. 20. Adding a Profile app: Okay, let's go ahead and add profiles, because right now we don't have user profiles. And that's one of the things that we needed to add in the last lesson. We just don't have them. Let's go ahead and create them. And so all we have to do here is do you Python managed up high start app, and let's call this profiles. And we see new folder in here. Let's go into our Settings and activate that. So we go into our installed apps. I'm going to call it profiles because that's what it's called Profiles. And then in our model.predict, we can create a new profile model. So let's do class profile, and this is going to be a user profile. It takes models, dot Model as it's inherited. Inheritance. User is going to be some sort of link back to the regular user object. Now if that was a little bit confusing, let's close up a few of these things. First, we have users. And with these users, maybe we want to use the first name and last name, but you don't see a place to upload an image in here. And so we need to extend that. And so one way to do this in Django, and there's usually a few ways to do things in any framework. But one way is to simply create another model, such as the one called profile and link the original user to this profile. And so that's what we're going to end up doing. And so we say the user is equal two models, one to one field. And this is going to take a Django user which will import in just a second. On delete is equal to modulus dot cascades. So when we delete that user, also delete this profile. And this gives us a related name of profile. In fact, actually that's not given us a related name just yet. So what I'm gonna do is comment this out. And we need to, from, we need to import that user. So let's do from Django dot contribute, dot, auth, dot models. I can't ever remember this one. Import user. Let's see if Django complains about that. That no complaints, good, good, good. Let's go ahead and make migrations. Created a profile for us migrate. And let's register this in our admin. So let's take a look at what we've already done. We've done this with feed sysadmin. So let's just copy all that, close that down and go into profiles admin. And this is read through this one line at a time. We need the admin module from models. What do we have here models? There's no post modelling here. It's not called post, it's called profile. And so instead of post admin, we're going to call it Profile and profile admin. And let's refresh in here and we're going to see wait for it. Profiles. Now there is no profile yet. So what we can do is we can manually attach a profile yards. So let's create a new profile here. And that user is going to be Caleb, and that user is going to link to this particular user. And so there's always going to be a one-to-one relationship here. So every time there's a user, there should be a profile. Every time there's a profile there should be a user. I would look at that profile object and doesn't have a proper name. Let's go ahead and add that proper name. Glad that showed up. Deaf, underscore, underscore, string, underscore, underscore. Self, return, self dot user. Don't username. And just wait for that to load. And there we go. So there's actually no point to having this at this point in time. But we are going to extend the user by adding a profile, which can then have a thumbnail or some sort of image. Let's go ahead and re-enable that related name profile. And let's see if this makes another migration doesn't ever hurt to check to see if you need to make a new migration. So let's go ahead and migrate that. And we are good to go. Now one last thing we want to do here is we want to add this thing called a signal. And so the idea here is, every time we create a new user, also create a new profile automatically. And what a signal does, it's a lot like a web hook or a hook in React. Or if you come from the line of JavaScript, it's a lot like a call back. So when a user is created, we can take a particular action. And so that is called a signal in Django. So let's do def, create user profile. We're going to ask for the sender or not ask but received a yes, the sender, the instance created and keyword args. And if you ever wondering more about signals, because this course is not going to go too deep into signals. The Django doctor, a great place to start. And so let's add a docstring and here, Create a new profile object. Object. When a Django user is created. Simple is that let's go ahead and say if created is true. Profile dot objects, dot create user, that's up here. So Profile dot user is equal to the new user instance. And in order for this to work, we need to register this as a receiver to receive that signal. Post save, and that sender is going to be user. And so what we're saying here is there's a signal out there somewhere. And think of it like a signal out in space. And we have a giant satellite dish and we're trying to receive signals. Then we're looking for a particular type of signal. We are looking for a post save. So once the user is saved or once an object is saved, rather, we're saying check to see what kind of data is coming in. So if that data is a user and it was just saved, execute this. And so we need to now Doo-doo-doo-doo, import receiver and post save. We don't need to import user because we already have user. So we need to import receiver and save or post save rather. So from Django dot db, dot models, dot signals, import, post, save, nope, post save. There we go. And from Django, I'm really stretching my memory here. From Django dot dispatch, import receiver. Django didn't complain. Cool. So now we can go ahead and create a new user. And ideally it would create a new profile as well. So let's go back into our admin. Let's create a new user. Test. Put a password in there. And let's just say if let's go into profiles. There it is. It automatically created it for us. So it automatically creates a profile every single time we create a new user. And that just keeps that one-to-one relationship exists. And so as a quick recap, what we did was we said, hey, every user needs have a profile attached to it. We haven't added anything in particular yet except that one to one field that really connects that user to that profile. Then we said every profile should be called self.users dot username, whatever that username is. So it's saying self dot user, go over to that user, that Django user. We know that every Django user has username and use that as a representation for its name, its object name. Then we said, every time a user is saved, execute this function, create user profile. It's going to accept the sender. Our sender here. Our instance is going to be the user instance. So if I create a new user called test, it's going to send that to test user as, for instance, check to see if that object was created. And then a bunch of keyword arguments that we don't need to worry about. If a new user was created after post save. Then we can say profile dot Object.create user is equal to that instance. And that just creates a new profile for us every time a new user is generated. And we learned about receivers and signals and how a signal is like a radiofrequency out in space, and how a receiver's like your satellite dish. 21. Adding a thumbnail package: Alright, let's go ahead and install a thumbnailing library. So let's just close this down from our last lesson and we need to do a few things to install this. So first of all, we need to find a library to work with. So what I'm gonna do is just open up my browser and type in Django thumbnailing package. And we can go to Django Packages.org and look through a bunch of them. There's Django thumbnails, Jim, Django, thumbnail Maker. We're going to use Sorrell thumbnail because I'm familiar with this one. I've used it in Django 101. If you took Django one-to-one, you used it as well. So we know that it works. And we need to go down into installation, get the code. This is what we do pip install soil thumbnail or because we're using Pip env, We can do it a slightly different way. We can get out of our Pip env and we can do pip install Sorrell thumbnail. And we just give that a quick second to do its thing. Already that is done. So we can do Pip env, shell. And do Pip. Pip show sorrow thumbnail. And there it is. Now if you're not using preventive, you can always just do pip install Sorrell, thumbnail. You can actually do that inside of a Pip env as well. It just won't add it to your Pip file or your Pip file dot lock. Now how do we install this thing? We add Sorrell dot thumbnail, not just sorrow, which we usually do with the Django package. Just the name of the package. We're going to do the name of the package and then a folder in there. And that's how we're going to enable this. So let's go back over to VS Code, and let's open up a settings.js. And just somewhere in here, toss in soil dot thumbnail. And we should see that Django restarts. We have an unemployed migration. Let's go ahead and run that migration. Python managed up high migrate. And this just applied thumbnail 0-0, 0-1 initial, and that's from Sorrell dot thumbnail. Now how do we actually use this model usage? This is what we want. We want from soil dot, thumbnail import image field, and then use image is equal to image field and then upload to wherever we want it to upload to. And that's going to be inside of our media folder. So it could create a sub folder. And inside of our media folder called whatever. I think we're going to skip this and we're just going to use this part here. So let's go ahead and open up our profiles models. And we want to add this right in here. And then we can write this right there. Image is equal to image field that's coming from Sorrell dot, thumbnail and upload to wherever. Let's call this profiles. I said maybe we'll, we'll skip it, but maybe let's not so we can see what it's actually doing and where it's going. And now you can see that to Django was complaining. Django actually says, I'll, we cannot use the image field because pillow is not installed, but we have to go and install Pillow. So let's get out of Pip env again. Pip install pillow with a capital P. All right, let's go back into our Pippin. And let's say Pip show pillow. And this is just going to show us that it was in fact installed. We don't have to verify every single time. I like to verify just to make sure, just because I have multiple versions of Python and lots of weird Python puffing on my computer. I just wanna make sure that pillow is actually installed properly. Now if we go over to this terminal and we simply restart Django, we won't see any more errors. We won't see any more complaints. So this is good news. Now at this point we can go and edit our User Profile. So let's go into our admin and let's go to profiles. And it says, oh no, no such column. Well, what we did was we ran a migration for soil thumbnail, but we didn't make a migration and then run that migration for our new field on our profile. So let's go ahead and do that. Let's do Python managed up high make migrations. And because we're saying that it cannot be null or blank, we have to provide a one-off. So what I'm gonna do here is I'm gonna show you that I'm gonna type one to provide a one-off default. And because we don't have an image, I'm going to type the number one. And that's good enough for what we have. Now. Let's go ahead and make those migrations and then run those migrations. And let's head back over to our browser refresh. And we can see that this works. Cuckoo cool. So this image has currently one. If I click this, first of all, it goes to local host port 8 thousand slash 1 dot an immediate folder. We haven't set that up yet. And the image is just what we typed. So even if we did have our media folder setup, which we don't, this still isn't going to show an image. There's no image called Just One. It's usually like 1.jpg, F1 dot PNG, 1.and whatever. And that just isn't here. And so that's what that one did. What we can do instead is we can say browse. And we could select like animated ab.js JPEG and saving continue editing and watched s, This goes into profiles animated. Now this still isn't going to be found and that's normal. Notice that it did create a folder called Profiles and then an uploaded the image for us. So that's all we're going to do for this particular lesson coming up, we're going to be working on actually getting this to work by adding our media URLs. 22. Adding media settings: Okay, so we don't have our media URL setup yet and we need to do that. The reason we do that is because basically it tells Django where to upload all of our files to. Now if we look, if we look at this in our left-hand column here, There's already a folder called Profiles. And so it just added animated DOJ pegged to it. And that's me as an animated character. My friend Steve made that for me. So we don't want that. We want all of these images to actually go into its own folder called media. So first things first, what I'm gonna do is I'm just going to delete this because it doesn't belong there. And no harm done if you deleted because we currently don't have working image URLs anyways. So first things first, let's close this down and go into our settings.js. And that's somewhere, I don't know, I guess near the top. Let's just do this. We need a media root. Media root is equal to something. Os dot, dot join. And then the base dire that's coming from line 16 just above us. And a folder called media. And then we're going to say that media URL, how that should be served, is going to be slash media with a slash at the end. Now that's not quite enough to get us there. We also need to go into our Django URL's or URLs. And we need to first of all include our settings because we're going to be using that media URLs we do from Django.com import settings. And we're going to need to import one more in here. And this is going to come from confidant URLs, that's daddy. I believe. Let's just copy that over so we don't have to type it all again, static. And we want to include is static. Now again, that's still not enough. All we did was import some stuff. We actually need to add it to our URL patterns. So what we can say here is the URL patterns plus static, and then we want that settings.js media URL. And the document root is going to be settings.js media route. And what this is going to do is say, hey for your static URLs while you're running on localhost not in production because you don't want to serve your media files through Django in production, that's not safe before what we're doing here, that's totally okay. We're going to say connect that media URL with that media route. And let's go back and take a look at that. Then. Media URL slash media. So your website.com slash media. With that media route that, that folder that we're going to end up creating. So let's go ahead and save that. And let's go back to Firefox. And we can close that one down. Let's just refreshes page. Looks like nothing changed, but if I reload animated, saving, continue editing, it looks again like nothing changed, but this URL now changed. This URL. A shows up and B says, logos port 8 thousand such media slash profile slash animated. It has that media in there. And that profiles is coming from, if we go to profiles models coming from right there. And so now we can actually see on the left-hand side in our folders, we have media and then profiles, and then animated, dodge a bag. Now if we do a git status, this gets status is going to show us that we can add media to our repository and we don't want that. So we want to open up our gitignore. And I usually just tosses at the bottom somewhere. I just typed media slash. And then if we do another git status here, we can see that media doesn't show up. So we do git add git commit, and then you can do your regular git commit. And that simply is just not going to commit year all of your images to your repository. Because if you have 20304050 people working on a repository and they're all adding their own test images. That repo is gonna get really, really big and you're going to download their files every time you do a git pull. And it's just It's not a place you want to be living in. So that's really all there is to that. Let's do a quick little recap though. To add her media URL. We add a media route and immediate URL. Then we go to our URLs dot pi, we import static, we import settings, and then we say URL patterns plus static. And then we connect that media URL with a document root, which is going to point to media route. 23. Adding authors to posts: A rate at this point in time, we now have feed, media profiles, templates. We have all sorts of stuff in here. Let's go ahead and open up our feed. And what we want to do is we want to give a Post and author because currently we don't actually know who's posting anything. It can be completely anonymous at this point. And maybe we don't want that. Maybe we want people to be able to follow each other, like on Twitter, Instagram, anything like that. And so what we need here is a foreign key to an author. And so we can do the author is equal to models and dot foreign key. And this is going to accept the Django user, which we're going to have to import in just a second. Delete is equal two models dot cascade, which means when we delete, this user, also delete their posts. And then if we go back up to the top, we could do from Django.com, trip dot auth, dot models, import user. Think That's great. I have a hard time remembering this one as well, but I'm pretty sure it's Django.com trip dot-dot-dot models. It, I saved it in Django, restarted and there weren't any problems. So presumably, that's all we needed to do. So let's go ahead and make migrations. And the reason we make migrations is because we added an author field to our post. We added any field to our model, we have to make migration. Now this is going to say, do we want to quit and give a default? So all posts will default to a particular user, or do I want to say provide a one-off default now? And, you know, maybe we manually change it down the road. I'm going to say let's provide one now and we can manually change down the road. And I'm just gonna say that every post is coming from user ID number one. And we can deal with the repercussions of that in just a moment. So let's go ahead and close that down. And let's look at our posts. Oh, no such column that actually makes a lot of sense because we made that migration, but we didn't actually migrate it yet. There we go. Let's see. Our first post was author Caleb. And so when I typed in one, all that did was find the first user. Where are my users? So I have these users in here. And at the very top, you can see user ID, one in that URL. And that's where I got that one from. If we wanted all the users to be our test user or all the posts to be authored by our test user, rather, we would say number two. And so now when we add a post, we can select an author. It's going to be a user from our app. And we can give it some text. And currently all of our posts are from me. 24. Adding author data to templates: Alright, let's go ahead and add our author image and name to each post. So if we go back to our application here on our homepage and refresh, we haven't done anything with the template and everything is just the default stuff that we find in here. So let's go ahead and change this image, and let's change that name as well. So let's go ahead and open up our homepage. And in here, because we want to use our thumbnailing I live thumbnailing library or thumbnailing package. We need to load that into this template. And that just tells Django, hey, we're going to be using a package called thumbnail or actually rather load a file called thumbnail. And it allows us to use all the functions inside of a file or module called thumbnail. And so we can do that. That's where are we here? Where are we here? We need to replace this image and that name. Now we haven't author, so we can replace that name right away. Every post has an author and every author has a username. And so let's make that connection right away. Every post has an author that authors a user object. And when we are in our admin and we're looking at users, every user has a username right there. So let's go ahead and okay, look at that. Every post has now made by me. So that's that's right. That's is actually correct. Will make posts later with different users just to demo to demonstrate that. Let's go ahead and change that image. Now that image is going to be a little bit trickier because we're using a third party package. So what we can do is go back through that third party package. And how do we use this in our template? We load thumbnail. That's where I got this from. And then the simple usage is right here. And we use a template tag called thumbnail. We give it the URL or the object of the image, the image size, what kind of cropping we want. And then we set it as a variable name. And then inside of our image we can set that image URL, that image width, and that image, I think I said set that image URL, height and width. We actually just need to get it. And so that's what this someone who is going to do. And then we say n thumbnail just so that we can stop using this IM variable in our template. So in fact, what I'm gonna do is I'm literally just going to copy this and I'm going to modify it. Now. Where do I get that image from ly, get that image from our post author. And if we go into our profiles here, we're going to see that every author has a one-to-one field, or every user has a one-to-one field and that user has an image. And so what we can do here is instead of saying post author username, we can say post dot author profile, believe that's what we called it. That's a related name here, profile dot image. And that connects our username or regular user rather to our profile using the related name called profile. And then gives us access to everything else inside of this model here, including image. And do we want to crop this to be 100 by 100 with 373? So let's do the same thing here. 373 by three, 73, centering is fine. And let's just indent that. And now we need to change that URL so that source, that image source is not going to be from an splash anymore. And image source is going to be I M dot URL. And I got that from right here. I m dot URL. Let's go ahead and save that. And let's see what happens. Do I have a typo? Maybe, maybe not. Aha, look, there I am. That's me. Animated me. Now let's go ahead and add one more post and hear from someone else so that we know it's not just always me in there. Let's go ahead and create a new post. A note. Yet let's add a post. That's what we want. This is from a test user. Author is going to be test. Save. And let's go into our profiles. And let's edit our test user. Because that image is going to be one currently not found, not useful. Let's change this to that photo of me. Save. There we go. So it is in fact working. Now this isn't linking to a profile view. This isn't linking to a read more view or a dedicated view. We're going to get started on those pretty soon. But for now we just need to get this image and that name up and running. 25. Post detail view: Alright, let's go ahead and add a post detailed view and a link. We have these to-dos Actually, this one can be cleaned up from the last video. Oh, no, that's no, that's still pretty much correct. We still need to actually have that URL in there. So add URL below. So let's just close this down and let's maybe open up. Lets go to feed URLs. And we already have a homepage URL in here, and let's go ahead and set a new URL. And so what we can do is pretty much copy this over and wow, we didn't have to copy that over. Let's not copy that over path. I'm going to say int, and that's going to be a PK. I'll describe what these are in just a second with a slash. That's what this is going to be, basically our URL, so our website.com slash and then a post views dot post detail view as view. We haven't created this yet, but we will, and we're gonna give this a name of detail. So detailed view. Now post detail view doesn't exist. We need to make that exist in where's that coming from? Our views. And there's actually a syntax error right there. There we go. That's what we should be saying. We should see module phi dot views has no attribute. Post a detail view. Let's go ahead and add that. So we can do class post detail view. And this is going to be a detail view. So we need to go and import that at the very top detailed view that comes from our generic Django views. Let's go ahead and say HTTP method names is equal to a list and only get as available. That might be the default, but I like setting that anyways, the template name is going to be feed slash detail dot HTML. That's where this is going to be living. And we say feed, because that comes from over here, namespaces. So if we had 20 other apps, maybe we add feed and feeds. Plural, it would be important to namespace o. So if we had feed such detail and feeds slash detail, we could say feet or feeds slash detail. And then just sort of namespaces are templates for us because Django is going to look all over the place for a template and you never know which one is going to find first. Actually, that's not true. You probably will know to one. It'll find first when it shows the wrong one. And you just don't want to run into that kinda confusion. The model is going to be a post model. And the context object name by default, I believe is usually object, but I like to change that to be the name of my model. It's going to be posts. So in the template I can just write post. Nope, post. Now let's go back to our homepage and let's add a link to this detail URL here. So that link, where's that going to be coming from? So we say open up our syntax URL, feed detail, and then we give it an ID so that Post.all. And let's quickly go over what I just did there. So for every post, in posts, if I go into models, we know that there is an ID and we can see that ID if we go into our migrations initial, every model has an ID. Every model has an ID by default. And so that's where we're getting that ID from. A occasionally, you will see instead of ID, you will see the letters pk, primary key. In Django. They're the same thing. So feed is our app, and that's coming from our namespace URLs. And then details what we just wrote In our URLs. So this is the detail part, this is the feed part, detail bead. To go ahead and save that. And when we refresh our pitch, we should see that this now goes to a URL and this will go to slash for post number four. And that's all there is to this. We just have to add our template to this. And so we actually have this link working. Now if I go back and click the one prior to this, this will go to three and go back and go to the one prior to that, it will go to two. 26. Post detail template: Alright, let's go ahead and add a detailed view to each post. So we said the template name has to be feed slash detail index.html. So let's go ahead and add detail dot HTML into our templates here. So feed, templates, feed. Let's add a new file in here called detailed layout.html. And let's just do this test. Let's just make sure that this works before we get too far into this. And it works. So now we know that we want to extend from our base dot HTML file so that we have all of our goodies in there. In that base dot HTML file. We have body and a title block. So we want that title block in here. Let's go ahead and copy that. And to change that title to be, let's not do anything crazy yet. Let's just do a custom title just to make sure that that works. We can also add block body, more stuff in here. And it's not extend is extends. If you yell and scream San Kayla, you did it wrong. Okay, there we go. And the title says a custom title. Okay, let's go ahead and change our custom title to be our entire post, post dot text by post dot author dot username. And this is going to be a fairly long title. If your post is like 240 characters, this is going to be a super long title. How would this has, this is a second post by Caleb, Cuckoo. Cuckoo. That's when we're looking at, we go back. This is from a test user by test. More stuff in here doesn't show anything. But what we could do is we could say post-doc text. And it shows our, our little message. And so it's now moving. This is a second post. This is the second post from our ListView to our detail view. And if you're wondering where I got that from, in our post detail view, I have modelled as equal to post. And the context object name is equal to post. And that's why we're able to use post-tenure postdoc text, it not object and 0.2x, which is what it would be by default, it's postdoc text. Now let's go ahead and make this look a little bit nicer. So I already have a component lined up, one that I would like to use. That's this one here. What I can do is just simply copy that code. And we're going to paste that code in here. And we need a date. And that's going to be post-doc date. We're not using categories, we're not using titles and we just want to texting here. So let's go ahead and change this to be our post dot text. Let's take a look. And what this looks like so far. One too many curly braces there. We don't read more, we don't need that in there, read more and get rid of that. And basically what we're doing here as at this point, we're taking everything in here from our homepage and we're just going to copy it over. And in fact, we can actually do that whole thing. So instead of going the long way and doing it all manually, what we can do is come in here, paste it from our homepage. And it says invalid block tag on line 22, thumbnail. And that's because we didn't load that in. We have to load in thumbnail. So load thumbnail. Now ha, and that's looking pretty good. Now we have this issue where we have a Read More link and we're already on the detail view. So this is just going to link to itself. So that's no good. How do we get rid of that? Well, what we can do is we can move this into its own template to include and give it a Custom Variable to say should something show up or should something not show up? And in our next lesson we are going to be talking about the include template tag. 27. Include tags: Okay, let's go ahead and add an include tag. So what I'm saying here is I'll just move this up to a place where I can rate include. And the idea here is that we can write similar code or the same code more than once in, or rather just once in one place and included in more than one place. And so we have this code here in our detail view. And it is identical to what's in our homepage here, the exact same code. And so what we can do here is we can basically cut that code and we can go into a place that it's going to look for templates. And it's going to look for templates in TIL templates. Let's go ahead and create a new file. I'm going to give it a folder called includes first includes. And I'm going to call it post.html. And I'm simply going to paste all of this in here. And on our homepage instead of writing all of that stuff, what we can rate instead is include, includes, That's the folder I put it in, post.html with some contexts. So that point is going to be a post. And so for every post on the right here, that's this one. We're also going to throw it into this post.html with the name of post, which is just going to allow us to write this postdate, post texts. All that good stuff. So let's go ahead and save that. And let's see if I have any errors on here on the home page. Let's go to the homepage. Oh, look at that. I didn't load thumbnail. So on our homepage we have thumbnail that's actually not needed because it's not used in this file. If we go into our post.html, we can actually just copy and paste that in there and cut and paste that in there. And this is going to work for us. And the reason for that is because this thumbnail is actually being used in this file homepage. It's not being used in that file. So let's go ahead. And by the way, this was inside the for loop on our detail view. Let's go ahead. And in our body, just paste that in there as well. We're gonna say include, includes slash post.html with post is equal to post. And we don't need the thumbnail because thumbnails not being used in this file anymore, it's only being used in post.html. So let's save all of that and refresh. And let's go to here. And it looks like it's filtering out, which is pretty close. That is what we want. Now what happens if we don't want this read more in here? We're already on the detail view. We don't need to link back to this detailed view. Let's go ahead and add a new context variable here. Show detail. Link is equal to true. And I'm just gonna copy that, that name. And in my post.html, I'm gonna put a read more in here. So if not a read more, how is reading? Read more? And trying to do too many things at once there. No detail view. And I don't mind that there's a typo in there. I'll fix that in just a bit. And so what we're saying here is when you include post.html, also put in post and show detail link inside of this file. And so if Show Detail link exists and it is in fact true, we can show that Read More link. Otherwise it's going to say no detailed view. On our detail view. There is no detail view and that's because we didn't put that in there. This is our detail view, this is our homepage. However, if we go to our homepage, that link still shows up. And that's exactly what we want. Now we don't need that no detail view text in there. So let's click this and that moves everything over. So maybe we, maybe we do actually want a little something in there. Let's, let's just add a span. And nbsp, Just a force that text over there. And that's just because we're using flex and it justified between. So we need at least two items in here. Either the read more item or the empty span plus a div for all of this. Now what's nice about this is when we do this at this include, when we write an include, we don't have to go through multiple files and change this URL later. We can just go through the post.html file, change at once. And it's going to change it for us in multiple places. So currently this link goes absolutely nowhere. And on the homepage, this link also goes nowhere. But once we changed that because they're both pointing to the same file. Detail is including post.html and homepages including post.html. We only have to change the content or the URLs or the structure on post.html. 28. Post CreateView: Let's go ahead and create a new create view for creating posts, because right now we can only create posts through the admin. So this stuff in here, linking to a profile view will come back to that in a little bit. That's why we have a todo written in here. So let's close up all the stuff from our last lesson. And when we're creating a new view, what we wanna do is go to our URLs and we just want to write something new in here. So let's write, well, first of all, let's give a comma at the end there. Path. New with the slash. Let's go and add views. Create new Post.all as view. And the name is going to be, I guess new post. Now, this doesn't exist. In Django is complaining about that. So we need to go and create a new create new posts view inside of our view is file class. Create new post. We don't know what kind of model this is going to be just yet. We need to create something. We need to. It's literally right in the name there. We need to create the post. So we're going to use a thing called a create view. And where that comes in from is from Django, dot, dot, dot, generic, dot, edit, import, create view. And that's part of our crud operations, crud operations. So now this new create, create new posts view needs to have a model and it's going to create a new post. We're gonna give it a template name. And this is going to be feed stylish creates dot HTML, which by the way, if you don't know what comes on a create view, you can always go to CC BY VSCO WK. And this will show you classy class based views for Django. And that'll literally just show you anything you want to see. You want to see what a create view has, all the methods and properties on it. You can see all of them there. And the fields we want to add in your, Well, let's open up our postmodern note. It goes to feed models. And the only thing we want to add is text. We want the author to be audit added automatically so that I can't create a post on behalf of you. So let's go ahead and say the only field in there is going to be taxed. And this is going to be a list. Now we need to create this creates dot HTML file. And so we said it's going to be in the feed HDF5 file in our template. So new file, template is going to be create dot HTML. And let's go ahead and extends our base dot HTML file. And let's give it a block, title. Create a new post. And let's give it a block of body. And in here we can say form dot as P. And that's going to put all of our form items in paragraph tags. Now that's nice and everything, but there's no link here to actually go create one. So let's create a new link in here. So let's open up our homepage. Nope, let's open up our base dot HTML and it's already folded up. So let's say if the user is authenticated, they can log out. So they can also create a new post. So let's copy this. And we're gonna say New Post. Now, where does that link come from? That link comes from feed New Post. So we need to add that in there. So we type feed colon New Post. And we're probably going to want to change that icon. But for now, let's just see if this works. New Post goes to nu. We have text in there. This is working now this is not actually going to create a new post at this time. So if you wanna test this out and it doesn't work, that's normal, that's expected. We have a few more things we need to do here. That New Post though, let's go ahead and change that icon. And so where's that coming from? That's coming from Box icon. So let's see this box icons. Google is your best friend. And search. Plus maybe. Let's add layer plus. And so we click on that and it says name is layer plus. And that's a Web Component. The straight HTML, that's pretty cool. I didn't know that we can do that as a font. We want Bx, Bx layer plus. Now looks a little bit different from what I have done it. Where are we? Where are we? Where are we? Bx, Bx logout. We want layer plus. There we go. Now we can go to our new post view. Now we have a problem with this. The problem is anybody can come here and essentially tried to create a new post. Now it's not hooked up, so it's not going to work. But anybody can come in. So let's copy this and let's open up a new Incognito or private browsing window. And it works for me while I am not logged in. And so we need to force the user to be logged in. We can do that quite easily. I'm gonna show you how to do that in the next lesson. 29. Forcing authentication: Alright, let's take a look at how we can force the user to be logged in. Because right now a user doesn't need to be logged in in order to attempt to create a post. And we don't want that. We want them to always be logged in. And so let's go ahead and open up our editor and close everything down. And I'm going to show you the easiest way to do this. And we go into our views and we're going to import this thing called a mixin. So from Django dot contrib, nope, dot auth, dot mixins. Now we'll find out if this is wrong. Django will complain quite loudly. This is wrong. Do a log in, required, mix in. Let's see if that's right. Let's just save that and see what Django says. Now. Okay, that must be read. So that login required mixin. We can now put on our create view and inherit two classes at once. And so what this is going to do is say, hey, if the user's not logged in, kick him out. Otherwise, if that user is logged in performing irregular create view tasks. And so I'm currently logged in here. This is going to work. And when I opened up a private browsing, I'm not logged in. So it didn't work for me and kicked me out and brought me to the login page and said next is going to be the new URL. So now I can login here. And then it brings me to the new the new post page. 30. Creating a new post: Okay, let's work with our Form View a little bit here. So inner form view, not our view. Maybe let's work with our template first. That's going to be a little bit funnier, I think. So Enter template. We have this form here. And while that's good, and it seems like it's doing what we want it to do. If I right-click and inspect your gonna see that It's just a paragraph, there's a div, a paragraph, a label, and an input. And I mean, while that is ok, we don't have a form. So how do we submit this? How would do we submit this? This just doesn't work. I can hit enter as many times as I like, and this is just not going to work. So we need to wrap this in a form. So let's do exactly that. And let's do form. Method is equal to post action is going to be its own page. So whatever page, whatever URL this is coming from, we simply wanted to post to itself. Then we need some sort of submit buttons. So let's go ahead and add a div in here. And so I want to add a nice button and already have a component lined up for this. It's this nice one. I wanted this Send button, I want that nice one in there. Let's go ahead and copy all the code. Let's just copy the send. That's that first one there. And I just pasted that in there. And let's see what that looks like at first. Okay, send not bad, not bad. Why is I padding off a little bit there? And button is acting weird. Oh, it's got margin all the way around it. Let's say margin x is going to be 0, margin y is going to be three. And that for lines it up a little bit better. I guess we can always make this look better, a lot easier. So instead of saying send, let's say create post. And this button, its type is going to be Submit. Now when you submit a form in Django, you're going to see a CSRF verification, failed requests, a boarded. And so whenever you see that, what you need in here is a CSF, CSRF token, C, S, R, F underscore token. So now when we go ahead and refresh and test this out, at least we're getting a different error at this point is saying not null, constraint, failed. Post author ID needs to be filled. And that's because we go to feed models. Our author is not being set inside of our form. And so if we go back to our create view, we said the fields, it's just text, not the author. If we add author, this might look a little bit different. We can now select who he posted from, which is not what you want when you're on Twitter, you don't want to be able to select from 340 million other users or however many users they want. A really long list. Plus you don't want to be able to post on behalf of someone else. You want it to be a little more secure than that. So we don't put that in there. But it's complaining that it's not in there. So what we need to do here is we need to say def form valid. When this form is valid, it takes a self and the form itself. And at the very end, regardless of what we do, we need to return super dot-dot-dot form, valid without form. And in here, let's go ahead and grab that form. Let's just call it object though. So form, dot save and commit is going to be false, which means we're just simply just not going to save this yet. We're just gonna grab the form and all the details ended, but we're not going to save it yet. Then we can do object is equal to an object. Author is a, is equal to request. That user, we don't have requests and we're going to need to add that in in just a second. And then we can do OBJ dot save. Now where does requests come from? If you try this out, you're gonna get an error. So we need to throw this into dispatch. So we do deaf dispatch. Self request, args and quarks are eggs. And quarks. Return super dot dispatch, request, args and quarks. And you notice how we have requesting here. Well, this is a class, so we can do self.age request is equal to the request. And down here we can access it with self.age request. And dispatch will always be run before functions like form valid. Let's see what kind of errors we have here, right there should not have a colon. And that seems to work. Okay, one more thing we need to do is on a create view, we need a success URL. So what happens when the form is successfully submitted? We can say the success URL is going to be just our homepage. Or we could reverse it to be our homepage using Django, reversing URLs and basically say, always go back to our index page, but our index page is always going to be hard coded to be the same. In this particular case, it's always going to be the same. Or homepages always, always, always going to be just a slash. So let's go ahead and give this a try. Test post number one, create post. And there it is, test post number one. And I can go in here, has posted number one that is working for me. We are now able to create new posts. 31. Tailwind CSS modal: All right, let's add a tailwind modal so that we can start Ajax and new posts onto the page. Now I already have one picked out and I just got it from tailwind components.com. They have a lot of great components on here. And this is the modal that I want to use. And a modal is just a little dialog pop-up box in front of all of your other content that says, hey, do you agree with cookie user? Hey, do you want to confirm or deny doing this thing? And so I'm literally just going to copy this code. And I'm going to close down these files and go to base dot HTML. And in my base to HTML at the very bottom. Right, I'm just going to paste all that code in here. And I don't even know what that code looks like yet. So maybe it's too dark. So we do actually want that background to be a little bit transparent here. And so in the realm of tailwind, We can do that. So we've got BG gray 800. We can also do BG opacity 90. And that makes a little more see-through. What if we wanted to do lake? Could we do 75? Yeah, we can do 75. Let's let's stick with 75. This looks pretty good. We're not going to have a modal title, Are we? Yeah, maybe we will. New post. Now, not page New Post. And the text that's going to be in here is going to be some sort of form. So we're going to put a to-do in here to do add the Ajax form. And we can keep the Close buttons in there. Let's refresh this new post. Today is going to be in there. Let's change that from Agree to create post. And in fact, let's go to our, our create view. And let's grab this button because I really like that button. And let's, let's put it beside it. Let's see what we need to change here. So I just copied and pasted that. Cool, that actually worked out pretty good. Lets go ahead and fix that indenting. Delete that one, and let's delete the close one because we haven't x up here, we don't need that close. So there we'll create posts and a close. Let's create post. Can we do something a little bit smaller in here? Can we do text SM? Yup, that makes a little bit smaller padding. Y is going to be one padding, x is going to be three sources and make this a smaller looking button that we'll create post. And maybe let's add a margin to the top here. And again, this is just regular tailwind. So if you're not sure what any of these do, a, you can just read through them, which is really nice tailwind as a utility first library. And the documentation is actually quite good. And so we can do margin top, and let's do margin top of C4. Does anything. There we go, little margin top there. Create posts. Now it's not going to do anything. And I think what I would like us to do is set this up for success and in the next couple of lessons. So first of all, js modal is what I'm going to call this. And I also want this to be hidden by default. And that button, that submit button though we want to work with here. Let's change that from Submit to button because we're going to use JavaScript for this. This is going to be called JS submit. And nothing shows up, it's completely hidden. We have one more thing to do in here. If a user is not logged in, this component should not show up. So we can say if request dot user_data is a non ne miss, we can grab all of this code out dent. And if, and it still looks like nothing happened. But the idea here is that this modal should not show up. In fact, let's do a little demonstration here. Let's Where did I put hidden? Hidden was called hidden to. That's not a class name. And that's not showing up. And this is why we test things. Because I said if request that user is anonymous, we can say if request that user is authenticated, what I originally was going to do is say, if not anonymous, but we could do the same thing to sort of the opposite. Or we can say if request that user is authenticated, then show it. There it is. And let's just hide it. Done. And that's all we're going to do for this lesson. We just wanted to add a nice tailwind modal so that we can start adding stuff to it a little bit down the road. 32. Enabling static files: Okay, now before we continue, we need to add static files and folders to our application. So let's go ahead and close this. And we're going to want to open up our settings dot py. So somewhere in here, I'm just gonna throw this at the bottom. We want static files and see this all uppercase static files. Directories is equal to a list. And we're gonna say OS dot, dot join our project directory, which we have set up in a much earlier lesson. And we're going to call it front end. Then we're going to add a static route, not status, but a static route is equal to OS and dot, dot join. And then we want to join that base directory with static. And lastly, we want our static URL and put that on a new line. Are static URL is going to be slash static, which looks a lot like our media URL. This is just where our static assets are going to be built. Now, a static asset is like your own JS file or your own CSS file inside of your own application. We didn't cover this in Django 101. Now let's go ahead and create a new file in here. And we're gonna create a bunch of folders first. So the first folder is going to be front end, and that matches what we wrote on line 156. And then a subfolder called JS, and then a file called main.js. And we're simply going to say console.log, hello world with the heart. And then in our base dot HTML, what we can do is at the very top we can say load static. And at the very, very bottom where we want to load our static JavaScript, we could simply say script SRC is equal to static js slash main.js. And let's make sure that that is a string. Now let's go into our page here and go into the console. So I just went right click, Inspect Element, console, refresh and nothing happens. So what we want to do at this point in time is we want to type Python managed dot py, collect static and watch what it does. On the left here is gonna create a static folder for us. There is our static folder. And so our static folder whenever we run this static in here, and what we need to do is close that. Whenever we run this static template tag, this function, it's going to then look forward this folder, this file. And that's going to go in here, js, main.js. And so basically just copied over our code from our front end folder into a static folder. So let's go ahead and refresh. And it says hello world, heart sign. And less than three. Now moving forward, can we do anything we want in the front end? Hello, this is front end. In fact we can. And so the whole reason we collect static is because ideally we don't really want all of our front-end code to be executed. We want it to run from our static folder. And so this is how we do that and we use that collect static command to collect all of our static files so that when we're in production, Django knows where to find things. We can say, hey engine X, instead of using Django or we can say hey engine next, look for all of our admin files in static slash admin or something along those lines. So moving forward we can put all of our code in front end slash js, slash main.js. And when you go to deploy your application at some point in the future, whenever you're ready, you just make sure you type Python managed dot py, collect static. Or if you don't want, watch this. It's gonna ask me for input. Are you sure you want to continue? Its gonna override a bunch files I could type yes, or I could type Python managed dot py collect static dash, dash, no dash input. And it's not going to ask, it's just going to do it. Last but not least, we need to. If we do a git status here we are going to see something interesting. We want to commit our front-end code, but our static code has all this admin stuff in it and it's going to have a lot of other stuff in there as well as your application grows. We do not want to commit our static folder. What we do want to do is ignore that static folder and only ever commit the stuff that we've changed in our front end folder. So let's open up our gitignore. And at the very bottom where it says media, let's type static. Let's do a git status once more. And it doesn't show up anymore. And we are good to go. We have our static files up and running. So now we can write our JS in a file in our project and we don't have to write it all in. Well internally. What I mean by that is if you're not a JavaScript developer is we don't have to write our JavaScript in here. We don't have to do that. We can write all of our JavaScript in a file and then we can apply caching to that. So that's your script, either only loads or downloads once, or is at least really, really fast to load for your future website viewers. 33. Adding JavaScript: Alright, let's go ahead and add jQuery. So first things first, let's just go to the jQuery dot website is a jQuery.com. It is jQuery.com. Download jQuery. Or instead of doing that, we could do jQuery CDN, and we're on version 3.5.1 right now. So let's go and get the most modern version. Is there a CDN in here? Now this is the CDN, Google, Google, Google. Okay, so we want the minified version. We want full. So I'm gonna do this. I actually, no, let's not do it that way. What do CDN JS. And this is acting, let us copy the whole thing. So copy the script tag. And that's gonna give us 3.5.1 jQuery dot main.js. So it's been minified already. So all the spaces and stuff have been removed. And let's just throw that in front of our main.js or main.js can then execute any sort of jQuery. Next for Ajax calls, we have this strange one which I'm not going to walk through the entire thing. And so this is what it looks like before we do any sort of ajax. We're gonna set up our Ajax. And what this is going to do is look for where are you, where are you, where are you? A CSRF token is going to set that cookie so that we don't have to set it on every single Ajax request. Now, I would highly recommend just downloading the source code and copy and pasting this and making sure that it's before your main.js and you can even throw it right inside of your main.js. In fact, let's go ahead and do that. Let's delete that. Delete that. And let's go inside of our main.js and let's just throw that in here. And the idea here again is that remember when we were trying to create a new post and we got that CSRF token error. Well, with ajax, we don't know how to necessarily submit that. But CSRF tokens are cookies. And so we can set a cookie, or rather we can get the cookie, then we can set the request header to use XSS CSRF token so that in the future, when we make an ajax request, we don't necessarily have to apply a CSRF token to Oliver Ajax requests. Now there's a lot going on in here, and this is not a JavaScript module. Django to1 is not meant to cover a lot of JavaScript, just a little bit. But if you're unfamiliar with this, I would highly recommend checking out to JavaScript, one-to-one JavaScript to JavaScript through any one of my other JavaScript courses. So let's go back here and let's just make sure that things are loading the way we expect. Cool, we don't have a favicon, So that's totally fine. We now have jQuery and install, installed. And what we can do is right-click inspect, go to console. And if we just type dollar sign, we should see something in there. We should be able to grab anything we want. So we can grab our entire body and it gives us an object. And that's how you know, jQuery is installed. Alternatively, you can always just type jQuery. Well, make sure you're actually typing it jQuery and you get the same thing as that dollar sign. Once you have jQuery installed. Let's head on over to the next lesson. 34. Toggling the modal using JavaScript: Alright, let's take a look at toggling or modal. And a couple of lessons ago we created a modal and we need to basically make it show up. How do we make it show up? We're going to use JavaScript for that. So what we can do, first of all, is let's go into our, not our homepage, but are based on HTML. And wherever say new posts, this is a link. And let's give this a new class of js toggle modal. And let's open up our frontend jazz main.js. And let's just scroll down to the bottom and let's create a new event listener. So document, dot on dot click or not dot click, just click dot js, toggle modal. Function E. For event e dot prevent default. So if it's a link, don't go anywhere, don't do anything. And let's just say console.log. Hello, I was clicked. Now if you're unfamiliar with jQuery, you can always take my jQuery 101 class that will cover all of this as well. But for the time being, I'm going to assume that you know jQuery at this point, if you don't know jQuery, basically you are done this project. There's a little bit more we can add, you can skip ahead where we add a detail view for all of our profiles. But if you don't know jQuery, you don't have to worry about the Ajax stuff. So some of these lessons you can just skip through. So let's go ahead and open up our console. And every time I click this, it says I was clicked Hello, I was clicked, clicks it 11 times. I'm going to click it 11 more times. And just keep saying I was clicked so it's working. So what we need to do now is we need to, in our modelling or base dot HTML, we have JS modal and we need to toggle this class hidden on and off. So we can now select that modal toggle class hidden. And let's just do this. Let's see if this works. There we go. That works. And now we need to figure out how to close this so that SVG, we need to close it with that SVG. And we could apply the same thing here, js toggle modal. And because all we're saying here is every time a class called js toggle modal is clicked, that modal is going to toggle that class on and off. And because we can't click anything else outside of here, we can just simply click that x. And now it is toggling on enough for us. 35. Ajaxing new posts: Okay, let's go ahead and start creating posts dynamically with Ajax and jQuery. So as little reference, Let's open up our base dot HTML. And in our modal, that's our modal here, we want to add this Ajax form. And so we don't actually need to add a form at all because we note that this is already JavaScript powered. We don't have to make sure that it's not necessarily not JavaScript ready, so that we can assume safely everyone is using JavaScript. So let's add an input in here. With no, not none input. Let's do a text area class. Border to border, blue 500. I believe that's going to do what I want. And let's say the rows are going to be three rows. And there's gonna be some margin in there to margin top and bottom of six pixels are not six pixels. And that's going to be for REMS to REMS, I can't remember. Anyways is going to add some margin top and we can always change that if we don't like four, we can always use eight or 16 or something like that. Slash text area. And for JavaScript, and to be able to grab this, we need to add a JavaScript base class. I like to prefix my classes with just jazz. So JS, new post. Now I'll just call it Post. And we can get rid of that to-do. Okay? Okay. We need to add some Whitman there. So with full and that didn't do what I want. So let's go to tailwind CSS doc's type in width. And it is it's W full. Why did that not work for me? W full might be the parent element. There we go. Cool. So we have a text area and there let's add a placeholder as well. Placeholder. Enter your post. Let's also add a maxlength is equal to. And how long are these posts is supposed to be. As a maximum feed models 240 characters. So we're gonna say this has a max length of 240 characters. So now what happens when we click this nothing. We need to make sure that something happens when we click Create Post. So when we click create post, which has a class of JS submit on it, we can work with that. We can say document dot on. And I'm just changing these together. Click dot js submit function e, e dot prevent default because I don't want it to do whatever its natural thing is on the browser. And let's just say console.log. Submit me open. And there it is, shows up. Submit me, cool. So now we need to get this text. And before we do an ajax, we need to do, we need to get the texts, OJS, post texts, that's what we want to grab. Just go ahead and grab that text. Const. Text is equal to that element dot text, note not dot txt, it's dot val. And let's also trim it. Then we can say, if there is nothing in there, if no text dot length, if there is no post whatsoever, simply return false. Otherwise, let's grab that modal and let's hide it. So I'm gonna grab that and hide it. Add class hidden. And this is just going to confirm that this is actually going to work for me. So nothing happens. When I add some text in here. It closes for me. Cool. But now when I open it, the Texas cylinder, so we need to clear that out. So I'm not going to write pretty jQuery here just because this is not a jQuery module, Jin Django to1 is not meant to cover jQuery too much. And we're just going to blow through a bunch of this using jQuery because it's nice and fast. And so we also want to Slashdot post and give that Val no value, 123123. And it clears it out forming good, good, good, good, good. That's exactly what we want. Now. We need to send this data somewhere. And what I'm gonna do is I'm gonna paste in some code here and I'm going to walk through it. I just don't want you to have to spend the whole day watching me type. So what we're gonna do here is I'm gonna paste in some stuff. And we're going to adapt our code. So we wrote up to here, everything below here is brand new is what I just pasted in. So btn prop disabled. And let's go ahead and well, first of all, read through this. This is going to select the button. It's gonna make it disabled, and it's gonna make that text say posting. So we need to select that btn. So we can now say constant btn is equal to this. And that's that JS submit button. Then we're going to Ajax and post text areas looking for a datatype post URL. Let's do this. Data. Host URL is equal to a place, and we're going to see that this breaks, but that's okay. We can change that in a little bit. The data itself, text is going to be text and that's fine. And this is just going to make this accessible on the backend so that Python and Django can say, oh, just look up, requests outpost dot text, and it's going to be whatever text is. So this is our key. This is our value. On_success. We are going to return some HTML. We're then going to take that JS model and we're going to add a class hidden. We already did that. Let's go ahead and hide that. The text area, val nothing. We can replace that with what we wrote. And we're gonna say post container append HTML. We don't have a post container. So the idea here is to make this look very, very dynamic. So if we go back to our HTML here, actually it's going to be in our homepage. All of our posts need to be wrapped in this thing called Posts container and doesn't need to be called posts. Container. It could be called anything. I'm just calling it post container. Just whatever name you use, make sure it's consistent. So I'm using id posts container. You're going to want to use ID posts container. And that's going to prepend the HTML, whatever that is. We don't know what that is yet. On error console warned that there was an error and then change that button state from disabled to false. So it's not disabled anymore. And change the text inside of that button to say that there was an error. Now this should trigger the error for us. So let's go ahead and give this a try. Let's click on new post. Helloworld and says it's posting text areas not defined. Text area is not defined and that's because we didn't set it there. What we can do is just this because this is the text area right here. So this is going to get our text area. And then the data property, the data attribute, data dash, post, dash URL. And this is using jQuery again. Here we go, cuckoo, cuckoo goal. So we get an error in here. And basically localhost port 8 thousand slash place doesn't exist. And that's getting it from right in here. So we need to give this a URL of some kind. So where should this go? Well, we have a view already set up, so let's go ahead and minimize some of this stuff in our feed such views, we have a Create New Post view in here. We can simply say, if there is a post request, something along these lines, deaf post, self request, args, return. Let's say a thing in here. And this is not actually going to return anything that we want, not yet anyways, this isn't going to return HTML or anything. This is just an example so far. But because we can use this URL, we can go over to our URLs here, Create New Post, and we can use feed colon, new post. So let's go in here. Let's add URL, feed colon, New Post. And that's going to make sure that we are in fact logins. So if I right-click go to definition, it's gonna make sure that we're logged in with that login required mixin. And when we post, it's going to do something. So this should break for me, but let's do this as an example anyways, this is a post request, do, do, do, do, do, do, do. And this is going to spend my console in the bottom left here. Go ahead and refresh this hello world and this is not going to actually work. It went to a different location this time I went to slash new. That's exactly what we set up. So let's check out that URL in their URL slash slash. That's exactly where we wanted it to go. And here it is. This is a post request. This is a post request. So we know that this is working. Now we need to do some post-processing in here. What can we do to create a new post object? So in here, because we already have post imported, we can say the post is going to be post dot-dot-dot objects, dot create. The text is going to be requested outpost dot text. Nope, that's wrong. Who requested outpost dot get text? And the author is going to be request dot user because we have access to that request object in here. We don't need to use self dot request. Now where I'm getting that text from is in main.js. Not necessarily this one, but this one. It's the key we're getting and it's going to get to this value, whatever that value is, it just happened to be that they're named the same thing. We could name them differently if we wanted to. And lastly, let's render some HTML. So let's return render. It always takes request as its first parameter. Then it needs to take a string as a parameter to which particular template we want to use. So let's include or includes slash post.html. If we go to includes slash post.html, we want to render all of this in here. And what this is going to do is it's going to use this block for a third time. So it's gonna be used on the homepage, is going to be used on the Detail page. And would we Ajax a new post in here? It's going to show up at the very top with this exact styling and everything. Lastly, we need to give it some context. So that new post is going to be post, and that new post comes from post.html. We're looking for this name right here. Post. That's the context we're throwing in. And it's going to match this new post up here. So it's gonna have texts, date, and author. Now on our homepage we also have show detail link is equal to true. Because we're on the homepage, we want to pass that in as well. So show detail link, we're gonna make that true. Lastly, we want to set that content type is going to be Application slash HTML. No, we haven't imported render yet. So we need to go ahead and import render. And that comes from, this is an easy one to remember. Django dot shortcuts, import, render. And let's make sure our Django is not complaining down here. We don't have any complaints yet. Okay. Are we ready to test this? This was a lot to go through. Let's make sure that this is working the way we expect. Otherwise It's going to break. Will this break? And if it breaks, we're just fix all the bugs one-by-one as we normally do as developers. Look at that, will this break Read More, is gonna go to ID number six. I posted it. I posted it just now. And that works perfectly. Let's go ahead and make sure that we can do this one more time. One more time. And let's open this up and make sure that that was all closed and everything. It says New Post, oh, this is good. Good things are happening at. We now have an Ajax request. That is sending data to a dynamic URL. And so let's go over this once more. When someone clicks JS submit, we're gonna grab that text. We're going to make sure that the text has a length. If there is no length, skip it. Then we're gonna grab that button, which is this particular button, that button that you just clicked. We're going to say it is in fact disabled and we're gonna change the text to posting. And this just makes sure that people can't click it twice. Then we set up Ajax. And actually ajax was set up already to acknowledge that it needs to look for a CSRF token. So we don't need to do anything beyond that. We're gonna say that type is post. And if we go in here, that matches deaf posts, that URL is going to be js post text, dot data, post URL. And that's going to come from our base dot HTML. Nope, yep, yep, that's where our text area is. So JS post text, that's our class is going to grab that. And it's going to grab data, post URL, And we're throwing in a Django URL. So it's completely dynamic. And this way we don't have to put any sort of hard-coded URL inside of our JavaScript. We can change it on the Django end. Redeploy your application and this will always work for us. So it's this sort of future, sort of future proofing our JavaScript. Then what kind of data are we sending to our Python backend? And we said send the text. And the text is coming from request postdoc get text. And the text value is going to be whatever our post was. Upon success, we're going to return HTML data, which is down here. Render HTML data. We're actually going to render the includes post.html. And we're going to throw in some contexts post is post and show detailing qs equal to true. And when this is success, successful, JS modal is going to be then hidden. The posts container, which comes from our homepage here, post container. We're going to prepend that data, HTML, whatever comes back from Django, whatever comes back from in here we're going to prepend that's, we'll put it to the top of our list. Then we're gonna say that same button that we collect disabled is going to be false. So it's clickable again and we're just gonna say New Post. And then we're gonna take that JS post-tax that text area and we're going to empty it so that someone can make another post right away and it doesn't look like they're old data still lingering around. Upon error. We're going to console warn that error. And we're gonna say that that button that we click is going to be disabled. Nope, it's not going to be disabled anymore. And that text is just simply going to say error. And that's all we need to do. Now if you're not super familiar with jQuery or JavaScript, that's ok. You can always just download the source code and have some fun with this. You don't have to necessarily know all of this code, but feel free to explore and do whatever you like. And honestly you can totally break this code and just read downloaded later and it will work for you. 36. Profile detail view: Alright, let's go ahead and add a profile detail view. So first things first we need to open up our profiles, profile URLs. Oh, look at that. We don't have that. So let's go into our profile app and create a new file called URLs dot py. And in here we need to from Django dot URLs, import path, and from, from dot import views. Because we're going to be using that in just a second. We want to give us an app name of profiles. This is going to help us with namespace and down the road. Url patterns is going to be a path. And this is going to send the user to my website.com slash profile slash. Let's take a string and that's going to be the username. That's what we're going to call it, is username. And when we use it in our view. And let's make this render views dot profile, detail view which we don't have yet. But that's okay. We'll make that in just a moment. And the name is going to be detail. And notice how we have this recurring named pattern detail here we've got post detail as well. But our posts is called Feed detail and our profiles is going to be called profiles detail. Alright, let's save that and watched Django complained that there isn't a file in here called profile detail view. Actually, yeah, it's not going to complain because it doesn't know about it yet. So let's go into our main URLs file here. And let's do from profiles import URLs as profiles URLs. And let's basically copy over our path here. And we're going to use profiles, URLs, and that namespace is going to be profiles. Now what we can do instead is we can say profile with a slash. Come up here and get rid of this. And that's not going to be required because it's going to prefix it with profile anyways. And then it's going to be your website.com slash, profile slash, and then the username of that person. So now we can see that Django has recognized that it's complaining that there is no profile detail view. So let's go over to our views. And we need to create this profile detail view. So let's get rid of this because we're going to be using class based view. So we can do from Django.com trip dot auth dot models import user. We've done this before from Django dot views, dot generic, import a detail view. And then we need to create a class here called profile detail view because that's what the URL was looking for in our URL patterns. And this is going to be a detailed view. Now we're gonna say HTTP method names is equal to get. I just like putting that in there knowing that if someone's going to read this, that they're not supposed to be accepting any sort of post or put or delete or any sort of view. It's literally just a get request. The template name is going to be Profiles slash detailed dot HTML. And our model is going to be a user. Then we have our context object name. I tend to always set this so it's not just object in our template because in our template it would look like this object dot username. Instead, what I like to do is set this as something a little more useful. It's going to call this user. And in our template we could use user dot username instead. Just make it a little bit more sense when you're reading the code. Lastly, we need a slug field is equal to the username. And the slug URL keyword argument acquired is going to be username. And so that's slug field is how are we going to look up this user by what? Slog? And we're gonna say look, look up the user by the username. And that's led URL keyword arg, the quark is going to come from right here, username. So we're saying this as accepts a string and it's going to be a username. And that's going to be our keyword argument. Then that keyword argument can be passed here. We can find a user that way. Now we don't have to write to all sorts of ORM queries, object relational management queries. We don't have to look for a user manually. This is just going to do it for us. So let's open up post.html. And that's, we don't need that anymore, whereas our To Do, we need to create a new URL in here. So open up our syntax. Url profiles was the namespace we use. So we're going to use that in a string. We use profiles detail. And this accepts what as its parameter, a string. So then we can simply put post dot author dot username. And that's going to act as our string to go ahead and save this. And let's refresh our page and see if there's any errors or complaints. And this is going to go to localhost port 8 thousand and see this at the bottom left, slash, profile slash. Caleb, now if we go here, we're going to see that the template doesn't exist. But this is good enough. This works for us. In the next lesson, let's go ahead and create a new profile template. 37. Profile detail template: Okay, in the last lesson we created a profile detail view. And we said use a template called Profiles slash details. So if we go to profiles, let's create a new folder in here called templates slash profiles. That's going to be our name spacing for our template and then detailed dot HTML. And let's just call this detail view in here just to make sure that this is working, that, that there isn't any typos. And so let's go back here. And if I click on my animated face, it says profiles, detailed dot HTML does not exist. So one of two things happened here. Either I have a template and not a template, but I have a typo somewhere. Let's double-check that profiles. Yeah. Slash detail and that looks okay. The second thing is Django might just not know about it. It might need to be restarted. Let's try this. And in fact, Django just needed to be restarted at, that's all it was. So let's go ahead and start editing this. We're gonna say this extends from base dot HTML. Block title is going to be user. And I'm getting that from our context. Object name, user. That username. And block body is going to have some stuff in here. Let's just make sure that this works as well. Cuckoo, This is all working and have some stuff in here, says Caleb up there. This is looking good. I have a another component. I want to use a tailwind component, and it's this one here. So let's go ahead and look at the source code. And yeah, this is what I want. So let's go ahead and select all that and copy it. And I'm gonna paste that in here. And cool, we have a profile name. So let's go ahead and, and start editing as some of the stuff we can throw in an image. We can put in a name or a username, number of posts, number of followers. And we can get rid of that. And we're not going to do too much with this. We're not gonna extend this all the way, but if you wanted to, you could add like a background photo and let users change their photos and things like that. So instead of a Livia Dunham, let's say user dot, username. Let's get rid of that one. Cool, cool. Let's say how many posts does this user have? X, X, and let's add a to-do. Add total posts. And let's look for followers which we haven't created any followers yet, but we're going to in just a little bit to do add total followers. Good enough. Now we need to go and add that image in there. And what we can do is we can reference another homepage, butter post.html. So we know that we need to load our thumbnail and we know that it's going to look like this. So I'm honestly just gonna copy this. Load the thumbnail in there. And where is that image? That image is going to be right here. And I just pasted that from my clipboard. And so this isn't going to be Post.all authored a profile image. This is going to be user dot profile, dot image and isn't looking for 372 by 372 Actually, that's not right. That's supposed to be 373 by three 73. You might as well fix that while we're at it. And how big is this image, we don't actually know. So let's say, let's make it a 200 by 200. Let's see if that works. And it doesn't move the original source image up and simply replace the URL. And I just deleted the old one, the one that I originally pasted in to IM dot URL. Okay, not bad, not bad. My face is a little bit there. Let's try a 100 by a 100. That's better. And so now when someone makes a post like this gene, actually see their profile. Now again, you can extend this as much as you like. You can add a new background image and there you can let them change your background image, all sorts of stuff. The only other thing that we're really going to be doing in this particular course is we're going to be adding number of posts, number of followers, and then be able to actually follow and unfollow people. So next up, let's add the total number of posts to this. 38. Total posts: Alright, so if we just take a look here at our profile page, it says Ex Post and Ex followers. What I'm going to do is I'm going to show you how to add X posts. And I'm gonna leave a to-do in here so that you can add your own number of followers. So let's go ahead and open up our editor. And where it says x posts, we need to put total number of posts in there. So let's just do this. Total posts, we're going set ourselves up for success. We're gonna write total posts in there, but now we need to add this in because if we refresh, it doesn't say anything. So what we can do is in our view, in our profiles slash views dot py file. And we can say def, getContext data. Self-fund any sort of keyword args at a might take. Let's grab the context. So we're gonna say super dot getContext data, passing in any sort of quarks that might've been passed into us and return context now to get something we can say or to not get something, but to add something to our template, to add some new contexts or a template we can say context. And what did we call this? We call it total posts. Total posts is equal to, Wow, hello. And that's it. That's all we're gonna do. Wait for Django to restart. Flip over, and it's going to say, Wow, hello posts. Now that's working. That's getting something from the backend, but not really dynamic. Let's make it dynamic by glutton, by collecting the number of posts. So we need to import this from Fi dot models, import post. And then we can say post dot objects, dot filter by the author. The author is going to be whoever this particular user is. Dot count. No, we don't know who this user is because the user is an unassigned variable in this method right now. So let's go ahead and create that user variable That's simply going to come from self.age, get object. And so if I can spell that right, get object is going to get this user object. So it's going to look up, a user is going to look it up, it's username and it's going to match it with the username that's in the URL. If it finds a user, that users then going to be, for example, Caleb or test or you, or your teacher, or a chair if you wanted to, or some sort of meme account. And so what we're saying here is all the posts, all the post objects, filter them by the author, makes sure that author is whoever this current user is on his profile page, and then count all those posts. And let's refresh. And I have six posts. Is that correct? 123456. In fact, I do have six posts and so that is how we add that in there. Now, what I'm going to do is I'm going to let you figure out how to add followers in there. Now we don't have a followers app yet, so you don't have to do it right now, but I'm gonna add a to-do in here, total followers. And you're gonna have to figure out how to add the followers in there. I'm gonna add a to-do in here to add total followers already have that in there. So I'm gonna leave that for you. 39. Adding a follow button: Okay, let's get ready to start being able to follow people. So what we wanna do here is we need some sort of follow button right now there's no action. You can see someone's account, but you can't actually follow them. So let's go ahead and add a follow button. So right underneath where it says Caleb, or whatever the username is, your username is going to be different. I'm going to add a little button. And so I open up profiles detail. And I look for my username in here. And I'm gonna paste in a button. And this button, we've actually seen this button before. This button I'm going to show you. Looks a lot like that button, doesn't it? So all I did was just copy that. And what I wanna do here is make sure that I have some JavaScript attached to it. So the type is not going to be Submit, the type is simply going to be a button. And in fact, it doesn't need to be a button. It could be a URL or not a URL, but an, a tag as well. So an anchor tag if you wanted to use a button. And I'm gonna say this is called the JS follow. And we need to eventually figure out who this is going to follow. So data, the username is equal to user dot username. And this is going to allow our JavaScript to figure out who this user is. And we could use a user ID if we wanted to or use a name. It doesn't really matter, just some unique way of looking up the user. And I'm going to set this text in here so JS follow text. And again, we're just setting ourselves up for future JavaScript well selectors. So we can select this particular node or we can select this particular node and we can make sure that something actually happens. Let's go ahead and let's make sure that this doesn't show up. The user's not logged in. So if request user is authenticated, then they can see this button. Otherwise, let's say class of margin, top three, note I did that wrong. Dot m t dash three. Login to follow this user. Else and end that. There we go. Okay, it looks like it did nothing. That's good. Let's open up new private browsing login to follow this user that text is a little too big. Can we make that text smaller? Text? Small? Yeah, that looks a little bit better. And because I'm logged in here, I can already do this now. I don't want to be able to follow myself. So let's wrap this in one more if statement as well. So what we can do here is if you are logged in, yep, great. You need to be able to check to see if you are comparing against yourself. So let's say if the request dot user is not the current user, this otherwise is not going to show anything at all. There we go. That doesn't work. Or rather it's not supposed to work, but it doesn't show up for me. I let's go and see test test account and see if I can follow test account. I can follow test account. Cool, cool, cool. But I'm not allowed to follow myself. That's good news. We don't want to be able to follow ourselves because then our features can be full of our own stuff. Anyways. 40. The follower app: Alright, we're talking about followers. We need to be able to follow people and track who's following who. So let's go ahead and create a new app called followers. So in my terminal here, I'm gonna type Python managed i pi, start app followers. And on my left you're gonna see a new folder called followers. Let's go into our Settings dot pi and our installed apps. And let's add followers in here somewhere. It doesn't really matter where at this point called followers, Django is gonna restart. Everything looks okay. Let's open up our followers and let's go to model.predict. And let's create our new model here. So let's call this a follower class follow or is a model's dot model, model, mongoose.model, model. Interesting way of saying that. And then we have Followed by is equal to models dot foreign, key, user. On delete. We haven't imported user yet, but that's okay. Models dot cascade. So whenever the Followed by user is deleted, we're going to delete this relationship. And let's give it a related name. Related name is going to be followed by because actually I'm going to comment that out. I'm going to show you why we need to do that in just a second. Then we need to see who that person is following is equal two models, dot foreign key. This is also going to be a user on delete is equal two models dot cascades. So when we delete this particular user, it's also going to delete this relationship. So if you delete the account that you're following or that person deletes their own account, then it's going to delete this relationship. Otherwise, if the person who is following you and deletes their account is also going to delete that relationship as well. So this is followed by two following relationship. Let's give this a string def string, so it has a string representation, return some sort of F String we can do self.view followed by DID whatever that user ID is, is following self.age, following dot id. And let's also give us a class meta. And let's say unique together is going to be followed by and following. So this is going to create a unique index. So you can see if someone is following you, if you're following them, and that there can only ever be one. So that you can't follow people over and over and over and over and over again. And let's just make sure that that is a tuple or a tuple. I don't know. Sometimes this tuple, sometimes it's tuple. Yeah. Okay. Name user is not defined. Let's head back on up from Django dot contract, dot auth dot models import user. And let's take a look at what this is saying here. Let's do this over. And it says, and this is why we are going to be adding related names in here. It says followed by clashes with reverse accessor for follower following. So these are basically conflicting in the land of Django magic. What we can do to avoid this, if you ever see something like that is we can simply give this a related name and we'll give this one a related name. And we'll just call it following and followers that are followed by and following. And that error goes away. Next up, we need to make migrations and also need to spell that properly. And then migrate. Cool. So we have that in there. Let's also register this with our admin. We've done this before. And so let's go into our feed. We're going to copy that admin. Followers go to our admin here, paste that in there. And instead of importing from models and getting post, we want to get follower. That's what we called this modelling here, follower. And let's just select all of these. Django should restart and there should be no problems. Now let's go back over to our admin and we're going to be able to see we have a new app in here called followers and there's no one following anyone. And I can select Caleb to follow Test. Save and add another. Let's try this. Caleb is gonna follow test again and save. And it says, follower with this followed by in following already exists. So only one can ever exist. And so that is a unique constraint on our database. And Django will enforce that for us. So at this point in time, you should have a follower Django application. 41. Following and unfollowing: All right, we need a way to follow and unfollow a user. So if we open up our app, I can follow this test account. But when I click it does nothing. So we need that to do something. We need some sort of interactivity. Then we need to Ajax that data to and from the servers. We need to send data to the server and accept data from the server and change the word follow two following or unfollow or anything like that. We might also want to change the number of followers in there. So I'm gonna leave that up to you because I haven't purposely done that. I'm going to leave that up to you. But if you wanted to in JavaScript, you can dynamically update that as well. And then once we're done with that, we're going to change our homepage to make sure that we're only showing posts that are relevant to us from people we are following. So let's go ahead and work on this. We have to open up profile detail dot HTML. And we have this JS follow. And so we wanted to write some JavaScript with that. So let's go into our front end, main.js, and let's add another event listener in here. So whenever you click that function, E dot prevent default. And that's just going to prevent a link from going anywhere or a button from submitting a form console.log clicked. And we just wanna make sure that this works. Cool. Clicked. And let's go ahead and collect some data as well. So we need to, what I can do is maybe split this. No, that's that's too crowded. Okay, let's split that. So in our detailed layout.html, whenever you click follow, There's a username and there should be some sort of action. So we can do data Action is equal to follow or unfollow. That's what it's going to be. Let's go ahead and get that username and let's get that action. And we also needed data URL somewhere to actually Ajax is data from door to rather. And so that's going to be URL. Profiles. Follow. And we're going to say user dot, username. Now this isn't going to exist. So when we load up our template, this is going to give us an error. Just like that, reverse for follow not found. And so like all Django things, we sort of need to do multiple things all at once. And so it's going to our profiles and it's going to our URLs. Let's just copy this and put follow at the end and profile detail view. Let's go follow view. Now that follow view doesn't exist. So let's go into our URL or our views nut or URLs. Unless you class follow view. And we don't want this to really be anything special. We just want this to be a regular view. But we also need to make sure that this is log in, that the user's login. So. Login required view and I don't believe that's in your yet. So where did we put that? Feed views? We scroll up here, we can just go over here. I'm literally just going to copy and paste that copy paste. And instead of detail view, we're going to add a view in here. And last but not least, Python doesn't like empty lines. Says login required view out nuts, because it's not a view, it's a mixin. There we go. Django is no longer complaining. So first things first, what we need to do is we need to make sure that a our template loads. Let's refresh this template still does not load. Wonder why it's not doing that? Because I didn't call it the right thing. I copied and pasted. That was a copy passed a problem. When you copy and paste, Don't do what I do. Always make sure you read through everything you copied and pasted. Ok, let's make sure this works. Let's inspect this and see what we have in here. We have a button with a data URL, a data actual data username. And we know that when we click it, it's going to be clicked. So now we need to do something. We need to collect some data here. So let's go back into, I'm going to close some of these into our main.js and where we have this ajax file. What I'm going to do is I'm going to copy this entirely. Boom. And we're going to write a very, very simple one year. We're not gonna do a lot of processing or JavaScript validation rather. So what kind of data do we want to send? We want to send, the action is going to be some sort of action. So because it's on this JS follow button, which is right there, it's a property on here. We can say this, this ATTR, Data action. And the reason I'm using Data action instead of dot data. And then just putting action in there. I'll show you that there's two different ways to write this. Data action is going to be cashed. This is not going to work for you if we change it in the future, which we are going to do. Whereas ATTR, attribute data action is not going to be cached and it's going to look it up every single time. So that's just a nice little way around that, that caching problem. We need to check a username. So username is going to be this dot data username, or we can use data here because it's not going to be a problem if it's cached, this username is never going to be changed. Whereas the action here will change from follow to unfollow, to follow to unfollow. But the username is never going to change. Username is going to be the same on every profile detail view. Last, we need the data URL, and that's going to come from Django. That's going to be rendered right inner template. And we're gonna make sure that JavaScript is dynamic. And so we are going to change this URL to this dot data URL. That's what I call it, right URL. Yep. And let's just make sure that there is a comma after that. So basically we're going to say, go to this URL wherever that is. And we could change that on the Django backend and JavaScript won't carry in the future. The action is going to be either follow or unfollow. The username is going to be whatever that username is on that page. And let's go ahead and get rid of this stuff because we don't need that. And we're just going to keep this nice and simple. This one's not going to return data, data HTML. This one's going to return just regular data in the form of JSON as we usually expect an, a, a, an ajax request. Okay, so let's save this. And let's just see what happens here. Let's click it. And we get a 405 method not allowed. Cool. So that means we can actually do something with this. That means that our endpoint is working. So let's go back to where is our reviews not in feed. We're not in front end profile views, HTTP method names. And we're gonna say that the only method name that's allowed here is post. Let's try this again. And this time we get a method not allowed as well. That's actually OK. I think what we're looking for here is, if I remember correctly, deaf post self request, args and quarks. And we just want to return something in here. So let's return a JSON response of w1 is equal to true. And we need to import that JSON response from Django dot HTTP import JSON response. Ok, let's try this out. Okay, it seems like it's doing everything that's turned on XHR here. Now look at that. We're getting to a hundreds now, this is good, and every time we click it we are getting a 200. So what we wanna do here is we wanna say data is equal to request and dot post-doc. Because I like working with dictionaries. Then we can say if action not in data or username not in data, return an HTTP response, bad request. I'm going to copy that so I don't have to type all that 300 times the feature. And I'm easily missing data, and that comes from HTTP as well. And so what this is going to say is if someone tries to access this URL, even if they are logged in, it's going to be looking for an action and a username. And so if they're missing that, they're going to get a bad response. Now let's go ahead and try to find this user. So let's do try. The other user. Is equal to user dot objects dot get, and then the username is going to be data username. And that's coming from our ajax requested data object is our ajax request, and this is the username on our user object. Is user imported. In fact, it is, that's good news. And that's except this. If the user does not exist. So if someone's trying to be malicious and they're being pretty good at being malicious. Let's say the user does not exist exception and return an HTTP response, bad request, and missing user. Or we could 404 or whatever you wanna do there. So now we can say if data Action is equal to follow, else, unfollow. And I like to leave little comments in here so that people know what I'm doing when they read the source code. And so the idea here is that you're supposed to follow someone. Let's, let's do a get or create. So we can say the follower and created because this isn't going to return. A tuple is equal to follower dot objects, dot, get or create. And we're gonna say, followed by is equal to their quest dot user. And following is the other user. And that other users coming from right up here. And I, I am personally making that request to follow someone. So I'm gonna say that that person is followed by me or the request that user. That's how I got that logic. It's a little bit confusing at first, but it makes more sense when you work with it a little bit here and there. Otherwise, we want to unfollow that person. So follower is equal to follower dot objects dot get followed by is equal to request dot user, and following is equal to the other user. So let's see if we can get that person. Let's try this though, because that following might not exist. So we need to accept that. And we can say, follower does not exist. Pass. Or we could say follower is equal to None. And then down here we can say, if there was a follower that was found, follower dot delete. Then in our JSON response we can say done is true or usually successes true or something like that. And then we can say, we want to return some wording. Wording is going to be unfollow. And this is going to be the wording that we put back into the DOM. So unfollow, if data Action is equal to follow and this is a ternary statement in Python. Else follow. So if someone is following someone, we're going to return the wording unfollow. Otherwise, if someone has unfollowing, someone, return the word, follow. And I'm going to show you what that looks like in here. We can do console.log data and we get an internal server error. Ok, let's go and see what kind of error we have. Followers not defined. Oh yeah, that makes sense. And we're gonna have to go and import that from followers dot models import follower. That's refresh. To go ahead and follow. And it says, success with true wording unfollow. Now, let's go into our followers in here. One is following two, I personally, Caleb, M1 test is two. We can see that in Caleb and test. Let's go ahead and delete this. Does just make sure that this is working. Let's click it again, refresh our admin. There it is. Now we need to switch that. So if someone is being followed, you need to unfollow them and if someone is not being followed or it's being unvalid, you need to be able to follow them again. So we go into our main dot js here and our button. We can say this 0.2x is going to be data dot wording. Let's go and see what this looks like now when I refresh this unfollow and oh, that actually changed to much of what I want. I still want that button and everything. I want JS follow text to change, but I want that SVG to stay in there. So let's not do this, but let's select this particular node. There we go, unfollowing. We also need to change this action and this action, where are you follow, needs to be unfollow. So what we can say here is, if the action is equal to follow, change wording to unfollow, else, the opposite. And so we could say this because we're not using functions in here where we're using arrow functions. So we're not using functions with the this keyword. We're actually tricking jQuery a little bit here. We can keep using this in reference to JS, follow this dot ATTR. Data action is going to be unfollow. Otherwise it's going to be. And let's just make sure that this works. So we have username test. Data actions must be follow and unfollowing here, let's go ahead and click it. And it's not doing what I expected to do. Why are you doing that? Let's debug this console. Dot log, debug. Unfollow. Follow. Action is not defined. And that's because I put it up here, turn those into a variable. There we go. Constant action is equal to whatever the action is actually is equal to action. And then we can say if that action is equal to follow yada, yada. Okay, let's see if this works now and that should work better. There we go. It changes from follow to unfollow. Now one last thing we need to do, and we're gonna do this in the next lesson, is we need to see if I'm currently following someone. I need to change that button and that default action from follow into unfollow. We're gonna do that in the next lesson. 42. Dynamically follow and unfollow: Already we need to see if someone is following someone or not. So if I do this, go over to followers. Cool, cool. So it does say I'm following what I refresh this page. It doesn't say that I'm following them. It wants me to follow them. It's prompting me to follow someone and this actually needs to be the opposite. And so what we need to do is we need to go into our use dot pi and our profile detail view. We need to add some context in here. So we need to say if request dot user_data is authenticated, the context of you follow is equal to follower dot objects, dot filter. And who am I following? This particular user, which we have set up here, users equal to self.age setObject. And followed by is equal to myself, request dot user. And then we just need to see if this exists. Does this exist? And this instead of saying filter and could possibly get multiple lines, it's just simply going to say it doesn't exist yes or no, and it's going to return true or false. Now here's the thing, request dot user does not exist. We don't have requests in get context data, so we need to add this into our dispatch. So deaf dispatch, self request, args, quarks, and return super dot dispatch request args and quarks. And all we wanna do here is a self.age request is equal to that request object. And now instead of saying request down here, we can say if self dot request, that user is authenticated and we can do the same thing here. If self-taught request user dot exists, say you follow. Now in our detail view, we can say due to, due to, due to due. We can change the wording in here. Give us some space to work inside of the span. If you follow, unfollow, else follow. And if and let's see if this works. They're real. Am I following this person? I am following this person. It now wants me to unfollow. We know there's one more thing we need to do in here is we need to change as action. And so we can say if following, nope, if you follow rather, let's just prefixes with the word on. And if so, it's either going to be followed or unfollow. That's unfollow and delete. And we see that the following was deleted. And if I go back and follow again and just refresh my admin, it shows up in one more example, unfollow, and it goes away. Just like that. We now have a following. We can follow multiple people. Now here's the thing is if I follow this person and go back here, it's still just shows up whatever posts are in here, we need to change, that. We need to actually go in there and modify whose posts we're seeing. And we're going to do that in the next lesson. 43. Dynamic HomePage Posts: All right, we are nearing the end here. So what we need to do is we need to customize our homepage feed. And so what we're going to do is we're going to open up our feed views. And we have this homepage, ListView. And there's a couple of different ways that we can do this. We can overwrite the query set here by writing def, get query set, self, doing a bunch of stuff in there, which is probably the proper way of doing it. But I wanted to take a slightly different approach. And I want to convert this from a ListView to a simple template view. And so let's go and change this ListView to a template view by importing that. And I don't think there's anything else on your other than okay, what I was looking for was another list view. I can delete this. Now, here's the thing, is, even if I delete this in, Django stops complaining, there's no more query set for us. There's no more model we have to go and actually also no more posts. So we actually have to go and delete these and add it to our context. So we can do deaf, getContext data. Self request, args and quarks. Context is equal to super and die get context data, request. Args. And quarks. Return that context. Now we can say context posts is equal to, I think that's what we called it. Let's say let's hope I made that too small. Their homepage, yep, we called it posts. So we need to add that to our contexts. Post.all objects, dot all. Get context data missing one required positional argument request. Oh, okay. We don't have request in our getContext data. That was a little bit of a brain fart I had there. Wait for it to reload. Okay, so I think these are actually in reverse order. These are in reverse order now. So we need to change that as well. And we need to see if someone has logged in to dynamically change these posts. So first of all, let's do this, let's delete that. And let's say if self-taught request user is authenticated, following is going to be a customized list. Otherwise, I'm not following that supposed to be. Post is going to be a customized list and post. Otherwise if someone has not logged in is simply going to be post dot objects, dot, dot order by ID. And let's show the first 30 like we originally did. No self dot request is not defined yet, so we have to do deaf dispatch. Self request. Args, quarks, return super dot dispatch, request. Args inquiry. And self dot request is going to be, and the question is equal to request. And that allows us to use self dot request in here. So now if someone is logged in and we don't know what those posts are supposed to be. We need to get a list of all of our followers. So let's say a list of who we are following. So following is equal to follower dot-dot-dot objects, dot filter. And the people who are followed by me, self.age request dot user. And this is going to give us a giant query set. Now we don't want a giant queries that we just want a list of these users. So we could do dot values, list following. We just wanted to list of all the people were following. And we want to make sure that this is flat and that's print that out. Print following. I'm going to comment this line out so it doesn't complain when I load the template. And let's reload that. She it'll show nothing. And by nothing, I mean, it's going to show an import error as expected. So let's do this from followers dot models import follower. Now let's reload this and we should, we should see that there are no posts in there. And down here we should see something. That query set of two. No, we don't want that to be a query set and it just says two in there. We want this to be a list, so we can type cast this as a list if we wanted to. And in fact, let's put this on multiple lines just because Python lets us do that. Now what we can say is Posts copy this whole thing. But instead of all, we can do filter. And we can say that author is in a list of following. And instead of getting 30, if you're logged in, let's go ahead and show 60. And let's go ahead and make sure that this works. And that did not work for me. And that's because I didn't throw posts into my context. Context. Posts is equal to posts. And that context, that post is coming from right there, right here. That's important. There's nothing to loop through if we don't add it to the context. There we go. Let's go ahead and click on that guy is name, unfollow, Go to home. There's nothing there. Now one more thing. If there is nothing there, what do we show? So let's say if this list is empty, so if not following, showed the default 30. Else, the posts are going to be a little bit more dynamic. Here we go. Default 30. Let's do this. There we go. And that work that shows everybody, everybody's post the last 30. Let's go ahead and follow. Well, I can't follow myself. Doesn't show up. It's not an option for me. But what I can do is I can follow this person, go back home, and it changes my list for me. So now we have a working application. We can get rid of this as well. It's not of any use. And so now we have a working application. There are some bits and bobs that I would like you to work on them and then tell you about those in your project in the next lesson. 44. Your project: Ok. Hello and welcome to your final project. In your final project, we, while this whole Django to u1 has been a large project in itself. But I left a few things open for interpretation. And so for instance, if I go to Caleb, it's going to show you that I have XX followers. You need to fill that in. How many followers do you have? Another thing I would like you to do is a little bit of front end work. So when you log in, you need to actually change these templates. So they, they look a little bit nicer because right now they don't look very nice, but that's completely up to you. The other thing I would like you to do is once you are logged in, I want you to add a new section in here that allows the user to update their firstName, lastName, their username, their password, and maybe upload an image. Now if you don't know where to get started with the image upload, we covered that in Django 101. So you might want to reference that one's more actually recovered all of that in Django 101, you can use a model form or you can use a regular form and manually update the user information. But either way, I would like you to create a new little account area here where someone can change your username, password, firstName, lastName, and their avatar. That's going to require a little bit of googling, a little bit of research, a little bit of reading the Django ducks. And this is all real life experience because once you have a job as a web developer, you are not going to be having your handheld anymore. And a client is gonna say, hey, I need a particular feature. And unfortunately you're going to have to go and learn how to figure out how to solve that feature. You might as well get that experience right now while you're learning. And if you really, really want to know what you could do is you could try to make this mobile responsive if you're more of a front end person with more front end knowledge than backend knowledge. You can always try to make this a responsive page because right now, I don't believe this is actually to responsive if responsive at all. So it doesn't look good on mobile, but you might want to go ahead and make it look it on mobile. When you've done that, don't forget to share your project with the rest of the class. Especially me. I want to see what you're working on. I want to see how it turns out for you. But most of all have some fun with this. And don't forget to push yourself. It's important to push yourself because that's how you grow. And don't forget to share your final project and your final outcome with the rest of the class and especially me, I really like seeing everybody's work. Show it down below. I would love to take a look.