Finish School Break Filtering - Building SaaS with Python and Django #115

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hi welcome to building sas with python and django and my name is matt lehman and on this stream we work on a django app and i teach you what i know about web development and walk you through the process of going through a real world app we're going to continue on that path tonight we're going to work on some user interface changes for a feature that i've been working on for the past number of streams i think i'm getting towards the end and would like to put the finishing touches on it review the final pull request and merge it in the app and move on uh so that's what we're going to do if you um want to stick around and check that out i hope you do um if you like what you see please like comment subscribe follow wherever you are coming from whether that's youtube or twitch uh that would be really appreciated and if you want to support me you can do so um i'm on patreon.com at mblamen and they're there you can support me financially um and i do want to shout out and thank those patrons that i do have currently that's rupert andrew dragos patrick eric philip miguel peter and shivan those folks make it possible for me to do some extra stuff that uh yeah that help out the django community so i'm really grateful for their support for those that are live on the twitch stream you can ask me anything about python django or web development that's what i'm here for but otherwise we're going to get into the code if there are no questions if you do have a question just drop it in the chat and please just be friendly that's all i ask okay um so we're gonna pick up with a set of to-do's and of course right as i start streaming my printer right next to me decides it's going to do a self-test or something that's weird um and we have a few spots left in the app that i've marked with some to do markers and the associated github issue as an as a reminder if this is your first time joining the stream we're working on this feature to make it possible so that my homeschool application which is the app we're building will allow school breaks so vacation days to be possible per student and we worked out all of the details of what that means we're kind of at the end of of that process so i'm not going to walk you through all the data changes and model changes and all that stuff we're going to get into that as we get to these user interface changes that is actually exposing the feature that is available in the underlying system um we so we have a set of to-do's that are backed by this particular issue number is things that i still need to check on so we'll go through these the set hopefully it is encapsulates everything that we need to do and if not then well we'll do more um okay so there are a few comments right in this view and this is the school break create view and let's just as a as a frame of reference let's get you uh able to see where the app is and what the kind of area that we're talking about in the end the user interface that we're working with and we can do so by going to port 5000 and go to the app um whoops that hasn't been the app name for a while um i forgot i changed it a long time ago so really we're going to dashboard not app i forgot i forgot i changed that and then when we go over to the school year and look at this page you can see we're messing with anytime you see a yellow on the screen here those are breaks school breaks that we've added for this testing purpose so i'm working in a test account and there's just fiddly numbers that we can work with they did appear on the weekly view as this yellow column of highlight there that says the break so that's what we're adding so what we're trying to do is make it possible so that you can have a break for one student and not another that's what the objective is so we have this ability already and this page knows how to do that with the appropriate modeling but it's really on the creation page and the update page for these that we need to account for some changes so last week we worked on adding this as a at a basic level of showing what data should and shouldn't be there and this this is the simpler case because by default we want the application to show that this is checked for every student we assume that a family in a homeschool if they're going to take a break are probably going to take a break on the same day that's a reasonable assumption so if you're doing christmas or whatever holidays you celebrate then you'd want to have those checked so that it applies to all of your kids but if you're doing a vacation that don't excuse me a field trip or something that only applies to a single kid you could uncheck one or the other and it is the editing cases and stuff that's a little bit harder on this when it comes back and there's a difference in that i don't know if you can hear that in the audio so i apologize it's strange that a printer wants to do a self-test at a random time of the night but that's what it is okay so that's the page we're working from we also have so the page that we're looking at this is the view that's backing it the school break create view and that's this page it uses a shared template of the school break form that is actually shared with the the equivalent update view so whatever result we have to do we have to um add context to the update view that is going to be similar to the create view which we don't have that in yet so i kind of want to finish off the add view to make sure that that's in a good state and then move on to the update view so we're not tackling two different views at the same time even though it represents kind of the same screen if you will from a different point of view so the test this was really about this context right here last time we added this get students for school year method on the enrollment class and we haven't tested that so let's start there that seems like a good place to start in the code i'm going to close this factories that we're looking at previously this is the other to do that we have to do there's some additional validation that must be done to make sure that we don't get ourselves stuck in a bad state so we'll come back to that when when we get to a different to do so we're going to go to the students app and look at the test models for that um because that is where well why would i type incorrectly there we go that is where the enrollment model is tested the enrollment is a model that represents the linking between a student and a particular school year so every student is enrolled in a certain grade level in a school year and the enrollment is what captures that association and it's what is needs to be shown here because we don't want to show students who are not enrolled in the current school year because they're not going to go on a break if they're not part of the school year so when we come down in here we should be able to find a test enrollment class here it is and i'm going to add a new method here that is this get students for school year so test get students yeah get students for school year and the testament doctrine that we'll add is find any uh what should we say enrollments now yeah let's stick with find finds any students that are part of a school year okay so to begin this we'll do we'll use the enrollment factory i use django um i use factory boy which is a system for making test data and so the enrollment factory will build not only the student but also um any associated data that must go in so there's some foreign key relationships on the enrollment that need to be factored in and uh the enrollment factor is going to build all of those things so i want to have um let's see what do we need we probably just need to check that one student is in there that should be a sufficient test so the other thing that we need is a student a student and trying to think if we should add any extra data about them so we probably want to confirm that they are not like it's a we want to make a comparable example so probably a student that belongs to the school is at least appropriate so we should be able to use the we'll say the unenrolled student is what we want to create in fact we don't even need to have a local variable but we'll say we'll use the student factory and we have to give it the parameters that we want to make some comparable data and so a student has a connection what do they have a connection too that's a good question let's look up it's been a little while since i've looked at the model um it has a connection to a school year okay or to a school excuse me so we can say that the school we want these to match so that the enrollment also has if you follow the path back far enough uh school as well so we can go enrollment and there's going to be a grade level associated with degree with the enrollment so we're going to query that and then we want to take the there's a school year that is associated with that grade level and then ultimately the school year has a school associated with it so this will do two things this will create the this enrollment factory will create a student and this student factory will also create a student so we've now got two students for the test but one is enrolled in school year and the other is not but both students are part of the same should be part of the same school so now we can use this class method and we can say students is equal to enrollment dot get students for school year and pass it the um well what do we need to pass it the enrollment grade level school year all right then finally we want to assert that the students is a list that's equal to the enrolled student that's going to be enrollment dot student that's the other parameter that's attached to this all right run that test and it passes so this is a test kind of showing the api for this i'm wondering if there we could make this any cleaner um i mean maybe we could extract a single variable was this is going to look cleaner or not i don't know let's let's try it so let's do school year is equal to enrollment this way it's going to it'll just read i think in a nicer way it's going to be equivalent and functionality but you'll see how it cleans us up so we can take this and now we're doing something more comparable and then we can drop this stuff and it'll filter it down so it made it shorter too so by using a little local variable it kind of just tells a slightly better story for the test so that's our test and um let's go ahead and move this to do down to the context on the update view so that we remember in the right spot and let's see what's left here all this other to do is also for the update view okay cool all right so i think we're down to three to do's and one is that validation one that i mentioned we'll get to those in a minute um and then we've got these two about modifying the update view so let's close this and close this we'll we'll get to the right spot we want to put the oh maybe there's another test i might need and might be missing a test i don't know if i added this or not so let's look at the schools app the test views file and let's look for the test school break create view yeah i'm missing some context here that i want to include so the school break create view should be able to show that we have students in it that are enrolled and i want to i want to the reason i want to include this test is i want to be able to kind of stipulate here's the context that i expect that the template is going to use and so this is a way of just indicating to myself in the future of here's the context that's actually important to this template and don't go deleting any context before you're ready so let's add an enrollment here and we'll do enrollment in fact we're going to need that as a local variable and we'll do enrollment is equal to the enrollment factory and we will pass in the school year to the grade level that way we get a comparable someone that something that actually shows up here and then we can say assert self get context and this actually kind of duplicates a little bit our logic frankly from that other test but the other test at least shows it in the single place where it's defined and you would expect it to be defined which is in the models file and this is this is more testing that is present in the context which is something slightly different i suppose so we'll check that enrollment student is in there all right and then we'll run this test hopefully that'll pass great the other part about this is that this test runs quite a bit slower that takes 1.21 seconds and the model test is doesn't have to go through all of the machinery of django and went quite a bit faster i don't remember the time i wasn't paying super close attention to that but now at least we have like we've indicated that this school break create view has the students in the context and while we're at it the update view we should put that so there's an update view down here that has a similar get and we can assert oops we can put that enrollment there and come back up to this other one and take the same assert and use this as a little bit of test driven development this time so because we want to be able to display the students as well here and so we'll take this from the school break this time since i didn't assign to a local variable so the school break has a school year that should do it so we should get a failure on the students is not in the context which is what it's saying here kind of not in the clearest way uh it's saying false is not true that that message would be clearer if they actually like inspected the fact that it really should be in yeah it should just be different but i'm not you know that's from something from test plus that whatever things could always be better i'm grateful that test plus exists it's a useful tool okay so now we know that we need to add students to the context of the update view that's the claim anyway let's see if that's actually true so the difference this time is that so school you're still out of the context because that's the piece that's needed but it's gets it from the object so the update view is different from the create view in that the object already exists so create view there is no object yet nothing's been persisted this time though we know that there is an object um actually you've been welcome i actually don't know what kia ora means so i'm i'm hoping that that says like hello or maybe you can educate me on that but uh welcome back um so we need to put in the students that we're going to work with um and we need to access them through the same object pattern that's here so it'll go like that but here's the difference this time we have to kind of modify this list so that we know who isn't isn't um enrolled and that's something that we'll have to to add some extra querying for um or i i said that wrong um not who isn't isn't enrolled but if there are additional many to many relationships because that's going to have to be matched up oh very cool i do i speak other languages i apart from python um sort of i i learned spanish in high school and i guess in elementary school i did a little bit as well um so i know enough to fumble my way around through some a spanish-speaking part of the world and then i recently have been learning um chinese and a tiny bit of russian for fun but i would not consider myself proficient in any of them so i'm kind of a typical american in many ways that i i can only really claim to be very solid in english unfortunately anyway so we we need to add extra data to this student's context and i think i don't typically like to do what i'm about to recommend but i'm going to probably do it anyway and what i'm going to say is that you know python is is nice in that objects can be assigned attributes at any time so there's nothing stopping us from uh taking the list of students that comes back in fact while we're here let's uh let's change this so let's keep keep some part of this let's double this up so we want to then change this to students because i think i'm going to work with this locally and then assign students here okay and then in fact let's while we're at it let's go ahead and check the test so this test should pass okay but the problem with the test that is not captured is that when we're in the editing mode we need to show which boxes to have checked or unchecked in the process of the updating and that's yeah that's something that is a critical element of this so what can we do we can i could either do one of two things i could say instead of having students be the context in the dictionary i could instead do something like this which is uh and i'll i'll hack this up but i don't think it's a solution i want to go with but i'll start that way so i could instead do a comprehension um that is actually a dictionary and it's student as the key in value and then um is on break or something like that um which i'll just say true for the moment because this is not it's just illustrative for a student in students and um this is like a way whoops um a way that this could be done but the problem with that is that the template will then have different expectations about context uh in one hand let's let's look at the template and be easier to describe it if we can see it so the we want to go to the school break form and we'll uh put that in a vertical split here oh man it's too small um let's get rid of the quick fix window here and reopen this in okay not the best view but it'll work well enough okay so the problem is this is that in the create version of this students is just this flat list and we can display uh uh students here um thank you shaven i really appreciate your comment there will is he is excellent as well he does a lot of of good stuff i've had the the opportunity to meet him at uh at djangocon too and he's really he's a really nice individual he's very pleasant to be around so um definitely there's some there's some good-hearted people in the django community that i really appreciate and i appreciate you your comment there um so if we have this create view that is just a list of students um yeah i got to be on django chat that was a fun fun discussion with will and carlton um really um they they do a good job posting that if if we have a list of students here then iterating over this simply is is nice like there's no ambiguity about context like it's just it's just a student and we know that a student is one of the models so it's very clear what this is what is less clear is um if we put it in this other structure where it's now like student is really like uh student info and then you'd have to iterate over student info. or you know like some some kind of indicator that it's a container and i go back and forth on this a lot because i've actually done this on many occasions of i've had to wrap up the data some piece of model data with extra context and so i'm not foreign to doing this approach and maybe there's an argument that says well you know what actually i should do is not test this but anyway we'll see so here's my other thought what if instead um yes that's sort of true um i think steven but the the difference is this update view doesn't know anything about students so it knows about um school breaks so it would be an object if it was all about school breaks it was like a list view for school breaks but this is extra stuff that is outside of the um default model view kind of stuff so i'm kind of going my own way here a little bit to incorporate a little bit of extra data but yeah here's actually your kind of your point um here's the object that is already created from the edit view um so i think what i want to try and do instead in fact let's let's do this and let's see if my pie grumbles at me because it might uh with what i'm about to suggest is instead of doing this dictionary structure i'm going to take advantage of python being python and i probably actually i can't do this in my comprehension um so let's change this we will do all right i'm getting this is looking pretty hacky but it's okay for student and students and then um i i'm not drawing a blank on a good name here students on break i don't know it's a crummy name we just need a local name and i need to be able to type good grief gracious students on break so we want this to be an empty list oh what am i doing i just need to iterate and modify i don't know what i'm going crazy here so we still want students to be students so that's all good but we want a for loop that is adding an attribute to this and we'll say that it's just true for the moment now the question is i have my pie and i think it's make my pie okay so my pie is not going to complain that's good i was afraid that it's possible that since the the um since the student model doesn't have this attribute and i'm just kind of adding it on my own that it might have complained and said like hey you're adding a boolean to something that doesn't have this boolean and what are you doing my pie can be a little strict about stuff so i think what i really want to do is i want to find out are there any um there any breaks that are not specific to a student and um i'm not saying that well in the many-to-many field that is keeping track of the school break and the list of students there will be rows in there that have that mapping and i want to get those if they exist for this break and for any breaks that any students that don't show up in that list we need to mark this as true and false and that way we can then come into this on the edit side here which is coming out pretty funky but if we're in the edit version of this which is this area we can then display gosh that looks so my my editor really does not like what i'm doing with this kind of markup style i'm going to convince it that you're going to stay so then we can come in here and then check on that parameter that boolean attribute and that will determine whether we list this as checked or not and then the form is going to do all the processing that actually will clean this up appropriately so i think that's what we what we need is really just a couple of a couple of lines in total but we have to set the data up correctly first so we want to go to the the object and we want to get the students out and i want to do probably uh well we'll need to do a couple things we can still use that really crummy name although this might be more accurate now students on break is going to be self.objects dot students and this is a rel related manager so we have to treat this like a something that we have to query against so that'll give us the students on break and then all of them and that will be actual student records which really is way more info than we need but that's fine it's it yeah it's really truly fine but and what we need to do is then modify this list of students so we want to go through each student and we want to default assume that everybody is on break because remember if there are no no rows in this minute of any field it's the default assumption that everything is on break um so that that is the default case that we want so we're going to set everything to true by default and then we'll do an extra check here that for the student so um if there are students on break this is one of the conditions here um yeah i'm happy to get to your question in just a second here shift and let me let me finish up this little for loop and then i'll be happy to jump uh to your your topic there so we're going to take the students on break and we want to check that everything exists or that there's actually values in here because if there's not any values in here then that's a waste of time so actually this this check will it will force the evaluation because the students on break is going to be a query set and the query set's not going to evaluate but once it does it will evaluate once and keep it cached so it does seem like it might be a performance penalty to check if this every time but it's just going to the do the evaluation on the first loop through so if there are students on break is want the first check and then we want to say if the student and the student is not in students on break then that's when we want to flip the bit uh here to be false and i'm going to probably rewrite the cinnamon and i'm seeing simpler ways that could this could be done yeah all right so one of the anytime you see this pattern where you've got an if and all you're doing is setting a true or false really what you're saying is that the condition that that boolean is is really the outcome of this if statement so we can really re rearrange this and represent it such that um i think well we'll start with this more complicated version and give ourselves a test and then um we can we can come back and get a more accurate version so i'll pause there though and come to your uh question about hash fields shivan so yeah i use the primary keys internally [Music] do you have to pass that all right let's take a look at one of my existing hash fields and i can just show you the example and you can decide whether it makes sense to you because what i did is actually slightly different from what you're suggesting although your solution works as well so if we look at what is something that has a hash field the school's app should have a hash field so let's bring up let's quit out of the template so you can actually see for a minute and go to the schools models and go replace the that file for a second and if we go to a school year which is something that is definitely what uses the hash id field okay you added a reference id yeah i think that's that is an acceptable way to go so here's my example so what i am doing here is a little bit different from having a separate id and your route totally works as is like an i think i think i talked to you about having an external something that you keep treat separately which just looks like it's up in your paragraph above um so i use the the actual primary key and use this auto field um the thing that i think is going to matter no matter what that you want to put in here is probably some kind of salt value so if i bring up the documentation for hash id there there was another like auto field or another field type that you could use that wasn't auto field there's just something extra that you add to your model ah maybe you're using this uh this documentation here so that's gonna add um i yeah i wouldn't set and i think i got this this um suggestion from carlton gibson actually of django chat so i think i've asked on twitter about about p if people use this and i believe it was carlton who who made the suggestion of you want to change your salt and the reason why so what i would and it doesn't have to be complicated like i'm you can see i've got an f string so i have the the global salt here the hash field salt that i think is a setting that you have to supply um for this package and all i've done is prefix it with a school year the model name to give it something different to use as its salt value and it will generate different values the reason for doing that is if you use the same salt for all your stuff then um let's say you have two models you've got model a and model b and not being very creative with my naming here but the instance of a dot id or reference id in this case might be something like a b c d e f i i don't remember exactly how many characters the hash ids have but if you have the same salt the if the if the a id or if the the count number um so if a reference id is equal to b reference id just like that then the actual value that you'd see the hash of b reference id would also be a b c d e f so the salt value guarantees that if you had different salts for them then b reference id might be you know just something different g a i don't know h whatever i can't remember my whole bed right now it's weird my brain is not wanting to do like start from where you are uh e f g h yeah there we go okay something like that anyway the so that's that's the value of the salt um i i think a salt i mean you're gonna need to read the documentation there i don't remember any constraints on the salt i think it just needs to be a string of some kind so as long as it's a string you should be okay yeah i'm pretty sure it doesn't care i gotcha yeah yeah so i think i think you're good so i presume that the hash id field when you add it will add a number now if you already have existing what you might want to check on your your if you add a migration or you do this locally i would suggest um with a local check have a couple of users or i think that was your model that you're referencing yeah you've got the abstract user so have some users that don't have any reference id add the reference id in the way that it's suggesting here run the migration and see if in the migration process each id that was added was actually unique for that um that particular user i would hope it would be but there's a possibility that the hash id field when it's being added to an existing model might be giving it the same value to start which would obviously be quite bad for for what you're looking to do um you know it may be unfounded fear on my part but as a sanity check for yourself for later that's something that i would i would recommend checking on hopefully that answers your question all right so let's go back to the view here and great glad to hear it so as i was saying well we actually didn't modify the test so let's let's go to this test and we want to say um well okay let's pull this out this is going to be actual no no we can leave it as students we don't have to get fancy here students there we go and then after here we can assert students is equal to enrollment student oh what happened ah it's not objects it's object okay so that test still passes which is good but we also now want to check that um that there is a student that has that is on break attribute so we want to assert that the first student the only student in this list of students is on break is equal to i'll just assert because that should be a true statement all right yep now the other thing that we want to do is i think i want to make this test a little bit more complicated and do the um do we want it in this test or i don't want a separate test we could do another enrollment and we would want to do it on the same same school break so that needs to be what does it need we need to create a break i've got the set of order that's the problem well crud all right talk it out um we want to have both of these students so this data is not correct yet that's that's reality so this enrollment needs to have um a school break be associated with it so we want one of them to be we want this to be a break for one student but not for another student so they both need to be enrolled in the same school year ideally in the same grade level so we'll come to this second one and we'll say grade level is equal to the enrollment above that grade level so remember the factory is going to create a second student because we're not providing it here and so we've now got two enrollments but we're overwriting the second and the the first enrollment which is not good we're shadowing that variable but what we need is um we ultimately want this to be a list of two things so we're gonna have to have both of them enrollment one and enrollment two and we've got enrollment one so i'm just making the data more accurate to what we're trying to represent and coming down here we need this to be enrollment one this should fail even if i get the variables right because it's one we'll have a list of two items and the other will have just the list of one so we need to add the second enrollment here and i'm realizing now that this is we're going to have to provide even more data because i made this sort by first name so this isn't going to be deterministic it's only going to work some of the time because the it's by the first name so sometimes this will pass sometimes this will fail so that was a case where it didn't pass and i'm going to switch it to a different version of this that uses a more verbose output so we can actually see everything [Music] so we we can see that the order is different so this is adam and i did this by first name so adam is expected to come first so if we test it again one of these times see there we got a pass so that would be annoying so we need to come up here and we need to say student first name um is equal to alice i guess classic like security kind of thing and then we'll do this is equal to bob allison bob all right so now this test should deterministically pass because it's always sorted by first name the other thing we want to do is we want to make the school break only apply to one of the students and that's where we can check well you need to pick one and so we want to say that let's say that the school break alice gets to go on break bob does not so school break dot students dot add enrollment one dot student so now alice is added to the many many to many tables so that um only one student is truly on break this should still pass because the first student is on break so we haven't haven't hit that other condition but if we change this to be include the second student the second student should not be on break this should be false for that student if we did it right and it is so we want this to be the the not of this okay so we now have the passing test that i expect in this test setup the it's got the right arrangement of data this test is getting a little bit more complicated um yeah we should probably even leave a comment here of why why it's getting more complicated like this [Music] make only one student on break because i think future me is going to be like what the heck is he doing in here so now that we have that we can correct and simplify this logic so as i said earlier if you have a condition where where you have a boolean check here and it's a true or a false you can condense that boolean down into a single line effectively so but what's important though is to notice that this is actually inverted so that if here let me let me comment let's have both solutions in place so that you can see what i'm talking about and then you can see how it will be different so we'll kind of keep one we'll comment it out for now and the the solution that i could start with is to say take all of this stuff and put it at the end here so we've got students on break and like so all right but this is actually wrong this one this version of this and it's wrong because it's backwards so because this this if condition is actually setting this to false when really if this what this version does is like it's as if this version right here is as if we wrote [Music] false assuming false by default and setting this to true when that then that statement turns out so we can fix this we still want this to be a bool and i'll explain why in a second we could put a knot in front of it and that flips it so that it's in the right sequence so that test should pass now all right why did i wrap this one with a bool um all right have a good one shaven back to work for you um i wrapped this with a bool because this is a a short circuit operation here with this and if the and fails and student on break is the is empty and we didn't wrap it with a bool then what would be assigned to is on break is not actually a boolean but an empty list and that's going to cause confusion so we wrap it with a bool to force the evaluation of that expression into a true or false and then we flip it because that's the logic that we actually want and that simplifies that i mean you might argue that one might argue that it's not actually simplified which is could be a fair critique but yeah that's that's the way i view things on this at the moment okay so here we are we've got students and we now have this is on break attribute that we can use we've written the test for it and i think we're ready to return to the template and modify uh that template so that it actually shows what we expect it to show so here we want to add checked only if the student is on the break so we can do that by wow it really does not like that so we're going to just put it away over here for the moment so if the student is on break then we want this to be checked and that's it i'll put the end if and pull this way back over so that my editor stops being weird okay so that's the condition that should display the edit version of this properly is the hope and i think i think that might take care of both of these to do's let's go check it out though all right so we're going to return back to the school year page where they have existing breaks and we'll pick one so both of these are checked right now so let's make it so that faye does not have a break on this date and let's remember the date so this is 9 16. to update that and it still shows on the calendar because there still is a student that has that but if we come back in here with any luck yeah cool faye is now not listed here because fay was not a break um you should be able to check that again update it again and return there we go conversely do mark and come back and mark awesome so that's both um as a sanity check let's aha sanity check is actually the thing that we want to validate against so i was about to uncheck both of these and see what happens if we uncheck both of these right now the code would allow it and this is a condition that i don't want to have happen i want there to be it doesn't make sense to have a school break on the calendar when there are students that are can enroll and there are no students selected that why why would you do that um so if there are students that are enrolled which there are two in this case you must pick at least one that's the other thing that we want to test um so i cancelled that so this is the version where one is all right it seems to be working like we want that's pretty cool represents a lot of work that has been done over many many weeks so we've added it to the view we have uh we've fixed the template to show it i think i think we're pretty good there let's see let's see what that what we've got so far so we updated the test to have this we check that it's in the context we've got we've gone down to the edit version of this we made the test data set up a little bit more complicated so that we could represent and test this is on break logic great we got rid of the to do's those three to do's and we've got the logic in the update view to actually add that student appropriate student information to the context there and then we've got the test that was that first to do so this is the other test that actually tests the model method and checks that that is the data is appropriate and then finally we've got this extra else clause on the template to actually show the appropriate data for the update view so we're done with those and let's return and i believe there was one more to do and it's in the form this one should not be too tough to knock out so this is where i said we need to do that data validation so at least one student must be checked if there are enrolled students all right let's see here this is not something that we want to put in the save i just put it i dropped it in there but we probably want to have a test first so let's bring up the schools test forms and we can use what test this is the school break form yep test school break here we are um so we want a test that goes after all these which is at the very end so test at least one enrolled so when students are enrolled at least one must be on break on a break for this break day form that's the context that makes sense um let's get rid of the quick fix window there and we need so we need the setup so let's get the happy path test of when this actually works or really no we don't want to create when it's created we want one that's already existing because we're talking about updating an existing one that right no no create would work too hmm oh this is tricky this is a really hard case to get to because it's hard to get to because it only comes up when you've gone to this edit case like on the on the create version of this if if there are no existing rows in the many-to-many relationship between school break and students that means that the school break applies to all the students and in that case is okay we actually don't want an error to come out that way so the create side of this doesn't really matter there's nothing to validate on the form in that regard because um the object doesn't exist yet there's just nothing to check against we only want to ensure that we can't be stuck in a state where well maybe i'm i'm thinking through this now thinking like what would happen ah okay here's here's why this this why this can't be permitted i can't leave a school break around because with no students checked because it would actually have the opposite effect of what the user desired so remember i said as this is modeled there's many to many and the many to many field any of those rows that are for a break that actually represents the school break for should only be for our subset and when there are none of those it applies to everybody but if we make it so that you can edit a break and take away and uncheck all of those students the user would expect that it doesn't apply to any of the students and that doesn't work with the way the data is modeled so that's why this is important to check i just wanted to make sure that i had a clear understanding of myself of is this in fact needed and yeah i believe it is so i want to work from an existing instance i think so we want to find one that uses instance which is one that's already there um so let's take this and i don't know if this is quite right what we want but uh we'll start with that so we've got a school break and this was about some other something totally different so we've got the school year id that's not going to change we'll keep the school break start date and school break end date we're not modifying we don't care about any of date parameters we don't that want that to break the test and we're going to say right now this should still be valid let's just check that it is okay so what we want is to modify this so that there is a student on this break so let's create um let's listen do something that's pretty representative here so we want to do enrollment is equal to enrollment factory and we want to set the grade level school year to be the same as the school breaks school year okay so that'll give us a student to work with that is enrolled in this school year for this that matches with the school break and then we can say um we actually want a second one of these and we're gonna make it so that's part of the same grade level just to get more accurate here so um enrollment uh enrollment grade level so now there's two students that are enrolled in this grade level and they're part of the same school year and they have both have a break on this day at this moment since there's nothing in the menu to many field it applies to both students um this should still pass there's nothing that we've changed about it we just are setting up some additional data around this what we want to do though is that we want this to be specific to a singular student so we want to say the [Music] let's we only need one of these to matter so we only keep one enrollment and we'll do self no we'll do school break dot students dot add enrollment student again we haven't modified anything so we're not we're not changing anything yet um what we do need to change though is going to be in the actual oh well now we've got a more challenging task ahead of us so the the way i did this was the the key in the data is equal to the student's id so that is student the sorry the enrollment student id and the value on that side is the enrollment student id so this again is not changing anything um in fact this is still we're not even calling save so we're really not changing anything we're just being more exact about the data that we're trying to simulate here um this is the scenario that you would want if uh if you weren't changing the the checkboxes on that form so this is like um coming back to this example let's start up the server again so let's go in here and so what we're kind of setting up just to make make it really clear we've got two enrollments the first one is part of this break so let's make it like this so this is the scenario we've got the first one is part of this break right and we can actually look at the payload for this bring up the inspector tools i thought that was command shift i am i wrong [Music] alt shift i i think i'm thinking of chrome i think it's command shift i in chrome um so when we hit submit here you should see the student id with the value of that id it'll be in the post so we've got the post and we can look at the request data that went with it so it's got the school year started and end date those are all fields that we have we're including description which we're not doing over here technically would be more correct to include that but we don't have to and then the there's also csrf middleware token don't worry about that that's for the csrf protection that gets stripped off by the time it gets to the form and we have the the student this is the extra attribute that we're going to care about which is the id value okay so what we really want to do is remove this we don't want this we're going to pretend that we're taking it off but we're not including any student ids [Music] and that's the scenario that is actually the bad one where it's gone uh down to unchecking both of those and then it adds that ambiguity in the data modeling where it would clear the menu to many table and that would cause bad news because it would actually have the opposite effect of what the user had expect so this is what we really want to be is valid is not valid and i want to find surely there's a place where i have errors in here that are checked against okay the non-field errors which is what this is going to be it's going to be part of a clean method and we can do something like this okay this is going to fail right now because we haven't added the actual test um let's get rid of this to do because we don't need to keep it and i want to come up to the clean this might actually be wrong right here i'm not going to worry about it the instance it's possible could not have a primary id a primary key yet so this could be wrong but whatever again not going to worry about it we want to go up into the main clean method and where i guess the question becomes where do i want to check this let's just put it at the end for now and if something blows up then we'll have learned something because as i said just a moment ago i think that this instance can have can be set to something like on create and not be there yet but i could be wrong about that so let's do let's assume that it is just a self.instance that should refer to the school break so there well actually there's two another test that we need to write later to do 434 write test when no enrollments that's the other test that we need so if there are enrolled students well actually let's ignore the enrolled student's part yet because that is the next the other test that i want to have that i think will represent this well let's just take the naive approach and let's say if self instance students count is greater than zero so that is like the specific filtering and there is no key that starts with students so of all the data um how would we write that concisely and not oh man okay let's get some working syntax i think i'm going to put this in a separate variable to make this more easy to understand because it's going to be too big of a condition i want this to be a boolean no student ids so we only really care about the boolean aspect of it we don't care about the actual values and we want to loop through the data and so we can use a python method here of any i think and any any key in self data if key starts with student with a dash so what is wrong with this call here why is that invalid syntax ah come on man four key there we go so if this evaluates it's this is the this is the raw data dictionary and you can see it in here in a test it's coming out literally as a the dictionary that i'm passing it in as when you do iteration on a dictionary by default it looks at the keys so it's going to look through here and see do any of you start with student dash and that will be an empty list and an empty list with any called on it should be false any on the list is false [Music] so actually this is more accurate to say has student ids and then we can over here say if you're if you if you have stuff in this menu and many table so you're already doing specific filtering for the school break and you don't have student ids if not has student ids then that's the error condition okay so we want to then say add error at least one student must be on break okay this test will still fail because the messages don't match up but we hit the condition so it tells me we set this up properly um we can delete this i'm pretty sure this is right um thanks lee thank you for joining in appreciate it so let's correct the error message at least one student must be on break and now we should have a passing test we might let's see if we have a passing file no okay yeah yeah yeah okay so as it does it is as i suspected that for these create tests this is look look what's failing the test create is failing so the reason why it's failing is because there is an instance but it hasn't doesn't have an id yet um it's interesting this this code is actually slightly wrong but i'm not going to worry about that so what i want to do so i really want this to check to be if the instance is persisted and so we know that it's persisted if the instance id is not none that's the more exact thing so it's there's always going to be this instance object by the time it's saved um or by the time it's cleaned and i think the reason for that is that the the model form is like setting up the instance in preparation of saving it um and it just hasn't called the save operation yet on the form that which will persist the instance and so that id hasn't been populated yet so now if we run the same set of tests hopeful that yeah we get full pass get the more accurate result all right so this is correct for the scenario and in fact at this point we should be able to go to the server and let's come back to the the edit page this way we don't need to look at this anymore so now we have um the model the many demanding data is still set up the same way if we uncheck this and update this break and it says now has the message at least one student must be on break so we've counted we've accounted for that edge case that will cause us to get a bind conversely though we should be able to check this no problem and it saves it and then we're back here and we should be able to uncheck this that's not a problem it's only a problem when we uncheck both and it puts it back and at least puts it back here okay cool all right one more condition and then i think this will wrap up the feature actually ah good question kwetlin um django upgrades depends on how far back you have to go um if you're looking at like super super old django and i'm talking like um like 1.7 time frame or something like that you're in for a rough ride there's a lot of change that has happened over that time period um like the migration system switched you know at some point when there was a migration system that wasn't part of core django it was called south um same author uh andrew godwin um but he built it for django and built it into django and there was a bumpy path there and um along the way for each of these different point releases on the one.x series were some pretty sizable changes um the jump to one eight was pretty challenging the jump to 111 i think was another one that had quite some challenges to it if i'm recalling i mean you know no upgrade is necessarily super easy but my take on it these days is that the upgrade process has gotten much better so i think the team that has worked on django has like gotten extra diligent about doing upgrades um so you know certainly if you're on a like this app is on the latest three um because it's the easiest thing to to go on to like i've got a valid test suite um there's a lot of compatibility and there's yeah it's just very stable to uh to your point though like going from django 111 up if you're already on django 111 i think you're actually in a pretty good spot um that doesn't mean there won't be stuff to do the switch to django 2 will have changes like url to path and re path if you still need the regular expressions um and there's some other changes like they're they're the django at least in my experience the django guides that include come with the releases um that have the release notes about what to do are very descriptive of here's everything that we think has changed in this time period that that we think you're going to have to pay attention to the other challenge that to answer like the other half of your question probably depends on how much or how many third-party packages you have installed if you have just a couple then you're probably in pretty good shape um especially if they're well supported ones um like django olaf or any mail or you know things like of that nature that are very popular you're not going to have trouble with them it's when you have really obscure third-party packages or if you have forked packages on your own and made your own custom changes that might require a bunch of extra work to get those packages updated appropriately but yeah i think it's i think it's pretty doable um once you get and i think from from to onward that the path to upgrading had gotten way easier so like the the team at least from my observation i don't know them directly or their experience with some of these things but from my observation the people working closely on django django core dev and all the developers there have just really lasered in on what it means to do an upgrade and make that process as painless as possible so the 1x each of one of those was a little bumpy and over time got less and less bumpy there's a little bit of a jump to two but then from two and beyond um like i don't even remember the details with upgrading to it's been so uh trivial um in my experience it's not to say that if you use certain features there might be things to deprecate and there are deprecations and things that you have to clean up but i've i've just found them to be pretty manageable hope that answers your question all right there is a condition here that we have missed perhaps that we need to factor in or although maybe not i'm going to talk through this and and maybe we don't actually need to do it so if when i've presented this this app before so in the app it is possible to build a school year for the future that has no students in it yet so like you can build out you can see these grade levels and courses and stuff you can build all that out and not actually attach a school to it yet but here you can see this message there's no student enrolled it's possible to build out the entire school year with no student no students enrolled at all oh good i'm glad you're on 111. i think you're i think you're in for a much smoother ride than than you might fear um if you're if you're on one eight i would have said like buckle up because it's gonna be bumpy but glad to hear that's where you're at okay so what i was saying is that the school year has this possibility of building out the entire school year with no students enrolled and so that means that we have to make sure that it's still possible to add a school break that we won't get hit this error but i think that's definitely the case and and here's why i think that's okay because the many-to-many field is only going to be possible to populate if you had students to begin with and when it gets created that some of those extra instances were created because this is only ever used in the save part of this particular form and it's only going to add them if there are um if they're particular students so when all the students are on break then it clears out the form and if there are no students enrolled then yeah i don't think it i don't think it comes into play so i think that convinces me that that to do is not going to be necessary maybe my logic is off there but i feel like that's the case all right so i think that gets us to the end of this feature let's check it out let's check out what's left so we've got this extra guard clause here or this extra clean check i should say that adds an error that makes sure that that case when we cannot clear out users when there's existing users is never triggered and then we have the test that asserts that that's the case and we've got the setup here to prove it um i am going to come in here and delete this line i like to have all of my setup portion be grouped together in a single line if i can alright a single unbr unbroken set all right let's run the coverage stuff before i push up and make sure that i'm not missing lines of code i think i've covered everything but this is a good sanity check to make sure that since i'm the only one running this project that my test suite is doing the best it can to save me from myself this only takes about a minute or so once it gets going so there's some anticipation as a pi test spins up figures this out okay 100 coverage great and all of the pre-commit checks pass that's good news and then we'll say um fix remaining to-do's on on school break student filtering feature and we'll push this up this is on a branch i've been doing all this work on a branch because i didn't want to block the main line of the application that allows me to do package upgrades i have my my repository set up to give me package upgrades at the first of every month i'm using dependabot to do that so that keeps me on the latest stuff generally or only with a certain amount of lag and um i can isolate this feature i could change this feature so that it's behind a feature flag but frankly i i don't really want to i know that sounds terrible with me but i've been working on it and testing it for so long and with writing so many tests that i'm not concerned that if i deploy it it's going to have be problematic for users i think it's going to be fine the way it is um so here we have the the pr pull request that has all of these changes in it and this is the culmination of many many weeks worth of changes um what i'm not going to do is make you go through this with me i think what i'm going to do instead is is review this offline so this feature is almost done but let's just talk briefly about here are the other things that i think i need to do if i added this feature and i didn't tell anybody well there's there's only one person aside from those that are watching this and myself that would know that and that's my spouse the person who had requested the feature in the first place i could told her tell her it's out there and then for everybody else and i do have actual users they would just have to stumble across the feature and that's not that's not great so what i want to do is um kind of announce it so i have a way to write blog posts with my app so the app is at the school desk app and there's a blog associated with it and i can write content post here describing here are the new features um and i've done that for a few of these and you can see that i've not written one since may so it's been a while um kind of keeps people reminded that i'm actually doing stuff here by itself nobody's gonna read this blog on their own nobody's coming to this page and checking to that it's there but i have a feature within the app that allows me to put a little present image here that shows that there's something something to click on and maybe i can show that quickly um so that would be in the sub nav um it's what's new so let's just invert this for a second just so you can see it and if i oops this is the real site not gonna see it there no money no matter how many times i refresh let's bring this back up and we'll refresh and you can see this is so this is what a user will see and they'll see it if they have a new um notification and that notification is something that i associated um with the blog post so i add a new um it's one of my apps actually we can go look at the process here so you can see it to completion so this is just a way of like kind of marketing within the app to keep help with the retention of those users that i do have so i've got this notifications app and i can build an announcement which is what this is and the announcement takes a url and has some status associated with it and there's a task that runs or a little command an administrative command and what that'll do is if i create an announcement and i post it and i say it's announced it'll create a bunch of notifications the notifications will be to anybody who is an active user of the application so that their paying user or they're in their trial period it will get a notification so it'll show up in their page like this when they click on it it will take them to that blog post and also at the same process of if you notice where that's going it says notifications what's new at the bottom so that what's new page will actually clear the notifications and set it so that the next time they return to this page they won't see it again so they'll get the benefit of taking to the blog post for the one time they can read the article directly and then they can return to the application uh and this will be gone and so their their experience will no longer be disrupted so it's just kind of a nice way to like get somebody's attention and it is in all the the application is the one setting that i have in the app so if people really don't want it they can turn that off um so that's that's my plan for after i get this feature merged and deployed um i can put together that blog post and notify my app my users that there's this new feature so that's so that's what i'm going to do with this this has been a long time project and after this i probably need to do a round of some bug fixing because there's some things that have been coming in from my users that are things that have been requested that i have not been paying attention to because i've been working on this on the stream and so we need to get through some of those and finally once that is done we'll be able to return to actually some of this so the reason that i even did this in the first place is my spouse would like the ability to have field trips in the application and a field trip if you think about it is not that much different from a school break and so since the school breaks are already factored into the scheduling logic i want to essentially reuse the school break and put a different kind of description and labeling on them to expose them into the application as a field trip and something else that can be done and a different type of thing that can be added and tracked and reported differently oh thank you very much kwellen i'm i um i appreciate you sharing and uh yeah i'm grateful thank you so i think that's going to wrap us up for the the evening hopefully you will learn something about django tonight get some insight into this design process if you have been watching this feature um it's taken surprisingly longer than i wanted to but uh i've only been developing during this stream so i basically have been working on this app a couple hours a week that's just the nature of my life right now because uh kids are doing soccer and all sorts of activities uh that are outside of school so life is busy um appreciate you liking or subscribing or leaving a comment or following or wherever you're catching this from and want to thank you for tuning in and with that i wish you all good night take care
Info
Channel: Matt Layman
Views: 135
Rating: 5 out of 5
Keywords: twitch, Python, Django, SaaS
Id: jfA9pQ7HU7I
Channel Id: undefined
Length: 94min 28sec (5668 seconds)
Published: Thu Sep 23 2021
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.