Hello and welcome to a walk through for Django for everybody. So the walk through that we're going to be doing is the walk through for the my articles, which is really focusing on the notion of owning rows in a model. And what I've got going here is I'm logged in twice. I'm logged in one with a user ID of 1 and the other is a user ID of 2. So let's just do a quick demonstration of what really owning rows in a model is. So here I'm logged in as my first user and now I'm logged, I'm using a separate browser because this is Safari and this is Firefox. You can't just do this with tabs because the cookies are shared between tabs. And so you can login and logout, I want to just be logged in simultaneously okay? And so, not to these tabs, refresh. So you'll notice that the Edit and Delete buttons show up differently for different users. And that's because this row in the table lawn mower belongs to this user, user number 2 and this row these two rows belong to this user. So if I take a look at Administration and I go down into groups, not groups, not groups, users. I can kind of hover figure out that C7 is 2, you can kind of see the bottom there. And dj3 is user 1 and C7 is user number 2. So those are the primary keys in Django's user table. And now if I go and I take a look at these articles, you will see, and this looks a lot like if you were looking at say your Autos. Here's the rows or the models in articles. I look at lawn mower you see that there is a title, some text and the owner is a drop-down and that's because this is a one-to-many field. This is a one-to-many field between the articles and the users. And then if I switch to a different, so that's C7 that owns the lawn mower. I must be unlogged in as dj3 here. No, here, that's my name on dj3. And if I switch to Trombone will see that that one belongs to dj3. Now, I can sit and edit this in admin, right? But that's not the point, the point is to build software. If you'll notice when I create a new article, it doesn't ask me what user it was, right? So this is where you would say Dodge or whatever when you were doing cars. So this is like a race car, slightly used. Neon. So now I have another one, right? And so the key thing was is it looks like a create from things we've done before. But look at my articles, here's this. But somehow magically this owner field is populated. And that's the essence of owned rows as we've gotten a column and we these are marked and we know in all our code. We know the difference for example between things we own and things we don't own, the things we own we don't own. So if I hit refresh over here, there's a slightly used race car, but, [COUGH] Slightly used race car, but I can't edit it, right? So that's the ownership. Now, I don't know where to start. There's a lot to cover here. Let me show you. Okay, I'll just start with we're I always start. Let's start with running this locally. I know if that bug is there, that's not a bug. So this is just looks kind of like your Autos. If you look at it, it looks pretty much like the Autos, I might add a mileage of price in there or something. The only thing that's really different at all is this, there's a foreign key. Now, in your Autos yet a foreign key to makes. And the key difference, the only thing that's new is this idea of where the foreign key is pointing at. Now, the problem is and Django there is actually a table that all these users live in, and I can probably come over here and type of .tables, and I can see it, I think it's probably here. So let's just try saying select * from auth_user, so we should see a bunch of rows. And so they're my users, C7 is kind of all jammed together and dj3, so this is row 1 for dj3 and C7. So there is a table and a model, the difference is is we're not allowed to know that this is the user model in the off application. And so what happens is we have to let Django tell us what the name of that model is. And so we go into Settings, which is settings.py actually. And we're going to pull in the settings.py, and the default is off user model, and that's the name of the model. And so this is a foreign key to a place that we don't exactly know but Django will tell us and put in a string here. If we were to print this out we would see what that is. That's just a string but it's not a string we messed with. I'm going to delete CASCADE, that's just normal, the kind of parent row, the one row is in users. And if you delete a user that means that all of the rows that belong to that user will get deleted because on delete CASCADE. I'm choosing that as the as the model. And so you see created at if we go into that back into the you see all of these things. Now, created it, admin doesn't show, it's pretty smart knowing about created_at update. So it doesn't bother the doing that and it actually up taste them automatically so it knows something about created_at and updated_at in admin, okay? So that is our model. If we were to take a look at My Arts article, you will see that there is a foreign key right there. There's a foreign key and if we say select * from myarts_article, we will see that there is a foreign key in there. So this row belongs to user 1, this row belongs to user 1 that row belong to user 2, and that row belongs to user 1, which is perfectly reflected here. So now I am user 1 so I can edit three of them. Now, I am here with user 2 and I can edit one of them. So I'm editing the ones where I own it. So ultimately we're going to write a bunch of code so that these numbers are properly updated. So when you add add the article it works the way you want it to work, okay? So let's just take a look at url.py, it's pretty boring. This looks just like Autos. I probably hoisted up success URL here, put that in the as view as a parameter. And so that makes my views even simpler. Myartsall points to that view. I think it actually is kind of prettier to put it in here because this is sort of a URL place. And so it's not in my view. So that makes my views really succinct. And so it's like what you did once you got your Autos down to like just using the generics. And so I have a list a detail of create an update and delete. And I have a model called article, which I just showed you. And I am going to reuse some general purpose views for owner kinds of rows. Now, these are not from Django, these are from me, I made them, and now I'm using them. So ArticleListView extends OwnerListView. And then I can put all my things in there. The success URL actually, I don't need that here, but for create view, I do need a success URL. But for create view that success URL comes right from here. So I could have put it either here or I go to put it here, for me and just kind of after a while I think this is prettier because now I can look all the names of my arts, it's all there, so that's kind of nice. Okay, so here's views. We're going to come back to this, all the the brains of this operation are in OwnerListView, OwnerDetailView, CreateView, UpdateView and we'll look at that last. So that's where the hardest part is. But let's first look at the template. And so here's the template, this looks a lot like your Autos. The only real difference here is that this Edit and Delete button is inside of an if statement. So it's article_update, article.id, arguably, article_delete, article.id, so that's the same you've done that, right? But iit says is if article.owner is equal to the current logged in user. Article.owner is one of the article lists and that's just one of the fields in the model, which happens to be a number but you're given this user for free, right? It just hands us user and so we can compare it. So if this is one that we own, we show this link and if it's not one that we own, we don't show the link, right? So we don't show the link for the ones that we own. Now, I hope by now you realize that we have to also protect, right? So this is number 4, right? And if I click edit number 5, but what if I just change this to 4? We still just not putting up the link because it does not mean that we can't have some go to that link. So I'm cheating by putting in this link but this is going to blow up, right? And so we'll show you how, just because you don't put the link up doesn't mean that people can't go to the link. They can guess what the link is. I just did that, I went to an article I could update. I noticed the primary key of 1, I could not update to be 4. And then I went to 5 and then I just edit it, changed it to for 4 and I attempted to update 4. So it's not good enough just to not show the link, that's actually not security, that just is prettiness usability. Don't you all linked it, it's going to blow up if you click on it, so it's not really protecting us. This if statement is not really protecting us from clicking on or attempting to go edit or delete. It's just not showing it if it's not going to work. Okay, and again article.owner is just a column in this model and user is the current logged in user, okay? [SOUND] Okay, so what we've seen before is I would create a view and then use it in my url.py and they will extend another view. So now it's time to take a look at where this view is being defined. And so here's our view, generic view, the generic ListView, DeleteView, these are the generic ones. And all I'm really doing here is I'm creating a class called OwnerListView that extends ListView. Now, it pulls in everything that list view is. And well, notice I've added nothing to it and I really could because the ListView doesn't really care. It's not really doing anything other than the fact that that value is in the models, so it's going to come in, right? So I could have made this be ListView, but I just wanted to be pretty, I want to make it so that I could just say OwnerListView, OwnerDetailView. And so you'll notice that I'm not making any changes at the moment of inheritance. I'm not changing owner, so OwnerListView is exactly the same as ListView and OwnerDetail is exactly the same as detail. I just did it to make it pretty in case later on maybe I want to add some. But now we get to OwnerCreateView, OwnerCreateView is something that matters a lot. So there's two things we've got to do for OwnerCreateView. So let's go ahead and start to add an article. So the first thing is is there is no field here for which user it is. It would be kind of silly and I can actually mess this up. So if I go to CreateView here, and I add user.r And if I hit Refresh on the CreateView, come on. Back up, it's not user, [LAUGH] owner. So if I add it to that list and part of this what's going on here is there is both a model and a form. This fields equals is informing the form, and the form is the user interface. So look at this, I'm allowing the user to specify it and now they can set the owner pretty convenient. Well, that's not what we want. So that's why in this create view, you say don't let him set the owner. I don't want them to see the owner because it takes a little while for this thing to restart. Okay, now it's restarted and I can hit refresh. So what is blowing up? It was just because I was coming here too fast. What is that? So go back to the views. Maybe I'll have some tiny little bug, I'll dig through that bug later. It's working, it's probably passing the autograder, something pretty heavy. So remember that there are two sides to this one is the database side and that's what a model deals with and then there is a forum. So these views, this ListView if you recall once we tell it, Once we tell it the model it actually makes a form. So it is CreateView, it's making a form from article copying all the fields in article or the fields according to this little list. And so that's why we're doing it. So the key there's were explicitly not putting owner in there, and we're not putting created_at or updated at if I wanted to put created_at in here. Hey, let's show them created_at, okay? Let's refresh, refresh, refresh, okay? Okay. So again, it's trying to take a model, trying to go to from the article model to make a form and it's not an editable field. So it's mad at me, so I'll go back and fix it, I'm sorry. Okay, you get the idea, that uses this list of fields to make a form. The same for article and for update. The update and create are so equivalent that the template for them is shared. It's basically, we'll talk about crispy tags separately in a different little video. The form is the same, the difference is when the update comes there is already data in it. That's the only difference between a create and an update. It looks exactly the same but update has data in it, a create has no data in it. So the code between create and update is super equivalent, very very similar and the template is similar as well. So there we go. So we got CreateView, DeleteView, etc. Now, I'm going to focus on CreateView. And the key to CreateView is now in owner.py, here's our form, that's pretty straightforward. In owner.py this CreateView now has a little bit more to it. ListView and the DetailView just inherit it. So the first thing is that CreateView has a LoginRequiredMixin. And that's because if you're going to create it, you need to be logged in user, but to view it you don't actually have to be a logged in user. Or if I was to okay, let me log out here. So now I'm not logged in as anybody and so I can see all these, I can't edit any of them, I could log in if I want and the detail view also works. So I'm completely logged out here, but I add article, so add article is protected by login. So OwnerCreateView, LoginRequiredMixin. And I do this because I don't want to trust the user who's writing this view to put the LoginRequiredMixin here. Because I am writing code right here that depends on the fact that this person is logged in. So this is like a guardian pattern, I'm putting this in here, so I don't end up with trace backs inside my code. If I didn't put LoginRequiredMixin in order to CreateView then and they forgot to user.py, they forgot to put it in an article CreateView. Then this code would blow up, it would trace back at when I tried to set object owner to self.request.user. It would just blow up. So I'm extending CreateView, which is inheriting all the functionality and I'm only going to override one method, this object orientation. And you got to go read all the documentation, this documentation right here. Show that documentation. So this documentation is hard to find and I'm sure I helped myself by doing some stack overflows. So that's why I put little notes at the bottom of the file, things that took me a long time to find. So this FormModelMixin and that is what they're really doing is they're giving us for these editing fields, like the update. They're saying these are the functions that are in this and I can override them. So the one that I'm overriding is the form_valid and what that does is it basically says here's your form save it to the database and set the current object for view and then redirect to get_success_url. That's its job, except that right before it's going to save the form instance, I want to do something myself. So what I'm doing here is I'm saying in the form is what's being called is the parameter which is the form that has been pulled in from the post data and handed to me. And so, what I'm going to do is I'm going to I'm going to say save it but don't save it to the disk. That's what commit equals false means don't save it to the database just save it into this variable object. And then I'm going to set the object owner. I'm simply going to copy the current logged in user into owner and then I'm going to set it to the database. And then what I'm going to do is I'm going to call the original form_valid, passing form in is a parameter, it's in this class. And that's really not going to do much because it knows that it's already been saved, so it's not going to save it again. And then what is going to do is it just going to do the redirect to the success_url. So this is rather late in the process of CreateView. It's been validated, it's check for bad data, etc, etc, etc. So this is like right before the redirect happens. And so, at a high level my whole goal was to pause the saving of this data to the database and sneak the logged in user into the owner column and then be done with it. And so, that's the basic overriding. I'm really extending form_valid to cause my code to be called right before the default code is called. So that's OwnerCreateView and that's how once I add an article and put some stuff in and I submit. Right as I submit it and right before it saved, there is a little field called owner that's going to be updated and it's there and so it's the owner field came from this code. Now, I could have put this kind of put this in this view.py but no, I got this really awesome creatable thing, I can write really cool code. I'm going to make hundreds of views that we will through the rest of the semester do this over and over and over again. We're never going to change this file again. So now let's talk about edit. So there's two things, so here we're going to load article 6 and then we're going to put out a form that has all this stuff. So the first thing that's got to happen in OwnerUpdateView is we got to check to see if this belongs to the current logged in user. Remember when I tried to go to whatever it was four here and it blew up? This is the moment where I'm going to protect it. So if we take a look at the moment where it's loading the model, have a link to that, django-populate, let's go to stack overflow here. This one got one stack overflow. It's been ten years, probably a good one. Middleware, somewhere in here. I should make that link be a better link. I like this one, I'm going to put a link just to this one here. Went straight to that. It's telling us what to do. You see I just borrowed this right there form_valid. I'm wait, no, that was the create. Sorry, sorry, sorry. Can't find quickly where I can do this, but queryset. So queryset was called when it's loading ID number four,so it's going to do a load. So queryset is not the actual load but it is the query to the database that we're going to do. So what I'm going to do is I'm going to tweak the queryset and this queryset is probably going to say show me Where ID equals 4, all right, it's going to say something like, select from articles where ID equals 4. And if I just did the normal query set, it would work, but I'm going to change the query set. I'm going to change the query set. So what I'm going to do is I'm going to call the default get _queryset and the super class, which is the delete view class. And that's going to say query where the primary key equals 4. In this particular case, but then what I'm going to do is I'm going to add another filter. This is like and, and owner equals the current logged in user. So so number 4 is owns by user 2 but a logged in user is one. So this is going to be and logged in as user 1 so when it does the query says give me the thing that side equals 4 and user ID equals 1. It's going to get no records. And so as the delete code for the update my looking at delete update and delete all the same here. Before it does an update. It has to load the old data and I have made it, so that if you're not loading data on something that you own and it's going to give you a 404. So it's like that wasn't found now for does exist. It just doesn't exist for the current logged in user, but five does and it does belong to me, right. Now, the other thing is, is when it's going to call get_quertset again, new stuff, x y z 1 2 3, so when you submit it, it actually is going to call get-queryset one more time. Before it actually stores the data and so let me show you this will be kind of tricky. So I'm going to try to mess with it on 4, try to change 4, so I'm going to go into edit screen and I'm going to inspect element and I am going to somewhere in here, find. That didn't where's ahead? Input type hidden, so that is the input type equals had no, that's the middle or token. That's not the one I want change. All this is going to where's the It's because it's in this URL. I don't think if I change this to for it's actually going to work. Let's try this by not going to work. Okay, but the idea is eventually someone could mess with that post. So you still gotta protect it, right when that data is being modified. It actually does it runs this code again and it reads the thing and if it can't read it, then it won't let you update it. And so that protects you from people breaking in and attacking there and the owner delete view is the same thing. So if you go into delete it does a get_queryset and I add this filter to say and it has to belong to the right person. So if I try to delete article 4 boom, it says I can't load article 4 but if I come over here and I'll get a log back in and so this one I think was seaside. So now I can delete number four here, because it is crappy, but there is he's deleting number four and away I go. Okay, a lot of stuff a lot of stuff. Okay, so here's the thing. We're going to use owner.py over and over and over again. We're going to write simple and elegant views and all we have to do ultimately, any model that we want them to have this owner feature with. We had an owner column to it and we have the views extend an owner view and then everything's taken care of after that point. So it's really, we're not going to change line of this, it's really cool. We're going to use in project after project and table after table after table, right. Because this can apply to different models is just this one happens to be applying to article model. So I built a super generic owner capability, that I can use over and over and and just take your time watch this more than once review object orientation, figure this out. It's hard for me when I'm teaching to explain, why object orientation is cool. Object orientation is cool, because it can let you write the super elegant and very reliable, the fewer lines of code you write, the more reliable it is. But the harder it is to understand because the magic is here in this object going back. And again, it took me a long time to write this stuff. But now, it's really gorgeous and really reusable. So keep thinking about object orientation. We're going to be doing a lot, going forward. In terms of object orientation, okay, so I hope this helps, cheers.