DeleteView Example - Building SaaS with Python and Django #99

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hi welcome to building sas with python django my name is matt lehmann and on this stream we work on django apps and tonight we're going to continue on the homeschool application and work on an ability to delete something that's per a customer request so we're going to work through delete views and class based views and all that kind of good stuff so hopefully if that's interesting to you you'll stick around and check it out if you want to stay in touch with me offline i have well offline in the sense of not on twitch i have stuff that you can follow me on twitter as mb layman i have also have a channel on youtube so you can find uh past recordings or maybe you're catching this on youtube now um if you are please like subscribe comment that does help uh help me out as and i'm happy to engage with people i try and respond to all the comments that i can um i also want to say thank you to my patreon patrons i have a bunch of patrons on patreon and they help me cover costs for like podcast hosting and other things that i try and do for the community and i'm really grateful for their support if you would like to join them you can go to patreon.com mblamen and that's where you can support me so uh the patrons that i do want to thank are rupert andrew patrick eric phillip and miguel thank you very much for helping out so let's uh get into the project for today i have a customer who is in the trialling phase and she is pointing out a feature that she needs she is poking around as a very active user on the site and um has this ability or desire to delete courses it's something i didn't add into the product initially because i could do it from the django admin console and initially my my spouse was the only user of this homeschool app that i've built so when she had to delete a course i could do it for her and i did that to get something out there quicker and to be able to build up a school year and if you're joining me for the first time this is a reminder that i'm building a homeschool application to help my family and ultimately other families use this as well but now i'm at this point where i need to let users delete courses because that's going to be an important element of their process and i don't want to have support requests anytime anybody wants to delete a course that would be silly what we have to be careful when doing this is that the way i've modeled a lot of the system is there are a lot of foreign key relationships to a course and if someone wants to delete a course it is going to have a bunch of other impacts it will delete tasks and graded work and grades and all these other things and we need to make sure that in the scheme that we follow that they can't or are at least aware of what else they're going to be deleting so that's the plan i have think there are going to be some other gotchu's in there of certain relationships that we have to follow and delete and other constraints that we need to add as well and we'll talk through those as we get there so because this is about django app development it's not you're not necessarily following this to see me build a homeschool application although maybe some of you are you're free to ask me anything about python django or web development and i'm happy to answer those as best i can all right so with that out of the way let's jump into the editor and see where we need to go actually maybe before we do that let's check out the an example of what we're trying to delete and i'm going to show you the admins point of view is the the admin has an excellent feature that actually does exactly the same kind of warning that i want to display to the customer and we'll see all of the pieces of data that would be deleted if someone tries to delete a course in the system and things that we need to take into account and report upon so that the user is at least fully aware of everything that they're about to do because the other thing i'm doing with with the system is i'm not doing um what's called a soft delete so you might have heard of this maybe you haven't before but a soft delete is this idea where you have an extra column or attribute in your database table maybe some maybe something like active or or something to that effect and if it's true then it's like it's a real object but with a soft delete you'd set that to false and so you'd essentially filter out anything that's been deleted um it's a way to keep data around but i'm really not interested in doing that and um i just you know i think from a privacy perspective like i really want to probably make it so that somebody wants to delete their account i'm going to delete their account and all their stuff and and that has implications for the product and it it means that certain actions are are more severe than others and so people need to be warned appropriately so um we'll uh we'll make sure that they get the the big scary warning well i don't want it to be scary just informative and make sure they're aware of what they're about to do so let's take a look at the application and we're going to go to the admin to start and i have some courses in the already in the system for one of my school years and oh boy which one is it going to be there is a a math course that we can probably use as a good example um but i have a number of those apparently so let's go find out which one i need to actually refer to so this math2 course that's in my test data is representative i'm not actually going to delete it but you'll see what it will show so we want to find in here there should be a math2 course here it is so i'm going to hit the delete button and the django admin page is actually going to take me to a second second page and it's going to show me everything that's actually connected to this course and because of all of these foreign key relationships and because these fields are not nullable that means that when i delete this course it has to to keep the database consistency delete the other things as well so that means that there is from a from a user's perspective they are saying i want to delete the course but they not may not realize oh i'm actually going to be deleting all of those tasks i'm going to be deleting everything that's been marked as graded maybe they don't care about that one that might just be a that's part of what what a task is those these two things kind of pair together they need to be aware that they're going to delete grades for their student that's a big deal any progress that their student has made that is also a big deal so um these are the categories of work that we need to report on i probably will do it something like um at least giving people just counts of here's all of the things that are going to be affected by this and i think that would be a good enough starting point to give the the user awareness of you know if you do this this is this is what this means most of the time i suspect that if somebody wants to delete a course what they're really wanting to do is delete a course they've just made a mistake like they don't actually plan to use it for their school year there's not going to be anything to report and so there'll be no reason to warn on that so we can make it so each of these counts that we're going to display will only optionally appear if there's something there to to to bring to their attention there's also i'm trying to think if there's anything else that we need to consider in the model relationship let me bring up that diagram um we've used this quite a number of times and we'll bring it up again because it's a diagram of everything uh that comes out of the models in my system and if you actually if you're if you've never seen one of these before uh this is a really cool tool i this is not something i built myself this is a command that comes out of the django extensions so i've got it we can see it in in my make file what the the invocation looks like it's a management command and so the django extensions project um which i should have somewhere here i've even got it up open up to the exact command so this is django extensions and you can pip install it and add it to your project and it gives you all these extra features some of them are actual like functionality related like models you might want to derive from abstract models some of them are actual commands and extensions this is one of them it's called graph models and it requires graphics and a couple other things but the end result is that it makes a diagram so you give it all of the django apps that you want to be part of that diagram and it produces what you see here which shows all of the connections between all of your models and foreign key relationships and yeah it gets a little it can get a little messy but it does tell tell the story so where we are going to be is over here in this course model actually i don't know how well you can see that let's zoom in a little bit more so we're going to be deleting one of these and this is where you can see it spiders out into all these other things like course resources and of course actually this is one that didn't even come up in my math example but that's definitely another one that will be part of this um so course tasks and then where it fans out to all of these other bits um so what i what i also wanted to check is like do we need to make sure that some other constraint is in play so there's a there's a connection between a grade level and a course there's a kind of a many many many-to-many relationship here that's tracked in the special model table and that was one that showed up on the admin um as this thing with the through model and there's one of those i don't know that i mean this will be deleted i don't know that we need to report on it to the user i don't think they're that's something they certainly wouldn't care about and i'm just trying to think you know i don't think it means that even we need to delete the grade level either because the grade level could be connected to a bunch of courses so i'm not going to report on that and there's not some requirement that a grade level have at least one course so we don't need to report on that so it's really the stuff we need to report on as let me let me jot some notes in the in the editor here as comments so we need to get a report on course tasks and what else did i say um grades oops i know that's hard to see these comments so i'm not there's certain aspects of my dark theme that i'm still not wild about comments don't show very well so hopefully hopefully you can see that okay but maybe not so grades and coursework and course resources are one of the other ones that doesn't show with this math course but is part of the relationship diagram so those are the four things that i want to be able to warn users about um the actual like mechanics of deleting stuff that's really not a big challenge in this pro process we're going to use a class based view we're going to use a delete view and a delete view already has a lot of the stuff that you want if you do a get request it takes you it renders a page that is the warning page which which is where we will provide all the context for the numbers um and um i'll get to your question in just a second second leo resende 77 hope i didn't butcher your name too bad um and so then the the actual when you're using the delete view the post request is the thing that actually does the course deletion so we've already got like the two step mechanism in here um we just need to wire all that together okay let's get to this question here in the chat and how do i divide local and production environments that's a that's a good good one for all of my different environment management i use the same settings file so in my project i have a single settings file and i guess i actually have two settings files one settings file is for just for unit testing i'm actually doing automated testing you can see the way that this file is structured as it pulls in from the settings here and then override some stuff that i definitely never want to be on when i'm testing for example like i never want to send out email to my actual email service provider while i'm writing a test that would be bad so i hard coded there to make sure that i can't accidentally do that but for my regular like local development here on this machine and my production setup i'm using environment variables for everything so i use a project called um what's the package called let's see we can go take a look django and byron is the one that i use there there are a couple that do something similar um but this is this is one that i i know works and it gives you access to this environment uh class or i guess more accurately this instance which uh the documentation eventually calls conventionally calls env and once you have that then whenever you want to get access to your settings you call env with the environment variable name that you want to pull from so by doing this i can get all of my local development settings using environment variables i can get all of my production settings also using environment variables and it's just a matter of where you set them i can't actually show you my my full local emv right now because it has actual secrets in it that i don't want to go rotate a bunch of keys and things but what i can show you is there is an environment sample file that i keep or example file that i keep with my project so if i were to lose my settings and stuff this is a tracking of everything that i would need to kind of go back and recreate a production environment and you can see i've kind of got placeholders in here uh to show you know this is where your real secret data would go and this secret key just so everybody's clear like this is one i don't care that this is committed it doesn't really matter because this is not what i'm using in my actual production environment but django needs a secret key in order to be happy so you can see that this is the way this stuff is configured and anytime i want to go change uh you know something that is is going to vary on on production like for example the stripe keys for doing credit cards that's where i would go and in heroku use their config section and set the environment variables so hopefully that answers your question of how i divide local and production as to um your other question about does the django extension go into production uh yes yes it does it um we can see it's it's one of the installed applications that's here it it's required to make this work it's inquire required to make the management command commands available all the time even i mean i guess you could do some stuff to say like if you know if it's in debug include the installed app otherwise include it in installed apps otherwise leave it out but i don't and the part of the reason for that is the the the system also has other stuff that i might want to use for the actual application i mentioned there are some model extensions so um they're different kinds of well maybe this isn't the documentation i was thinking of but there are somewhere in here there are different um different items that you can use to uh actually be a part of your django application not just tools to help you but actual stuff and code that you want to run um so that's that's one reason why you'd want to keep it in the installed apps list all the time hopefully that answers your question all right so let's um we've got our list of things that we want to be able to display as something we want to delete let's talk about where we want to actually put the deletion code or the template stuff and then get into it so i want to restart this the local development server here and we'll go look at the area where these courses are defined and we've got a bunch that we can use for them as targets of things i can delete i've added a bunch of these as like just junk um when we get into an actual course i already have a pattern for this so it was important for my spouse to be able to delete tasks and not not bug me every well bug is not the right word not ask me every time she wanted to delete one could couldn't she do that and so i have um in this area uh anytime i'm in the edit page there is a delete button here uh oh great i'm glad i answered your question there is this delete button here that will take um take the user to a page and say do you want to delete this actually why not let's go to that page so we have this pattern of here is this thing your if this is the confirmation page and this is where we're going to put the same kind of messaging information to let them know that this is what you're about to delete so it'll say delete course and then it'll probably in this scenario i think we don't even need to have this description thing the description is just mostly because of the tasks but we could say delete course and then give the course name um instead of this task label and then we can say um you know this you're going by deleting this course you're going to also delete this and this and this or or the account this many of these and this many of these um and so that's what we'll put that information there we'll have the same kind of are you sure um we won't need this label because all of that will be in the the kind of the paragraph or bullet points that will go above and then there'll be a little delete button and we'll actually do the post so um it's a pretty bread and butter kind of feature like you're probably very familiar with delete functionality in lots of places but i just want you to be aware of the patterns that i've already have established in place that will guide us as we're doing some of this stuff okay so we i think have everything that i think we need in order to actually get started on this so let's um let's get to it i'm wondering is there already a delete view in place here i bet there is the course task yeah so that was this course task delete view is that page you were already seeing um and we can probably model some of this after that i'm curious why i don't see the template here there's probably already it's probably a conventional pattern it's been so long since i worked on this delete view i mean this is one of the early requested features from my spouse so um i honestly don't where i'm gonna have to remind myself a bunch of these things so i'm gonna what i'm gonna do to start is i'm going to take this chunk of code and we're going to go to where the course the course views are and i think i'm going to follow yeah all right in my convention for putting out views i'm kind of following crud in my layout in my views files so my first view here is a create view my next view is the detail view which corresponds to the r which is read you can see the detail view there there's the edit view following that which means that the natural place to complete the crud layout is at the bottom here as the um to complete the d in crud so um i think we've got that much um you know i don't know this is not definitely not the right query set right now i don't actually know that we need this next url parsing because i don't think there's only going to be a single place that we are going to delete courses [Music] and where we need to send them back is an interesting question probably back to the school year but we can get to that in a minute um so yeah trying to figure out what else we need and my editor is complaining because i've got a class with the same view name right now so we'll do this and we can then also do maybe this okay so that that's a view um and that's probably way more copying than i would like to do but it's a start and i think the other the next maybe the next step the next logical step anyway is to back this up with some tests so let's go into the test file where this would live excuse me and let's find where the model should the test case should go somewhere around here yep here's the edit view so we'll use this as the starting point because it has a lot of good tests in it already and we'll paste it right here and say change this test case name to delete view all right so we've got a starting point and we it has all of the attributes that i care about um in fact i feel like that edit view is actually missing a test it's missing the um do you have the right access level that bugs me we might need to go back and add that later so i try and check that only the people who are allowed to get into stuff can actually get into stuff and there should be a test for that i'm going to check this edit here to make sure i'm doing that properly okay i am at least doing this so that the test that i would add here should pass um oh good question cascade cascade is uh people have a love-hate relationship with cascade i think um i think if you get to a certain company size like a certain amount of data cascade can become like a pretty terrifying thing to people imagine if you if you don't anticipate the where your cascade is going to happen and you end up deleting like spidering out and deleting tons and tons and tons of data because of cascade i think that cascades are probably a really good way for a reasonably sized app to maintain data integrity because the other option what is the other option or i guess there are a handful so let's pause and take a look at that um django model cascade i'm sure that'll get us close [Music] so there's some other choices beyond cascade and come on there's got to be a listing here somewhere here there okay oh i don't know what this one is that's new in 3-1 what is that i'll have to read about that well maybe not maybe not on the stream but i'll i'm i'm about to talk to you about something that i'm not fully informed on that's interesting so cascade is the the behavior when you want a foreign key and you want that data to always be filled like it doesn't make sense for it to be not filled like in the case of my example here i have a course and a course can it has many tasks and so the foreign key [Music] is on the course task and it doesn't it doesn't make sense to [Music] leave a task around if the course is gone right because the task represents work that you're going to do for that course so it's a it's a perfectly rational case there so in that scenario cascade says if you delete the course go ahead and delete the tasks then you have other stuff which says you know make sure you uh set this up so that if you ever deleted something that you actually raised some an integrity error which is basically saying like i never intend to delete this thing and if i did if anything tried to delete it that would be bad and there should be an integrity error that will prevent a transaction from completing and really messing up your data again i don't really know what restrict is so i'm going to set that one aside because i just don't want to speak unintelligently to anything there there is set null is the other common one so you know your other real option with a foreign key is to set null equal true on it so that will allow in your database table to instead of having a foreign key allow a null entry there again if i had a course task that had no course that was like null it doesn't make any sense so this is uh some people will say like null is like better for um maybe some data safety of like you don't inadvertently delete stuff that you didn't intend to but it leads to another a different kind of problem which is like orphaned data and i don't know if that's the official like term that you might get out there but that's that's the the impact of it is like you could have data that's left around your database that nothing can really access or there or i mean you could access it but it may not be used in any scenario so uh the way that i've modeled my stuff i'm using cascade pretty heavily because like i said i if if somebody wanted to come and say hey delete my account and you know i'm going to bring gdpr the general data privacy the eu law about um data protection and say if you don't delete it i'm going to bring like a lawsuit against you well i don't want to be in that scenario where someone would have that ability to do that so i'm definitely going to make it so that it's easy to completely expunge someone from the database that's my plan anyway so hopefully that gives you an idea of why i use cascade um i do think that it has it has a lot of benefit but you have to be careful with it it has to like make sure you plan out that you've thought through all of the on-delete behavior of what's going to happen because if you don't you might get surprised about stuff okay um we've got uh we don't have that test so we might add the test for the delete view and then come back for this other view that um yeah well we'll will not get hung up on the edit view right now so let's start with uh this this one right here i want it to be so that just like any other action in the system um you can't you can't do a lot of things unless you are authenticated so this is a general protection i put on almost all of the the views with the natural of exception exceptions of like the login view and the policy the privacy policy in terms of service and other things that are meant to be for anybody but for most of the application for actual users um i wanted to be login protected so we've got a test here so the first thing i think we need to do is right now this test is pointing at edit and we need to change this so that we actually have the delete view wired up to a url route so let's go to the courses urls and connected i'm going to try and follow i'm sure there's already a delete in here with a certain naming so let's try and follow the same pattern so we've got path and it's called task delete we're already in the courses app so here's where we're going to put delete right here and we don't need the task uuid so i'm just going to call it like this and we're going to simplify it to delete because we're in the app the courses app name space already so the reverse of this will be delete courses delete sorry let's delete the word task from here that's going to point it to our new view that we have and that should be enough i think to get this test going in fact i think looking at this path right now uh actually let's try doing yeah um so black reformat fit in a single line which is what i was hoping for um where what we're saying is we're going to expect a uuid for the course and it's going to be at this delete route so in the actual application this is rooted at courses so this would be courses some you id slash delete is what that view will look like view route and it's wired up to the right the right view and it has the right name so i think i'm done with the urls file entirely let's change this to delete and since this is just testing the authentication part of this this test should actually pass now because it's we have a route and it has the right mixin class on it because i copied it from somewhere else so that test was really testing is this login required mix-in added to the class and it is now it's this next step that's going to fail um so if i do delete here again the delete a get does make sense because um we want to have that confirmation page so we're going to start with that and we'll do this and probably going to give either template error or no not even there yet because it's the query set problem so i'm going to go up to one of these earlier views and see if i have i don't looks like i didn't refactor this to look for a particular query set which is kind of lame as you can see the uh the query set that i use here it's it's kind of the same pattern the the way that i have to check if the user is allowed to look at this is i have to go to the grade levels that belong to this user for for this the school that they're in and then say does the grade level and the uuid is can i find a set that matches and that replicates over to here as well it's the same exact kind of code [Music] and the difference is that this one does some pre-fetching and i'm wondering is the create the create doesn't have one because it's not it doesn't have actually have to get a course yet it's actually the one that's doing the creation right so that doesn't make any sense to have that but i think that what we could do perhaps is make a little mix in or something that can ensure that that logic that i've been copying from place to place is not replicated because this is something that is going to be consistent all right so what um what would we call this i guess this would be the um it's like a course we want to get the course query set is that accurate i'm not sure it is all right let me just let me sketch it out what i think it might be because what i'm afraid of is like is this going to does the distinct order matter with the prefetch i don't think so but i'm not positive on that um the worst that going to happen is we break some tests and we have to go back and fix it so let's start with the detail we'll do the extraction we'll run the tests i mean we've got a pretty good set of tests here there should be there's 84 tests here they only take a little bit to run one of those is one of that the new test that was broken so i'm not surprised that there's a failure and let's do the refactoring and then and then see if we can apply it to our delete view so we can get something some re code reuse so i want to add well the first step is to do the refactoring is to create the method that i want so i want to say get course query set and it looks like wow i am consistent because look there's a get task course task query set so this is a pattern that i've clearly applied before in fact i want to go look at that one course task query set all right where's that thing defined all right well at least i am a consistent human being i suppose so this one is a general function that i used and probably used it as a function because there are other function based views so maybe rather than a mix-in we should actually check on this first to make sure that the courses i don't know if there are any courses that are using function-based views that could benefit from the same thing so actually let's give ourselves a little bit more room for a moment i don't actually need that test open so we've got the course detail view so for the moment i think i'm thinking of using this as the method name but let's see if we can do a function instead so we'll use the same thing in edit for sure we'll use the same thing in delete and i'm wondering if this is the course copy which isn't that's something different um man i forgot there was a copy ability in here that's fascinating um i wonder if that still works got code okay anyway uh it looks like it's only for uh class-based views so we'll just use the mixin approach for now and what i'm gonna do with this mix-in is take in fact i'm going to take all of this stuff and we're going to use it completely because the only difference that i'm going to want for the detail view anyway is to take the pre apply the prefetch later so we'll save that black will condense it down to something smaller so now we've got this get course query set and it is the functionality that i would expect so we'll see how this will work with the actual stuff so now if we say we've got the course the course query set we'll call it that for now and self get course query set and i see that there's some kind of raid going on i don't actually know i've not experienced many raids so welcome those of you that are raiding um i'm working on some a delete view for my django app here so that's what we're looking at and i'm doing some refactoring before i'm actually getting to the delete view because i've got some replicated logic and this is the third time that i'm i'm using this particular bit of logic so i don't know how much of it will uh i don't want to repeat it over and over and over again and so that's what we're working on cleaning up so now that we have this course query set i want to take this and i want to return the course query set but i still want to get the prefetch that was important for this particular delete or this particular detail view so i'm going to take that and paste it here and join those together do i um i use that's a good question i don't think that i use lsp and i say that like uncertain because i'm actually not sure what i use you complete me is the short answer so i use whatever you complete me does and i think you complete me uses uh jedi but i'm not sure if jedi uses any lsp so i i i'm not i'm not 100 confident on the answer to your question there so i apologize for not knowing that one for certain um all right so we've got a we've got the stuff refactored here and so this is the simplified version we now have this course query set and um no problem answer the best i can if i know more i'll i'd be happy to oh you know expand upon that but i just don't um what else do we have we've got um well let's i guess let's go back to our test file and see hopefully there's no new failures still i should have that one failure from the test case that we're adding but nothing new well thanks for jumping hopping in for a minute appreciate you being here so it looked like that was a successful method extraction i think so i think the next thing i want to do is we want to turn this into a mixing class and so we'll say course um course query set query set mix in like that and then we're going to totally pull out this method and drop it in this class i have a feeling my pi is going to freak out because it's going to be like i don't know what you're talking about that's the challenge with um with mixing classes is that i use my pi to do static analysis and well my pie sees this as an object which they're right it's an object uh but since it's an object it doesn't know what the request property request attribute is which is kind of a pain in the butt but we can fix this by saying if type checking and we can define what request is going to be and so i'm just going to give it an http request i don't know if i'll need to import that or not um but that that's enough to make my play happy to hint at here's what the type is so now we have this mix in and we need to include the mix in and you want to put because of the way that um because of the way that python does method resolution order you always want to put your mix-ins before your base class which is why this mix-in is first and then this mix and then the actual detail view i'm not going to go into mro but just take it at face value that you want mixons to be before your base class listed when you're doing inheritance again this stuff should still pass i just all i did was move the code to a different class and then made the the thing inherit from that and we have just the same one failure the benefit now though is we should be able to go to the edit view and add the course query set mix-in like that and change this in fact in this scenario we don't have to add any pre-fetching so we can delete this code and then say self get course query set and that should still be the same with that one failure all right cool so we're getting there we've we're ready to go correct that failure i think or we're going to at least get closer to it so the edit view is now in place that's now got the right thing we can move down to the detail view or excuse me the delete view and say course query set mix in and now when we're at the get query set we can return self get course query set all right when we run this test though it's still going to fail why is it going to fail because there's no template yet and so it's probably going to say i don't know what you're doing all right so there is a template pattern that is expected here and so because we're using delete view i'm just going to use the pattern the conventional pattern that comes with with the delete view and create this template and this is where all of our confirmation data that's that i've been talking about is going to go that will populate a minute so let's go to the templates we'll go to the courses and i'm going to probably copy from one of the existing delete templates that i've already got and we'll create our new thing which is the um the the name of this has to be coarse because that's the model and then what the delete view is looking for on a get request is confirm delete is the pattern we need to complete and then we've got this and we'll change the go ahead and change the little bits of this here of course oops i don't need course j course and well we can populate some of these other bits in a minute um make sure that i think now that all of this stuff is in here we have everything that we need and with any luck that was what was required to make that test that was failing pass all right great we can also um we haven't changed the post yet but uh this is this is good stuff so if we run all the tests for this file we've got back to 100 green and that's a good place to be i don't like to be dealing with too many failing tests for too long that's not a good not a good spot okay so the i said that there are bits of data that we want to include on this con delete confirmation page and those are going to be counts um so what i want to include is i've got some to do's somewhere in this file we'll go take these to do's now and put them with the new view uh okay right here so we need to put these this thing these things into the context but i guess before we can do that we actually need context so there's a get context data method that we can include and so we'll start doing this we'll we'll take little baby steps uh so we've got the query set the way i kind of view this is like how's this view going to be processed um what is the the order of which i expect these things to happen so as a course delete view is going to come up it first is going to the access check to see do i have a real course that i can get and that's where the get query set stuff comes in the next thing it's going to want to do is ultimately like render that content that's where get context data comes in because the context data is going to be mixed into the rendering process so that's why i'm listing that second and then the success url which i really think is not going to require the details is going to be the last last little bit there um so that's why i have this ordered that way uh we want to put all of those those counts into the context this is so this is where it's going to go and i'm just moving the to-do's closer to where this is to to see this so um let's do this uh if someone has gotten this far if they've gone through the get course query set we now can trust uh and i'm gonna write that test a little bit later that they are allowed to see numbers related to this we need to walk through and do a bunch of count queries basically on various bits of this in order to populate the context so let's assert on the things that i expect to be in the context when we're done we want to assert that in the context we will have um i'll just say task count that should be sufficient and i want that to equal 1. right now that's not going to exist in the context at all so we'll get a key error when we run the test saying it's not there so let's go fill that in so we have context here this is the point at which we're going to add to it we've got the task count and we're going to say [Music] what are we going to say i can never remember is the is the object that's available for deletion is it as self.instance or self.object or i guess i can get the uuid there's a lot of ways i could could get this but let's try i think it's object so we'll go with that so we want to say course task objects filter and this is where we want to put in our filter um we'll say self dot course or self dot actually self.course might work because that's the name of the model but i'll leave it to the more generic and then we want the count with any luck i did this correctly and we'll still get a failure but at least it'll say it won't be a key error to say 1 and 0. yeah so get object must have been the right thing that's good um to make this representative let's add in a task oh yeah yeah that's a good call there's a get object method thank you i forgot about that so let's put in use the course task factory and associate a task with this course and i'm just doing this to make sure that we're actually querying the right data i mean i could even go so far as to say course task factory to have another instance in there and make sure that it's not going to be two i don't know that i really need that level of testing i'm not like that paranoid about it because you know i'm looking at this right now and i can i can have some trust in myself that i did this right um maybe that's maybe that's foolish but i'm gonna do it that way okay so we have um course tasks that's that's item one the next thing that we need getting back to you know what we saw in the admin is we need grades so how do we get all of the grades that are part of um this course so this is where we need to even i don't remember i don't remember how this was modeled together so we got to find so course task has grades they're way up here so they're through a bunch of different relationships so it's graded work is this guy which corresponds to a one-to-one field for course task which then goes back to the course so that's tricky but i think i think we can still do the right django query to get that so let's say um the next thing we want to assert on let's keep doing it driving it from the test let's say um we'll call this the grades count uh do i want this to be plural tasks count that's probably more accurate just me being picky so what goes into making this query work so we've got the grades count that's that side and on the other side we want to get the grade so we want to use the grade model which we might need to import i don't know and the filter on that looking back at the diagram is it's connected to a graded work which is connected well i've got some pre-fills here from previous stuff that i must have done so it's connected to a course task which is connected to a course and we say course and then we can do count on that and i don't have the grade model imported so we need to go import that i believe it's coming from the student models area and this doesn't tell me but i'm pretty pretty sure it's from student the students app so we'll say grade all right with any luck this should now be a zero versus one problem rather than a key error whoops court has no attribute course well that i guess answers our earlier question of is it smart about the auto naming clearly no it is not run that rerun the test and zero and one okay good and now we want to do a grade factory so i've got um this is using factory boy i've never seen it i'm not going to cover that today but there's lots of um factory boys really good tool for creating data for your tests and that's what i'm using here we want to use the same exact pattern of graded work connected to a course task connected to a course and so they can wire that all the way through so now we should have one and one although we need to bring in the graded factory the grade factory which is in the students app create factory all right so did that do what we wanted to oops two why are you two that's curious oh you know okay all right you got me so bye this is interesting the because i'm creating a task here the grade factory as it walks up the thing of the things that it needs it has to i didn't provide a course task so it also has to create a second course task that's what's implied through this connection so we actually can say instead task and then give this local one a task and it should be fine that they're the same same task that doesn't really matter now we should get one and one i hope all right great so two bits of data down um the next is the coursework coursework in the modeling that i've got for the system coursework represents anything that the student has done you can see it's this central model here the student is coming from this side to the course this is like the marriage point between um what the student is doing and in the school year so it's a foreign key relationship between the task and the students are both both tracked in there along with some date stuff so we need to look for a coursework count so we'll do another assert and i apologize it's good probably getting a little monotonous but it's important to get all of this stuff properly tested in here and it's just the reality of writing a robust view so we've got our coursework count and the path for coursework here these are all the nice part is that these are all pretty straightforward um foreign key relationships so these are not hard uh orm filters to write looking back at it it's course task up to course so we can say course task up to course self.object and then we want the count on there so that gives that one and then coursework is another model that i believe is in students area as well because it's something that mostly affects students and i i don't i don't really like bringing in a lot of models from other apps but um this particular view it just needs to be really informative and the best way to do that is to report on all these other models that are affected there's really no getting around it so we should get a failure there whoops um not that failure though it's objects because it's the manager i expect we should get a failure yep zero and one because there's it's basically saying there's nothing that has been completed but now we can do a coursework factory and we can complete the um the course task side of this so say course task equals task and coursework factory is not in this file so we need to bring that into whoops um coursework factory all right with any with any luck that's another bit of data that we need all right cool and then there was there's this last this last one course resources these are things that um that my spouse wanted to track like books and any um workbooks or whatever videos like i don't exactly know everything that would go in there but anything associated with a course and that will also be deleted because there's a foreign key to it so we need to then have a course resources count needs to be one as well so that should fail it does because the key is not there now we can go to the view and delete the two new comment and say context course resources count is equal to course resource objects filter and this one should be just course equal self dot object oops object man cannot type all right and now that should still fail because it'll be zero and one but we can add the last factory to complete everything course resource factory and we'll give course equal course and we got a passing test okay so why why do all of this seems like man that's a lot of tedious testing for this but i do it because this particular user this particular unit test is the only thing that is accurately describing this is the chunk of context that i want to be in my template it's the it's essentially like a data data contract of this is what goes in um in the template and i can look at this and say okay my template cares about all of these things and i do that because i don't do like end-to-end testing using selenium or something and and putting all this together and checking that out ah okay good good question um so you're asking about when did i switch off of conductor well i switched i think it was probably like was it stream 39 or something i don't know if you're watching going back into the list and the i switched for a couple of primary reasons my my spouse stopped doing college consulting and so that particular application was intended to be a tool to help with college consulting um it never quite materialized i didn't get it far enough out there she wasn't using it anyway um and then so she stopped doing that and i figured well what's the point like this is not an industry that i want to continue following because i don't have a personal stake in it so time to move on and uh so i when she switched and the part of the reason why she stopped college consulting is is she's now teaching my kids at home school so i wanted to make something to help help her because she was having some challenges with the the service that she used to use it wasn't secure there's other things involved in that so that's the primary reason for for me switching is i wanted a project to work on and um what better customer to use than the one who lives in your house and is willing to love you and answer your questions when you have silly questions um in terms of the differences um the final version of of college conductor is fairly similar to school desk school desk is the actual name of this service so that i it's how i've branded it there the biggest difference is though is i've switched hosting so with with college conductor i used digitalocean and i did all of the infrastructure myself using ansible and some other tools and it was a good learning experience like it was totally valuable but i definitely sank a ton of time into making that work well and doing all the monitoring and stuff when i didn't even have customers so with this project what i really wanted to do was get in there and deliver something as quickly as possible and not worry about the infrastructure so with this one i switched to using heroku as the hosting and what that allowed me to do is spend a lot more of my time focusing on the application and let heroku manage deployment of the web server and all the stuff that goes in that so frankly it's been a fabulous decision i mean i think i spend probably like two dollars more a month on hosting cost and i know that if i did get a lot more traffic heroku would eventually become more expensive but in terms of bang for my buck and considering like i am a well-trained software engineer and i know that my time has cost to it if i really want to think of it as like purely what is it worth to me to spend an hour doing this versus that it kind of become became a no-brainer to use heroku so at the application level they're both server rendered applications the college conductor one initially was not it was using ember and a django rest api um but i found one of my big learnings out of it i think i wrote a long article that you can probably find online about um lessons as a failed from a failed sas you can search my website for that and um one of my big learnings out of that was that trying to maintain a single page application a rich javascript client and doing a back end it ended up leading to a huge amount of duplicated duplicative work i found myself modeling something in the back end and then i would have to model the same thing on the client side and that was um that was another big time sync i mean i learned a ton from college conductor and it was it was a valuable experience and there's still a lot of good interesting content in some of the earlier streams but if i if i had to do it all over again and circumstances were different maybe i probably wouldn't have even started this but you know i have a whole bunch of different life circumstances so that's that's why this exists um okay i think we've got i now have um yeah there that's where that's the stream episode where i talked about this so i think i spent one where i um just talked about it on a video um if you don't have time for that there's also an article um let's see surely it shows up i have a little google search bar that i type and failed will that come up what uh uh why is that showing like there it is yeah so i wrote about like the actual so if you want to read my writings about it you can come here and check that out i've got details about like here's and it kind of reiterates a lot of the things that i say in the video so it's whatever your preferred mode of getting that stuff is or don't read it all i don't care um but that those were my thoughts on it that are over there okay um i think what we're we're well positioned to do now is now that we have this context data we can go to the uh we can go to the template and plug it in and some sort of styling so let's return to that confirm delete page which is still configured whoops that's the wrong one um where did it go of course there it is up at the top we can start to fill this in so we can say delete course and because this is a delete view so this is one area where this will where course will show up is the the delete view will populate the context with the actual name so we were referring to it says self.object in the view but there will be a course in the context in the template side so we can depend on that and let's go back to the model diagram because i don't remember if i called it title or name name okay so there's no title on a course but it is the course name and i'll leave that if blocked for a moment this is are you sure you want to delete this course that's still accurate so a lot of this pages is going to be really accurate okay so let's fire up everything here and we're going to have to navigate directly to the page if we want to see this because we haven't connected it to the edit page yet which we can do pretty quickly but i don't i'd rather just look at this page for a moment so we can see it i'm going to come up here i'm going to change the url and just add tack on and delete at the end so here we go we've got our delete course i'm not going to click the button because it will actually delete stuff and that's not what i want to do so we'll come in here and add in some text i don't know if we need like a if we need kind of a paragraph describing by deleting all of these things you're going to you know this is this is what's going to happen by by deleting this course yeah that's probably something i have but when do we display that we only want to display that if any of those task counts exist so there's the first thing we want to see is a paragraph tag we want to say if there's a tasks count or a grades count so any of any one of these should be if any one of them is positive then we want to display the message or a coursework account or a course resources count if we've got one of those then we want to say um by oh no what do i say deleting this course will also delete and then end it and refresh the page and cool got that so now we can have some very we'll use a list um an unordered list that will make exist no matter what because if if it happens to be empty that's fine oh not li we want unordered list so that's ul and in here let's actually give this stuff some breathing room there's no reason to have all this jammed together we don't need we don't need any of that and then we can say what we can say if tasks count then we want to put in a list item uh tasks and then we'll just this is going to be very very simplistic first version i i would expect that at some point some customers kind of come back and say actually can i see this on this page and that's fine i just don't want to do that prematurely so if we refresh the page now we'll see okay where did where did the bullet point go well we're using tailwind and tailwind takes that stuff off by default because they want they expect you to style these things so let's go to the version of tailwind that i've got i'm actually not on 2 yet i'm still on 196 because i haven't fixed up the colors and we'll go to unordered oh come on unordered how about just lists lists i don't want list inside i really love tail one as a framework but sometimes when you get to the more esoteric pieces you gotta like go dig into the stuff so we want list disk that's a mouthful class list disk disk there it is hopefully all right so now i think we also want that position there's like inside and outside looks like it's defaulting to using outside which i think i actually want list inside yeah i wanted to read more like that the other thing that i want to do is push push this down quite a bit so we want to actually let's in fact because i'm not sure that if this no i am never mind we can give this some margin bottom of 8 and that will push the question down and now that we just need to give some spacing to the actual bullet points themselves so we can say and we'll play with this as as we add in the second one because it might look a little funky so for now we'll just say padding y 2 so that some p y is vertical padding y axis and 2 is a fairly small amount and i want to say i want to make it clear that this is like there's no going back so deleting this course will also permanently delete um all of the following associated items wordsmith that later but conveys the idea the other thing that's pretty wide like i'm not that'll go on all day um so let's constrain this but wrap this in a div with a class of max width of extra large i think um they're all hard deletes nuna mas um yeah i mentioned that i think you might not have been here when i was i was talking about how for the sake of dealing with like gdpr and anybody who wants to like really request stuff really be gone i'm just gonna take a more privacy strict stance of if you delete it it's really going to be deleted and that's on you i think that will be i think people would generally prefer the privacy is my guess well now that just looks weird all right i think i just i think maybe rather than do that max width i just need to trim this this is just too long maybe i think all this all of the following associated items is is yeah no kidding so let's just delete all that stuff let's leave it at this permanently delete okay so the rest of this is still on filling in the blanks we have the grades count and i guess this actually makes sense to come after in fact i'm going to go ahead and reorder in the view not that it matters but i want to so grades oh yeah that's that's rough looking let's not do p two try p1 still looks a little goofy doesn't it let's give this uh lead-in pair sentence some stuff as well okay that feels better to me i don't need to make this view like the most amazing design but i don't want it to look like really jammed together either and we can say what else um this will be the this is where i want to put coursework and what is this this is completed this is writing it to be like a little scary completed student coursework they need to recognize that if they have if their student has done something yeah i hear you i do sometimes modify the element styles as well directly from from that so there's a good way to go anyway they so i want them to be aware that like if you're going to delete coursework you need to know that you're going to impact your students so i'm going to put it use the word completed to indicate that that is part of that and then finally the course resources and we'll just say resources for this one oops oh there were none okay so that's what it's going to look like if there's if there's something missing it's just not going to appear and let's to confirm that this is in fact going to be there um we can go to the actual math class and we can say add a new resource and we can say algorithms which is a terrible book that i took in college um yes it's using the uh the cascade deletion from sql to delete all these things it's a good question i earlier i covered why would you use cascade versus protect versus sentinel and for my use case to to be able to conveniently and quickly delete everyone's is a person's entire set of content like if i got a request to please delete my account and it was couple along with a gdpr of and delete all my data because i'm i'm really concerned about privacy i would like to be able to say let me go and i'm going to delete your school which where is it on the model here this school model right here is the root of everything that all of the users data that lives up here eventually ties back to a school so i could say delete the school and it will totally wipe out um anything that the user has entered into the system with a high degree of confidence and that's important for my application but you know each application has its own constraints so now we've got a resource on this page and we should say resources one so i feel like i'm using the orm that comes with django so it's it's what's enforcing all this stuff um so now i've got this page it tells me everything i want to do i bet if i deleted that if i click the delete button right now that would kind of work except it would take me to a 404 and the reason it would take me to a 404 is because the um the the success url is to the delete is to the detail page of the course which will be the thing that we just deleted which won't exist in the database anymore so that doesn't make sense so we're going to have to send someone back to the um the school year probably of where they just came from and i think i think the success url i think the object will still be available in memory on the view before it's like it's going to be cache is not the right word but it's just going to be in memory on that view call so we should be able to get access to the um school year that's associated with the course or the grade level and send them back to that page i don't know that we're going to get to that tonight but that's kind of that's that's where they should go so to do send to school year on deletion that's that should be the landing spot all right but that that completes i think what i want to do for this template and if they have nothing then this the message this message about deleting other things will not appear and all of these other things will be empty as well there will be some padding that's okay it'll be like it'll basically say delete course and my unneeded course and then the delete button so that's that's totally fine with me so i feel good about that all right let's return to the um the actual post that's going to do the deletion and this is where i think using the edit view is not a good choice because i don't think that it's needed although there is a delete view down further here oh come on so what does that post look like yeah with this post you don't have to provide any content because it's going to get it from the uuid and you can see where this post is going using vim is hard um yes initially it is quite hard um my my first my very first job out of college so my first programming job i should say i i worked for lockheed martin the defense contractor and worked on the global positioning system ground control software it's a good interesting first job um but day one of of being there like most jobs that you have to do you have to especially at a big corporation you have to do a certain amount of compliance training so like videos or whatever that they want you to watch and then you have to start understanding what is it you're going to be working on so my mentor if you could call her that like the person who was assigned to help me from the beginning showed me how to get onto the solaris system that this program was using and program contract that's the kind of the lingo in defense contracting um and so she got me signed into a gummi solaris account got me signed in and told me to like open up use vi and open up this text file and the the amusing part about this is like i've never used vi in my entire life she said read the stuff figure it out and let me know when you you know have questions um so i was using this new text editor that never touched on an operating system that i had never used and was told or just left my own devices to go read and yeah pretty much random string for quit i was i was very confused because i didn't even know what vi was back then like i was that's how kind of green i was at programming um and for those of you that don't know vim is kind of famous for being hard to quit because it's a non-standard text editor it doesn't do what you think it would do like i'm moving around but i'm using j and k to do that which you might be like what how's that working um anyway i don't want to get too sidetracked into vim but uh yeah that's uh it's been good to me it's taken many years and to get used to it but as soon as i did um i i'm i'm pretty confident in it now i feel like i'm much faster than i might otherwise be okay so we're going to delete this old this other edit this edit test this post test from the edit view because that doesn't really apply and i just copied the delete task view so it's going to be extremely similar to what we needed to do and we need to delete that and change it to delete getting kind of meta here so change this to delete and we don't have a task i forgot a comma so that will probably simplify it down to a line we will have we want to assert that the task is in fact deleted and this is where um right now this test actually might pass because we haven't changed the success url but like i said this success url the location header is going to be fine to make that the url but if a user actually went to that page that page would then be a 404 because that that we just deleted the course so it doesn't make sense in that regard but the test itself aside from that is accurate um so this is where uh this this needs this test needs to change right here to to assert that it's going to a different place it needs to go back back to the school year at which is most relevant um but again i'm not really sure that i want to take that on tonight like what do i want to do what what is the next step where should we go because i've already been going for half an hour and a half how much should we go maybe we should finish out the feature that's probably okay there's not too much left to do i think or i hope um we need to wire this together that's that's the next step so i'm going to steal from the one of these other pages we've got this course resource form page and it has should have on here a delete button here it is and so we need to descend to the delete view similarly for the course page so we're going to come down in here and make this look the same so i'm going to walk away from the views file for a minute we don't actually need that up um i'm going to go into the course form and we're going to probably have to modify this this isn't this is actually an older form so some of the stuff that might be in here is well yeah there's there's other stuff in here too so this is actually going to be a fair amount of reworking perhaps i need to see what's in here so we've got a surrounding flexbox which yeah let me let's let's bring it up together shall we so we want to look at what i'm getting to is this edit page right here it has a cancel and an update button and there is a course copying feature which frankly i forgot existed but this was important at some point and i don't remember even how to enable this anymore it's crazy that's a sign you've been working on an app for long enough is like the things that you discover in here are things that you wrote especially i mean of course that's not may not be the case if you're working with other teammates but it is for this context all right so let's do this let's throw in this delete button and just see what happens oh i can see i can already see how this is uh this is different so this look at this this has a flex row and it's got items center which is good but there's a difference here can you see it it's this justify end so justify end is telling this to move over here in fact we can inspect this row maybe inspect and if we took off justify end then you'd see a different i i think the buttons are just going to go the front okay so that's normal flexbox i would expect that but we don't want that we want um we want a delete button over here and then we want the buttons to be on the other side so what we need to do for our case is take this delete button which i say it's a button but it's really just a link and we don't want to delete when it's on a create so that's that's not the scenario we only want to do that when you have in the not create setting that's when we want delete okay so i'm going to refresh the page and you're going to see it's going to look oops uh it's not going to work at all because i have it broken right now let's change this to the right url to make this work okay so now we've got the that should properly work but you can see this is where it's broken is that the delete button and the cancel button are jammed up on each other and um what i showed a moment ago on was the resource which we have at the bottom which we can come into the edit view and you can see the thing is spaced out that's what we want we want the spacing so what i'm going to do is on we're actually done with i think i'm done with the resource form i'll leave it up in for a minute it's not hurting anything we need to put in a flex grow which because i want to force them to be as far apart as possible and that we can put it right there and what that'll do on this page is um take up the maximum amount of space that it can to push the buttons to the farthest and so we still get the nice cancel and update feel and the delete button gets to be where it is there's a problem with this though the problem it doesn't appear until you're on a new um create because what i just did a moment ago well actually is that not even true i left the justify end on there maybe we can leave justify end on there and be totally okay let's go check it out um i'm going to return to the this area and create a course do the buttons look okay ah there's copy cores they do all right well cool that was nice and easy um so we're done with the resource form we've added the delete button and spaced it out appropriately and so i think now we're in a spot where we can click it and we should end up back on the same place that we were just a minute ago with all the information about this is this is what you're about to delete okay time to go to the right um place on success so we're going to bring up the test and the view again so in other words i think where we're at now the template is for the delete confirmations done the button to link it in is done and we just saw that it links the proper place so i don't feel like i need to check anything else there i think really all we need to do is make sure that the this ends up in the right location so i really want it to end up where i want it to end up on the school year detail page so that's under the schools urls there's the school year detail page right here so what i want to include is schools school year detail and i need to have the uuid be grade level school year oops uid okay with this now this should now fail because it's going to be a different path and it is the question i have is i am not 100 confident by the success url what if i can even if i can get the grade level out so let's just try the what i think is going to be like the naive version of this i'm going to see if i can get a grade level and probably also need a safe fallback in case there is no grade level we'll take one piece at a time because they're actually never mind there should always be a grade level i've configured the system so that the constraint is that a course will always have a grade level and you can't remove it so we should be able to say self object uh there's a many to many uh field on there so that's through a many to many manager so we should be able to do first and that should always return a value i know first is you know it's kind of scary that it could return none but in if i'm modeling the system correctly this should never return none in my estimation so we want to change this to be the school's school year detail and then i also i guess need to do i don't want to do multiple queries so let's put in a select related on the school year oops so that will grab the school year as well and then when we get over here we can change see i'm afraid that it's just gonna well i don't know i'm speaking out of turn let's just try it grade level school year uuid okay does this work no oops oh um because i said grade level but i need to it's actually great levels because it's a many-to-many field a school could be part of multiple grade levels the grade levels will all be in the same school year that is another constraint so it doesn't matter which one i get um as long as i get one hey it worked so it must be that well i'm actually not sure what it must be i don't know if the the deletion hasn't committed yet i'm not i really don't understand how this actually works because i don't know if the delete actually occurs after gosh man that's that's really perplexing and maybe that's not super obvious to to you if you're not a big django person but you know think about this if if this is a delete view and it has to get the object first it loads that into memory with the orm and that's what represents what self.object is but then success url is supposed to be after the deletion right so the object is then pointing to a database row that no longer exists how then can it go and query on grade levels if it's not there anymore so it's like when does that actually get committed i'm actually really really curious about that so we're going to spend like a minute or maybe two looking at the detail view and see if we can make sense of this i don't want to belabor it but it might be interesting so i've got django here a local clone um yeah i don't know that actually if the orm there's there's you you might write there's some kind of caching going on the orm and django i don't know that entity manager is not really like a concept that's used much in this particular orm it's not an object of work or at the right pattern um unit of work pattern i don't know that that's the really a thing but we can check out the detail view and see if that will shine a light on anything here so views generic this is an edit view uh you can look for delete and you've never used yeah yeah it's a it's a involved framework so i don't i appreciate you trying on this it's cool aha check that out right there there answers the question they lie so this is why it worked is it does have the object but it hasn't done the delete yet it says give me the success url first and i'm going to and cache that in a local not cache it well i guess it's technically a cache but i'm going to store it in a local variable and then i'm going to delete the object and then i'm going to redirect okay i feel much better i was worried that i might be running this on like crazy data race condition and that this wasn't going to be reliable and that was mildly terrifying i'm glad that the code was so easy to quickly understand so this success url is not in the timeline that i thought it was it's asking for it first which is fantastic gives us the confidence that this code will be reliable okay so that brings us that's the post there is one more test i guess i want to have in here and this by having this test um how do they surpass the gill problem good question the the primary mechanism is through multiple processes is the short answer um and well okay the slightly longer answer so there's there's two two ways about this so when a django application runs and i'm talking about synchronous genko here so django uses uses wsgi the web server gateway interface which is a flask uses this pyramid uses this lots of things use wsgi and which means that [Music] that it can plug into g unicorn it can plug into you whizzy it can plug into apache all these things each of these work their their model of working is to have a primary a main process that delegates so it delegates to workers so um that's that's one way to get around the kill so if you have multiple requests coming in then the the main process is really just a it's dispatching to um other stuff as quickly as it can so it it's listening on the primary port yeah ipc style yep and um it's handing it off to another process uh and it says you handle the request now and there's some port magic stuff that goes on there to make that possible and then the worker handles that that's one way the other way is that when you do i o on um yeah when you do i o on some of these requests python will give up can can give up the gill and that allows other processes to work so the workers in certain modes with certain settings you can make it so that they will do more stuff uh if if the gill was given up i know that's crude crude explanation i apologize and i'm you know it's getting late my time so brain is not firing in all cylinders at this point but hopefully that gives you an idea okay let's get this last test in here it's like an access control yeah test so um test other say other course and it looks like i've already used this one and maybe i do have a test for this you're welcome um so perhaps i don't need to write this test because now that i've used a mix in as long as one of these tests checks it well then i would feel comfortable but i'm looking at the detail test so remember we well maybe you don't remember if you weren't here the whole time uh earlier i extracted a chunk of reusable code that i applied to the detail view and the edit view and the delete view to do get the right query set the orm query um and one of the things i want to check is that because of that someone who is not authorized to access a page can't in fact do it but it looks like i don't have that test so i'm gonna i did check the code that i'm confident that it's working that way but i'd like the the sanity of a test to prove it so a user may not access another user's course that's what i'm going to test here and we'll do that by doing uh well actually we don't even need to do this we can really just have any old course will suffice so what by having a factory that has does not take any parameters it means that this user and this factory are not connected to each other so if i do the login and do a course delete this should well i don't want to get check for a 200 that's not accurate i want to get the response out and i want to see i want to assert self [Music] wow there's a lot of asserts there good gracious huh i don't know where all those are coming from insert http those are kind of fun because what i expect is that this will raise a not found i'm going to try this assert i've never actually tried it before pretty descriptive it's probably like this because i don't really like it i don't want that i i like so i'm using pi test for a reason so i'd much rather say assert response status code equal 404 that's like exactly what i'm looking for so yeah i just feel like that reads so much better than a method and anyway personal preference not gonna knock anybody that prefers a method style okay so we've got um we've got all the tests for this we've seen it delete i haven't actually done a delete yet so maybe we'll go do that um we'll come back to the school year and i'm not going to delete that math one because it's convenient but let's go down to some of these ones that have other i think i was putting some stuff in here the other day for a different feature i was doing yeah um i do and so this is this is a trash course and i'm going to delete it and it should say that it's going to delete 15 tasks so we'll say edit we'll say delete it's going to delete 15. took us back to school year full loop full success and it's now gone from the list awesome okay um to finish this off let's take a quick look at what we got we'll review the diff pretty quickly we've got our test case here which tests for unauthenticated it tests access control it tests get and adjust the post that's what we need we've got the url itself we've got the actual detail view um oh i guess this part's the mix-in that was the refactoring that we did and yep we've got replaced all that stuff and then here's the new view it works on uuid it gets the query set it adds all that context data that we wanted and then redirects to the school year page that we wanted to um here is the page it has delete course which you can you couldn't see but i could see in the browser and it actually lists up the course lists all aspects and gives the delete button and then finally there's the button itself that we expanded so i feel good about that and one last check is to test that we have full coverage this should take just like 10 or 15 seconds to run well actually i think i lied i think it's um it's been taking a little longer these days but let's go over here for a minute we are going to fix 372 i'm just checking that there's there's nothing else that i reported on here so yeah i've been pretty pleased with how this flow went this was not um a very complicated issue but it just goes to show that even a simple one uh there's there's a bunch of aspects to consider to make it something that your customers are going to enjoy or your users or whatever however you think of the people that might want to use your website this will fire up in a second here i'm using multiple processors and it takes a little bit longer to start up but then it once it gets going it uh it goes pretty quickly and i'm running this because it will run coverage analysis as well and tell me if i'm missing any lines of code and i am oh shoot um 179. let's go take a look what did i miss oh oh oh that's the type checking stuff that needs to shut up um there's a no pragma i think if you want to like ignore no it's a pragma a search for pragma yeah there it is so i'm making my project use 100 code coverage but type checking does not run at runtime so there's a way to tell the code coverage tool to don't not pay attention to that area and use the special comment of pragma no cover and now if we run this again it should should report back that line is covered because it's actually skipped and i'm not worried about that skipping because again at run time it never runs type the type checking variable is coming from the typing module and it is only true if it's being run by a type checking tool like my pi so we can have the assurance that this will be false during the actual running of the code so that's going to run again and then i guess we can talk briefly about well now we don't need to we'll just let this run for the next 10 seconds and then call it quits all right success um yeah i'm happy with that so we'll commit it all it's running through whoops what failed i'm using pre-commit and pre-commit is a tool that runs a set of hooks on it that will check the code before i commit keep my quality up and i sort failed so i must have imported something improperly oh grade alphabetically sorts after that stuff so it's just just reordering that stuff cool no problem we add it again we get all the stuff working and then we say add the ability to delete a course and that fixes number 372 we can push this up and what you'll observe if you've not seen that by using that fixes attribute by saying fixes in the commit message it tells github to go ahead and associate it with that particular issue so get the benefit of doing that cleanup all right that's where i want to stop for this evening next time we will get to some more customer requests i've got a fairly good stream of them coming in um little modifications here and there um some of them are bigger and i don't know how much we'll tackle but it depends on kind of where i am next week and and what i hear from customers this week um hopefully you learned a little bit about django here learned some things to take away and i appreciate you tuning in so with that i'm going to bid you good night and take care bye you
Info
Channel: Matt Layman
Views: 343
Rating: 5 out of 5
Keywords: twitch, Python, Django, SaaS
Id: kztmPO3UKm8
Channel Id: undefined
Length: 113min 30sec (6810 seconds)
Published: Wed Apr 14 2021
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.