Finishing Onboarding - Building SaaS with Python and Django #81

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hi welcome to building sas with python and django my name is matt lehmann and i work on django apps on this stream and we're gonna continue on the homeschool application this evening my plan is to work on uh the onboarding flow of that i'm getting close to the end and i've got a couple more forms to do some polish to add to it and then you know just overall fit and finish kind of stuff so we'll get to some of that this evening if this is your first time joining thank you we work on web development and stuff and particularly django and python so you're free to ask me anything about python django or web development that's what i'm here for and you can follow my journey here as i work on this app um i have a bunch of ways that you can get in touch with me if you want to in the future including um a website that's linked above there at matt lehman.com it's got a mailing list where i write about python and django type topics i have a social media presence on twitter and i've got a github and all that sort of stuff usually under the handle of mb layman and if you're enjoying the content and you want to contribute financially if you're able to do so that would be awesome i have an account on patreon patreon.com mb layman where you can help out that way it helps cover costs of stuff that i do like podcast hosting and all these other things that i try and do for the django community i do want to shout out to my current patrons of my superhero patron of rupert and then my other patrons of abdulaziz patrick and eric you guys are awesome so thank you all right let's get into it so what what are we working on today i've got this onboarding milestone that i have been working on i mean look at some of the age of these or back in april now so i stopped working on them to focus on other features that my spouse needed as she was preparing for the school year because she's the primary customer of this thing but i'm coming back to these things because she's kind of in a mode where she's ready to input her data for her upcoming school year because we run on the calendar year at my house but not quite ready to get started yet so i have this intermittent this window of time where i can do some other things that i think are important uh hey quad quality welcome thanks for joining in again um so the uh the thing i thought i'd do is get back to the startup of of the app so when you first join the application um the plan is to give you kind of a walk through of here are the steps that you need to get the minimum amount of data to start getting value out of the app to see how it's going to work in practice and to kind of introduce you to the basic terminology that are important in the application that i'm working on so i'm going to fire up the application we can take a look at what that looks like today and we can see the piece that remains to to do hopefully everything's working i kind of blew up my environment the other day with some brew updates but i think i got it back to a healthy state uh we'll we'll see if that's true in a minute um web server's taking some time but all looks good there um yeah that's a good question my dot files are public on github so if you go to github mb layman and dot files and you you can see how i've set everything up um my scheme here the basic strategy for my dot file setup uh quality is is looks like this it's uh it centers around this bootstrap script there's a bash script that i run and what it does is it kind of checks for some of the tools that i require and if those tools aren't present it just basically gives an error message and says you need to go install that so it's kind of a low-tech version there are ways that you can get really fancy with this and have a script that goes out and and installs everything but uh frankly i don't i'm still running on the same laptop that i've had since 2014 and so this one just kind of keeps me honest of here the things that you said you need to run some of your tools and they're all in here so i run that bootstrap script to do that part and what it does the strategy of it is to takes my clone and creates symbolic links to directories in my home directory so my this vim directory becomes dot vim in my home directory and and so on so that's how it works out and it works pretty well for me but you're welcome to check it out all right so let's look at the um the app and get in there uh the way one thing that's going to be true of this app no matter what and people probably won't recognize it that much which is fine i don't i don't anticipate them coming back to the onboarding experience is that these start pages will always be accessible headcaxitas welcome um we're doing some uh onboarding development here so if you've got any django questions uh shoot them my way i'll be trying to answer them so we have this flow and this flow has got four major steps and you have to remember that my application is all about homeschooling so it's all about setting up your school year um for for students presumably you're homeschooling some kids that live at your house or you know are in your home whatever you know what i mean and getting them set up uh getting a minimal setup so that by the time you're actually clicking around in the application views there's enough data there um so this uh this flow is a it's kind of an introductory letter you can clearly see i'm not quite done with it there's some obvious call outs here but it sets up school year it sets up the first grade level that someone wants to use it adds a course to that grade level because a student is enrolled in a particular grade level and then it gives them their first task and after that they have enough data that if they go to the school year page and see the flow they'll be able to say oh yeah i created that i understand where this stuff goes um so that's that's what this is about most of these steps are already in place and we can kind of click through them um and i think i've i've already got a pretty robust account so a lot of these are won't even be forms they'll just tell you to go into the next step but you have to create a school year like i said you have to create a grade level and you have to create a course i'm sorry i've got a frog in my throat and it's this last one that i need to wrap up so you you look like this looks like the same view and it's because it is right now there the even though it's a different url it's pointing to the same django view um so we need to make this course task thing work and so each of these steps is kind of building off of the previous one and so one of this middle step so there's basically like three three states that it has to think about um so the grade level for example it needs to say like is there a school year yet that's a precondition coming in and so if there's if there's no school year yet it's just going to say you can't do this step yet you need to go to the next one because i'm not going to block people from actually clicking these buttons they'll be able to have this experience where they can kind of click around so there's there's the checking of the precondition and then there's like a post condition check so if a grade level already exists it'll say like hey wait a minute you've already done this step go on to the next one so the same is going to be true of all of these steps so along the way which means that our last thing here will be have a precondition check to say does a course exist and then if there is a task that exists then it will just say like congratulations you're done or maybe i'll even do a redirect right back to where they should be i'm not really sure yet but that that the the goal here is to give them the data and then when it's all done put them on in this page which why did that do that that was weird that should not go oh this is in the past now haha look at that i've been doing this long enough that my end date has passed so normally this thing this button would take you right to this page but because my school year is now in the past that's no longer right so i'm going to edit this really quick because i'm not going to remember to change that let's just make it a two-year school year even though that's ridiculous oops i've got guards against that clever clever me um how about uh just let's do like april for now i'm just gonna buy myself some time for now so a good question so your question is like what is the objective for this um and uh my focus is uh there's a good there's a good couple questions in there um so my goal is to primarily make something for my spouse that's first and foremost like i we we were using she's using a tool currently it's got some stability problems it's got some security problems things that um are not great about it so i'm making a tool that is uniquely suited for her needs as an elementary school educator because that's the age of my kids or they're both elementary age and so that's that's my first goal and the thought is if i can make a good tool for her because i want to put time into making something of quality for my my spouse that then i can turn around and extend that to others so my goal is uh to start there let her have experience with the app um and she's gonna identify all the things that like i need i needed to do this this and this like already what you see in front of you are driven largely by these are the things that she identified this this way of viewing the calendar this way of doing uh a number of these things are things that she identified and so we'll all go through that cycle a few times make sure that it's actually working in my own household so that i have some amount of proof that like i'm convinced that this is a solid product that can be used for other people from there i've i've got plans to reach out to broader communities for example i've joined a forum and have started to engage with some some homeschool communities that exist this is one particular one it's called the well-trained mind and uh you know i'm gonna start interacting with them and doing market research um and asking people and eventually kind of start very small look for a small set of like beta users that would be willing to try the app because you know some of the big unknowns for me right now is i don't have high school kids or even middle school kids so my kids are young i don't know how well the application would work for someone who's much farther along in their education of their children and so that's the strategy to start there and get a small group of people and hopefully after if a couple of families if i can get three four five six families something along those lines to use it as a set of beta users and really knock out all the pieces that are going to be important then i can start um shifting that to to switching from a beta set to a set of actual customers over the long run um and i have some ideas on in how to do that which i can talk through but i'm going to save that topic for for another day but that's the rough plan um my main focus and to finish off your question you're asking about uh where where this is focused on i i'm focusing primarily uh in the usa right now because i'm not multilingual i mean i took some spanish in high school but i would uh i'm nowhere close to that so i'm not going to internationalize this right away um i'm going to seek like seek a set of customers and and maybe there's a future where i internationalize and make it for a broader reach but that that gets pretty pretty complicated in a hurry um not that i don't want a broad set of people to use it but you just gotta bite off one little chunk at a time and if the app is wildly successful then it would make sense to try and expand it to other other regions possibly hope that answers your question um okay so getting back to the application the start flow is going to put people back at a school year when it's all done come on now um firefox changed something about the way they would highlight stuff that is not to my liking anymore anyway so when they when they finish this flow they'll get dumped to that school year page that we just saw and i'll open it up in a second one and then hopefully they'll have enough information they'll have created their school year they'll see some buttons about what they need to do i'm even might even have some kind of call outs kind of congratulating them on you know you're done um and here's what to do next i'm not really sure yet but the actual actions from onboarding and this experience of this step by step by step will be done and they'll kind of hand it off to them to to go explore more of the application if i can so that means that i need to finish this and let's take a look at what else is on this onboarding milestone that i things that i thought about that are going to be important to make sure that the app doesn't feel incomplete maybe you all have experiences i don't know but you can go to certain products and you can get off of their blessed path if you get off of their main path and find areas where they're just like it doesn't seem like the user interface is right like it doesn't understand what's going on because there's not enough data there and that's certainly true of my app today like if i created a new account and didn't create a school year and tried to go to this like week view none of this data would be here so it would be all in fact i'm not even sure that you'd see like some date range so it'd be this really confusing ui of like what do i do with this page so a lot of ways to add polish to a sas application is to deal with those different error states those edge cases that things that differentiate a really crappy experience from a good experience so those are the things that i've tried to add to onboarding here that weak view that you saw that is the main app view that is the the view that i intend for users to live in day in and day out see the schedule of their students but that view doesn't make any sense until you have students set up until you have a school year set up so i want to add different empty state conditions that do a query to check and make sure do you have a student do you have a school year those kind of things and when you don't there needs to be some kind of friendly messaging there that says like hey we noticed you don't have this set up yet here's something kind of pretty to look at probably like a something visual here's some explanatory text of what why you're seeing this view right now what you're missing and here's a big old button that says here's what you do next go go click over here and that's how you get to your next thing so those are what these these issues are about is adding some empty states um i also have this thought of you know we've got this start ui this toolbar um i'm thinking about ways where i can integrate this into other views so like this weekly view this daily view any of these places where they depend on a bunch of data i want to have that stuff um i want to have a call out to kind of guide people along so i want to be able to highlight like this toolbar is not very smart smart right now this banner thing um so if you've already completed like like look at this thing right now let's just pause and observe what it is right now it's on the start page it's on this welcome to school desk but all of these activities of creating school year creating a grade level creating a course they're already done for this user account that i'm on so i want to incorporate some logic into this banner so that it can like highlight this as a done step and i don't know what that's going to look like i don't know if it's going to be grayed out text i don't know if it's just going to jump you to the right point with and give you a message i haven't really thought through that yet but i kind of want the the path to force you along to say okay this is this is ready you don't need to revisit this because even though i have this empty state there like why why would a user want to come to this page like if they had a visual signal that you've already created a grade level they don't need to even click this button again they never need to see this page this is a this is a last resort of oh you ended up here somehow i'm sorry but that's not really what i want to do so this banner is going to get smarter in the final version of the onboarding so that i can and that's what this issue is about showing progress um and i think uh what's this one well maybe they're similar oh oh no this this one is about putting this the banner thing on the main app page and this one is about showing the different state and different information okay so that's all that's left in this milestone you probably all didn't care about that much detail but that's that's what i'm planning to do probably for the next couple of streams is i really want to finish this off and and polish it and make it nice so that when i do ask beta users to come in i'm not scrambling to say like oh yeah i gotta get something that will be um doable because uh the benefit of having your spouse as your first client is like as she was experiencing things she could just say like you're missing this or this doesn't make sense or you know like so it's a different kind of feedback loop um when you have someone who is not in your home they need a little bit more guidance through the app and i'm hoping to set that up before they even get to it all right that was a lot of talk let's get to the code shall we we want to work on this one and this thing some parts of it are are already done and plenty is not so there are all these things need to be here these are the rough list of things that i talked about like all the preconditions and let's let's look at what's there so i'm going to go to the start urls is where i put all the stuff all these views and i've got the name of this last view and you can see right now it's using the same the same view start course view that's why the appearance when i clicked on that even though it was the right url if you looked at the url bar that would have been correct but the actual view itself was wrong so that needs to change but that means that this aspect of creating the url itself is done so let's make sure we have a test that actually can check some of these basic states and then we can start checking you can switch it to the right view and check add star adding the context there and getting these pieces ready to go so i'm going to go to the core test views file maybe test views there it is and we're going to look for the test start course view because that's i want to put it after this let's just add where is this okay so it's pretty close to the bottom of the file and in fact some of these i found that these views are so similar that i'm gonna start from the same set of test cases or excuse me the same set of tests and we'll create this and it's going to complain because it's the same name and the view i'll say this is the start course task view and what is the view name bring up that file again start course task okay and so let's highlight all these again i don't know if i need all these tests yet so this is maybe a little overkill but it's easier to do it this way than the other and i'm just gonna do a fine replace and say substitute start course with start course task so that will change all of those um all of the reverses there so now that that's done we can do run this first test which should pass because right now this is relying on the view and the view has a login required mixer and that mix-in is what's protecting access to the view because i don't want anybody who's not logged in to be able to access this page so we'll take our core views file here and it's getting a little small i might uh well it's fine for now and it looks like i already have a start course task view and it's but notice what it is right now right now it's a template view that's it's right here and the notice also that the template itself is pointing at start course so it's even if i switch to this thing it'll still look wrong but that's okay it gets us a step closer i like making small little changes and testing those like if for example this view isn't doing anything right now but if i got this wrong not only does my editor tell me but if i ran the test it's going to blow up and say like i can't find this view so that's doing these little changes and then testing them is a good good strategy overall we aren't going to need that file anymore because we've we've fixed the url the url is now pointing the right view the rest of the job is making the view correct and ultimately changing the template and making the template correct i'm going to start with the view logic and put in all the data that i expect and care about there and then we'll move to the template and make the the template correct so it's just kind of working through the steps and i want to get close to the data as possible to start so we have this view in fact i'm going to switch this let's do it let's reorient this way that way we get can see i don't know if it's actually better or worse i don't know so what do we need we need probably a lot of the data from the previous test we have already checked that this test should now pass because it's pointing at the right thing and what it's testing is this that this mixin exists so if i delete that for a second rerun the test it'll fail just to prove that it is a failing test if the right condition isn't met so we've got that check that's done the next thing that we want to see is um what is it we want to see let's make a task the task is going to be part of the course this is the way this is the setup of the modeling that i have and we want um we don't care about the grade level in the context but we do care the things that remember we want to check two things we're going to check the precondition there is a course and that's going to be needed if we need to display the form and automatically fill it in and we want to check the post condition of a task that's already created so we want to add we will want to assert that the task is in the context as well so i'm going to call it task and give it that and [Music] this should fail because that task well there's a bunch of the reasons why it fails because i didn't do a number of parts um all right we'll keep we'll carry on i'm not going to make change the the view type yet that's not needed we will eventually because there's going to be a forum view on the course task but i'm not ready for that step yet we're going to work on just the context data so let's take the context that's here and what do we need we need all i really want to do is query for a course i don't care where the user so like let's let's imagine that the user has been using the app for years and they happen to come back to this page i don't care like because the really the realistic flow is that somebody's going to do this onboarding if they're going to do it at all they're going to do this onboarding experience for the first time so all i need to do is check that there is a course associated with that user um the difficulty with that though is that it has i can't just i can't just query all the courses and grab the latest one because that's not going to make sense and the only way that i know that a course belongs to a user is through the grade level because the hierarchy of this it's time to bring up the good old model diagram is the school is at the root so the user themselves the the person that with an account is the owner of a school that is their home school and for every school there can be one or more school years and then for every school year there can be one or more grade levels and the grade level is the thing that is connected to the course somewhere there it is um it's through this intermediate model table and so the only way that you know a course belongs to somebody is by looking at a grade level that's associated with them it's kind of a roundabout way to get to a course but it's just the nature of how the modeling happened happen for this app so we have to work with that uh so we'll return over here and this query that is here may actually be the right thing because um this view was getting the course or trying to get a course based on if it could find a grade level and so a lot of this logic i might even need to like recycle and refactor some of this logic so there may might be a need to have a grade level mix in is that what i want to do let me think through this for a second because you can only get to the course from the grade level and can i do i'm wondering if i can like get get away with not querying this way so what if i did well let's assume that would i don't want to do that i don't i don't care about that strategy right now because i don't actually need the grade level as part of the context let's assume i want to go straight to the course and straight to the ket task so what would that filter look like that filter would be like let's see course i'm just gonna make up what i think is the right filter and if uh if django gets mad at me then we'll deal with it so we'll say grade levels and a grade level has a school year and the school year has a school and the school is going to be the self request this is going to look ugly i promise we'll fix this and like like this so if this is going to navigate the right hierarchy there's gonna be multiple grade levels it's gonna look for so we're gonna say we're gonna tell django go all the way up this stack and go up the school year up to school and the user object in this system has a school property they can access and it's going to take a distinct and get me the first one of those so i think that's going to be right and hopefully that will solve so there should only be a single course created in this okay and that should get this first one if at all if the query is correct great it's it's correct because look where it's actually failing is on this next line in the test on the task so to then get the task so now we're going to do i'm not going to assign that straight to the context i'm going to rework this a little bit let's change this to be a course assignment to a variable and then we'll do a change to there and we'll assign that to a local variable so because this could come back as none because of this first attribute on here so the next thing i want to do is i want to say if there's a course then we're going to look for a task and the because the course is already filtered i can trust this course at this point because of this the chain of to get the right course went up through the courses that belong to this user through the school that means that in other words it's safe to not worry about getting somebody else's one so we have this course and now we're safe to like query on a any particular task i just want the first one it doesn't really matter because again the point here is not um it's not to show some specific one it's it's to say like is there one at all because if they're if one exists then we don't need to do anything in fact now that i'm i'm saying this out loud it's it's really just a boolean i'm trying to think if i want the template to like show the task that was created well it's okay let's just do the task it's going to be slightly less efficient but it does it's not really going to matter so we'll say course task objects dot filter where course equal course and we're going to grab the first one whoops first all right now we've got that and looks like we need to import the course task which is in the courses app it's a course task and now we also need to assign this to the context i'm not going to use it for any other purpose so let's just go straight to the context here rather than assigning it to a local variable okay so and we don't need any of that stuff now this test should pass great um what else should we say here we want this is still a test we want we want to prove so i want to have the confidence and i want automated tests to be able to prove this that my permissions remain true that if i ever like if i were to ever do something silly like delete this line by accident and it filter and just give me any course i don't want that to happen because it's very important to me that my kids data not leak out to any user that might be on the platform likewise i want wanna offer the same guarantee to any users that i have is you shouldn't be able to access anybody else's stuff um and the way i'm that i'm doing that is by making sure everything exists under the school so it's always checking through that permission level so this test that we want here is to the second test and probably the third one as well is to make sure that the task and the and the course that show up in the context they can only belong to the user so we'll rename this to be only user's course and it's going to complain because this one has the same name um and so oh that's interesting hey welcome i don't quite know how to pronounce your handle sicklio glad you're here if you have any questions for we're just working on a django app and i'm writing some tests for some onboarding stuff that i'm doing right now and i'm currently looking at why i wrote something the way i wrote it so this test that i wrote from this other file does not need a grade level that's a waste of creation the test should still pass without that oh maybe there's something else going on there okay never mind so we're going to create a course here and the this course factory by not taking any input parameters is um is is basically proving that or by not taking any input parameters it's not connected to the user that we created so the self make user has no connection to this particular course factory so we're going to then say uh course is none should be the correct assertion and that's true and the course must belong to the user and the all that we care about is the task must belong to the user doesn't even need to be tied to the grade level because again i don't care because if you're at the end of that process if you have any task you have done the onboarding work the only reason that the other ones need to be more exact in making sure that they're connected is because they're in sequence with each other so this one this one about tasks is going to be just the course task factory and we can delete even that so this is definitely going to be disconnected and then task should be none thanks i appreciate that if you have any questions for me i'm here to answer them so fire away i'm realizing now that this task i probably need to in order to get coverage on this thing i probably need an actual course associated with the user because look if i don't then well no no no never mind i have an actual test case that will get to this line so this is probably okay yeah it's not the best test but it's okay that's it's not the best test when it's not working what did it do there context i would didn't even put it in there right yes yes yes because i didn't have the course okay well let's go great create a course then so there's gonna i'm gonna create a course that but the that belongs to the user so that it will go to this path and actually run this line and add the tasks in the context kind of overkill but whatever okay so what do we have we have um we have the happy path test that's what this test is for for we have one that proves the task that the course and the task are in the in the context are that they're not in the context on these error cases and then we're going to have this final test that is actually testing that posting the right data will create a course so we want to fill in the data that matters to actually make the course connection so let's go and look at the course models and figure out what the course task model needs i think it just needs a like a uh name i think and it's been a long time so it's gonna need the foreign key i know that and ah description description is like just the all the information that goes into it and i don't know if i can get away with yeah i can probably the duration is required uh and that's going to come from the course default there's a default duration for how long a something should be and then we can leave the grade level none because this is going to be for onboarding so they're not going to be this is a specific feature for something other else don't worry don't worry about this aspect so these are the fields that we care about course your uuid will be auto-generated it's going to need a description and i think it's going to require us to put in a duration as well in order to pass so here we go we're going to come back to the ok path where it has the course that belongs to the user and we're going to put that there and the data is going to be description which will be my first task i'm not being very creative about that i don't have to be though and the duration we'll say um um what we say we can say the string value of course default duration and finally what was the last thing i said we needed well we needed the actual course itself so foreign key to the course so that's going to be course stir course id all right so this test will fail it's not going to do a redirect oh i'm not even going to get the right attribute default task duration okay all right let me take a look at your question here um you've got a question about creating a sas for data analytics uh okay well what's your question i can't promise i know much about data analytics or making specific bi web pages but i can try and help out where i can you're not able to show different graphs okay so do your graphs the first question i ask is like does your graph i'm assuming you have some kind of graph model and you're pulling you're pulling data about about them so you need to have some way to say that my this user owns this graph data unless it's like shared so that it kind of gets complicated on what you expect your app to do is it individual users own the data or is it like an organization owns the data and a user happens to be part of that organization in either case you're you need some kind of thing that is the owner whether it's the individual or the or a group of people it doesn't really matter and that thing would have to own the data so that's how you would differentiate between different graphs is like you so you can't um if if you expected this this notion is called multi-tenancy multi-tenancy um and it's the idea that you have multiple people that can be using your application uh in order to um segment that properly in order to give everybody their own space you have to draw those lines yourself there there are tons of ways to to do this and again like as you pointed out we're kind of working on something else i'm not going to dig too deep into that um but uh the way that i've done it in this app and what i've kind of shown is that you know my users here can only get to a course because they own it through the relationships that are there in other words through the fact that they own a school so by a course being part of a school a user can view that so i don't know how your application needs to be structured or what the modeling is but at some level you have to make your data have a notion of an owner again a group or user it doesn't matter and once you once you can do that then you can get the appropriate filters in there and have the appropriate foreign keys so now if you can say let's just do run through the two basic examples if you have a user and the user is the owner of the data so you have a foreign key on graph of data i don't know what your data looks like so i'm just going to be kind of hand wavy about it then you would do a filter that where the filter is the user and you get the right date range i don't know what what instance you you have or whatever but that's the way that you could display the graph for that particular user um the other scenario where you have an organization and you first probably find out the user's organization query for that first get get their org and then get um do the the foreign key query on your graph data model um yeah so in that scenario you get the group first and then you look and see like okay this person is part of this group therefore they can see this analytics data and this is still simplifying it like you could have really complex schemes of even within an organization different you might have different teams like it this can get really really crazy as you you might be able to imagine um so i'm just kind of trying to outline what i think are the two like our two really prominent strategies um but another in either case they involve like you have to have that association between something owns the data and then you need to be able to draw the line of like can i filter that or not i hope that helps a little bit all right getting back to this post test um we've got the fields that i think are going to be important um but this thing should fail [Music] and the reason it's going to fail is because this isn't a template view so it's returning the 405 response which is the http response code for http methods that a url does not handle and it's even giving us a friendly version that says hey i only handle these three methods and that's because i haven't made this into a view that i can actually take a post the view as it exists right now is only a template view and a template view is designed to be a view that you do a get request on it's designed to show you information so if we want to create a new thing then i need to use a different kind of view a create view that will give us this extra stuff that we care about the downside of doing this as you'll see i'll try to rerun the test is this going to say hey you don't have this configured properly uh it needs it needs more information so it needs like a form or it needs a query set or a model it needs something else because your your want what you need to tell django what it is you want to create so we are going to change this thing we have a create view create view um i have a form for some form classes that i'm doing and the reason i'm using form classes is do to do some extra checking to kind of guarantee that when somebody tries to create a form that they have permission to do that for example some random user should not be able to add new tasks to somebody else's course so the the form does the appropriate checking to make sure that that's uh not allowed which means i probably already have the this form available and i just need to go get it because i'm creating uh tasks in other parts of my application so here it is the course task form oh it has even things i forgot about here okay we'll still use it anyway so we're going to come back into the to the view and we're going to add the form class and i don't want this form class i want the task form and i haven't imported that so we need to come up and import it okay so that's step one the other thing to note is um this particular form requires the uh the user to be added to it because i do some stuff in the clean method that requires the user attribute and it just needs to be there uh so the view probably no this one takes a school year there's going to be one that takes a user here we go so i need to use this get form keyword arguments that is a method that is available to create view and what that says is that it is the arguments that will be passed along to the form when it's created so that will help the the user um it'll help this to work so actually let's try running it without see what's going to happen it's going to fail and i'll see if it fails in the right proper way it might get some other obscure error nope nope it didn't did what i expected so when the form is created it gets the keyword arguments and it tries to look for the user keyword argument um and it does a pop on it which is basically saying like throw an error in this case a key error sorry drink some water sorry about that um in this case a key error if it's uh if it's not present so by adding this form keyword arguments what that's going to do is the create view is going to take whatever dictionary you provide here and it was going to pass those along to the form which will help it get past the init method which expects the keyword argument of a user so now if we run this thing um we're getting closer but we still are missing um so it's actually looking like it's gotten through and created a a record which is good um but it's saying like it doesn't know where to redirect the user to and it's doing that in kind of a kind of a weird way i guess it's there's a couple of errors here and so how how do you how do you read this there's a lot of stack trace like look at this thing your first one is this attribute error which seems weird and then it says during the handling of this then you get down further and there's another big stack trace that is a bunch of stuff and um and then you get here which says you know it's actually this thing um so i don't have good great advice here my best advice is like scan through the errors like and if the first one doesn't make sense go to the next one like you might think you might encounter this and say oh i guess i need to define get absolute url which is a possible answer but it may not be the answer you want because this answer has implications so you continue reading and you read that it says um oh okay well this is actually a different it says it's improperly configured you either you don't have a url so you either can do the get absolute url thing from above on the on the model or you can do something else and the point is is like be careful with error messages and read them all because they're they're going to teach you different things um i i don't that's kind of my my uh soapbox lesson for the day hopefully you all can indulge me on that but um the thankfully i know i know the framework really well and i know that what i'm missing is this thing right here so when what was really happening there it was trying it was going through the default uh implementation of get success url and get success url says look for this get absolute url attribute on the model if it exists or throw an error so you can either pick from a default for the model instance or you can do something special and since this is an onboarding flow i actually want to do the special thing i don't want to go to some particular course that's not i want to control that this direction a bit more so i'm going to put it i'm going to put it control it directly and so where i want to send them where do i want to send them that's a good question um oh check that then check this thing out that's funky do not want to send them to start course task task that was a bad find and replace um i want to send them to the school years view i think i think that's where they want to be um like i said the end of the flow this it should have a page it takes you somewhere else that kind of dumps you right in the school year and that view i don't remember the path to it so we need to look it up it's called current school year okay so that's where i want to send them so let's change all this thing and we'll say schools current school year is the desired success location and let's do the same thing there and do a post hey look at that we've got all the data um so that tells me something as well that the form i was looking at this is graded attribute it's not required so i didn't have to include it and it defaults to false so it's not it's going to create an ungraded task which is fine the other thing i want to do here which i'm surprised didn't do it on the other test kind of silly um yeah that's dumb let's let's stop for a second we're going to go back to the other test to make this test better so i want to find a course that has the name of astronomy and because the way the tests work this uh the database will be free and clear of any other courses it should be so i can just do this of name equal astronomy and i wanted to that to exist that just proves that i actually created a next course record whoops um exists plural and i want a similar check down below here on the post of this task one so we want to say course task and then we want to say description of my first task and it's just going to it's just something easy to query on um i could be checking other stuff like is it connected to the right course and yeah yeah yeah i could do all that but i've i've got this particular form i've tested this thing extensively and i trust django's create view so i'm not going to waste my time doing doing a lot of that so let's go find i need to add the the right model which is where it's up here course task and that should fix that now we've got a passing test okay cool so what's left well why don't we run the tests to see if everything's running okay it should only take about 15 seconds or so the reason i'm running this is i have a requirement on my own application to make sure that i have that i'm testing every line of code and so i have this coverage target that actually runs my test suite through the cover python coverage tool so that it can check everything you can see here we've got 100 coverage so that's good news um but now it's time to now it's time to break some stuff because recall that this thing i said that we need to do after getting the data is work on the template [Music] we so we are using the right form so i can trust i'm going to just go through this checklist of what we need um we have the context for a course and the context for a task so that's all good and now we're ready to move on to the template stuff and we did do the redirect so that's good too so see we're just checking off stuff without even realizing it so step one to do this is i'm going to leave it on this test which is just the happy path test and i'm going to come over to the view and we're going to call this start course task that's immediately going to fail because the template doesn't exist and we got the right exception telling us exactly that the template does not exist exception very appropriately named the next thing i want to do in my opinion the the view is done so we can leave that file let's go to find the start course template which is right nearby where we want in fact it's going to be the thing that i want to start from and we're going to copy all of that and create the start course task template and paste it in all right now we can come back to our test and rerun it and the test is going to pass again doesn't mean the content's right in fact we know for a fact that the content is wrong um but now that now we have the split that we want so even the content even though the content is going to look like the course view for now it won't for very long so i think we're at a stage where it makes sense to bring up the server and return to looking at this onboarding stuff and i'm going to click this last page and it's showing the wrong thing here but we're going to fix this up and the title at the top that you actually can't see because i kind of hide my tabs it says add a course and i want to change that so what i'm kind of been picking these from based on the titles that are in here so this is create a grade level that says create a grade level in the title add a course so i'm going to say make your task as the title or make about just make a task that'll work so make a task and i mean refresh wrong page so it's saying make a task just to prove it here you go there's hopefully you can see that there's the title okay now it's time to change the actual content um we still want to include the start banner i forgot about the banner we're gonna have to we might have to tweak that as well so let's bring up i think we're even we're basically done with the test as well we're i'm done with the tests the rest of this is about getting the template right and i i could how do i describe this so some people might say write a test that does like an end-to-end test that actually fires up a browser and reuses your real form and um gives you confidence that from a test level that your forms are correct i'm not going to that level uh and that's i don't belittle anybody who does that route that's perfectly fine but for the level of effort i want to add to this particular application that's not something i am interested in doing so i'm not going to do that so my satisfaction is anytime i'm working on the templates knowing that it's just me i'm going to be checking these templates as i modify them and i you know short of me doing something really crazy with the html and dramatic level i don't expect that stuff to start breaking uh and if it did if i if that happened in the future then i would be interested in adding actual automation testing that tests some of those things using like selenium or something like that but that's not where i'm at so we want to get down to this last step and i already have this step defined so i'm going to change it to um task that is the name attribute that it's checking for and it's going to re-rend it's going to make it's going to move this button to be over here which it did and so now it's no longer clickable now i could go back to the course one if i wanted and now we have the proper separation on that banner so that part's done okay what's next let's just keep moving down the template um in fact we can i think we're at a point where we can give ourselves the whole template to see uh so this this particular aspect of something it has to do with the alignment of a form uh so it's just about checking the right state so um if and it's kind of like it's checking the pre and post conditions but these are the pre and post conditions for the course view so we want to say we want to know that the post condition which is what that was the task and we want to say or not course then that's when we want to do those this is now going to be the previous this is now the new post condition which is the task and i'm saying i'm calling it post condition because it's the thing that after we've posted after we created it and this is the precondition is satisfied and then this else clause is what happens when you don't have it um so we're going to have to monkey with this because my account that i'm testing with here does have course it does have tasks so it's going to jump straight to the one that says you're done which is not quite what we're going for so let's see what that looks like first it says your first course is ready to go so it's it's checking it's going down this task so really it needs to say your first task is is complete and then we can say the next step is to is to go look at your school year so we want to say it's time uh what can we say you can check out your school year and this one isn't super important because again this is like this is they're already done so they should never be ideally they shouldn't be coming back to this page we're kind of just putting in something that if they happen to come back for some reason like they hit the back button in their browser it's not like it's impossible but it's not something that's going to be super common either so we're going to change the url to the same one that the post redirected to which is schools current school year and that will direct them there and we'll change the text to be see your school year i think that'll work so let's see what that looks like so that feels pretty good um i want to also change the imagery so let's let's look let's go back to the flow and start over here so observe the positioning so this first one i have it on the left and i move to the right and i move to the left then i move to the right then i move to the left i haven't done that yet so we're going to flip these around in a minute uh but i'll get to that in a minute let's let's make sure that the forms and the copy are all correct and then we can go perhaps find a new image that is representative of a task and fill that in and i kind of already have one that i've been using elsewhere that i might just reuse for this one okay so we have to basically for the rest of this form we sort of have to fake it we have to fake we're going to pretend like we don't have a task the easiest way to do that is just say um it's just going to change that to not so actually before i even do that let's um let's go ahead and add these files to the git stage so we don't so now we're kind of tracking the changes as we're going along so we're going to say not so it's going to come down here it's going to go to the hit the lift and because i do have a course right here it's going to be it's going to change us to [Music] this this middle block and the alignment's going to be a little funny because of this stuff so do we need to change this let's try that so this is where i set the copy to explain what a task is what it does and so i need to probably i'm trying to make these as simple as possible and if you saw you saw that from that test that the parameters that it took for the post were only these three attributes that's all i needed what i want the user to add to this equation is this description part but i can for the onboarding flow i can just give them the default duration and i can give them the course that they just created i'm going to tell them the course so that they know what where the task is going to be created and i'm just going to give them a description field that's the plan so we can come down here and we're going to recycle a lot of the form that's here we i'll leave the copy alone the copy is just the paragraph the descriptive text if you're not familiar with that term it's kind of a advertising speak as you talk about your marketing copy um which is sort of weird i i confess but that's the way it is and so we have a course so we're going to plug in the hidden field of a course and we're going to plug in the duration and we're gonna this is where the value needs to come from the course itself so we're gonna say course default task duration well that will fill in that one and the form field that we care about is going to be the description and i need to give it some placeholder text so what should we say let's say uh i don't know this is like how about this because this is it talking about your first task so maybe it's like an assignment to a student to um read chapter one i don't know and that way it's just meant to kind of guide people along give them something that they can they can attach they can figure out what is appropriate for their their scenario and so that's the form so let's refresh it see what it looks like so now it's got this thing that says description um got a placeholder in there create the task let's check out the form let's look at the page source let's go to those hidden fields and we've got our csrf middleware token that's good we've got our course that's also good and we've got a duration that is filled in from the default which is also good this broke now because there's grade level was not in the context so it would have said the grade level but it's not there so let's put in some kind of maybe not so great uh copy and it's just as a start so let's say um how about it's time to create your first task for course course is going to return the string version of this and i like to use not dumb quotes so i get the special quote marks but i always have to copy paste them that so a dumb quote is the straight up and down quote and the regular quote marks that you would read in like a book are curved and stuff or fancy typography fact a dumb quote is also called a prime mark here's there's some random knowledge for you [Music] so i also want to make the course be bold and in your face let's so so it's time to create your first task for blah that your student will do that's probably good enough what does that look like it's time to create your first task for reading two that your student will do sure thankfully i have someone i have a spouse and a client who can look at this and say like matt that was really terrible you you need to go check and change that um so it's kind of good enough but i'm going to get a second pair of eyes on it with some actual someone who's actually good at copywriting my wife has got a talent for that but i do not but this gets the idea across of just putting something out there and making it go um what can we say uh i don't think we need this paragraph we make this simpler and um what and what is or how would you describe the task i don't know because i called it as a description it's kind of lame hmm but it'll work for now all right the really the really the the thing we want to check now so let's check this while we're here so um this is going to go on reading 2 and it'll be added to the bottom so we'll say from onboarding we're going to create the task and it sent us to the school year it went to the right spot i'm i'm trying to imagine like this is my first time using this application this stinks like people are going to be like what the heck why what am i doing now so we need some final message that will say like congratulations you're done you've completed onboarding go do that um or go go and be merry and create for the rest of your school year so let's create another task here because i'm not i'm really not not digging how that's going um so let's call it a celebration celebrate onboarding completion that's really what we want we want to um we want to show the user that they did it that that there's like clear next steps maybe the maybe the even the onboard completion message message could be i'm thinking of making like a big banner that that says like you know big big different box um that really calls it out and can be used to [Music] kind of hint at next steps can hint that maybe go here uh because it is at this point after you've inserted your basic information it becomes a choose your own adventure of what do you what is it what's important to you next if you're someone who is really just experimenting with this of what do i want this what's this going to look like from my school year maybe you want to go straight to creating a student and enrolling that student just to see what it would look like to have them enrolled with one task the other more meticulous kind of planner type of person might say like you know what i actually want to create a bunch of tasks for my student and create a bunch of courses and populate a bunch of data before i go on so no matter what just dumping them to the school year page and expecting them to know what to do next is not setting them up for success and probably would be a spot where you know if i just walk them through you had to create this data and this data and this data and this data and then show them the benefits of that and tell keep them engaged there's probably going to be a pretty dramatic abandoned abandoning of the application would be my guess so let's let's put that in there and i don't think i'm going to get to that tonight but i'm going to just just throwing that out there think through these things when you're designing your own app i think they're they're important attributes what can we say though that the what what we can say is that that form worked um in fact let's go look at reading two uh it picked this random one that happened to be the first course and if we go to reading two and go to the bottom here it is from onboarding and it's got our default duration um the previous task is lying it's not the last task but uh and you can see it's not graded so they they the user would be able to go to their school year go to their go see their grade level immediately go see their first course click on it and get to that but um you know it's it's nice to be able to verify that so let's go back to this view i think that means that i'm kind of i'm done with that aspect for now um although what what's left so we want to do there's one more thing we want to check and it has to be this this last case so in order to even get to this last case we need to add another knot to force it to go down to the else branch all right and so we can return this to this state that's going to put it back in the center which is where we want it and notice that what this one was to remember this came from the course template so this one is pointing you back a direction um so this language needs to be about you don't have a course yet so we need to go create a course so let's do the simple version of this just updating it as minimally as we can before you can make a task you need a course and then we'll say switch send them to start course should be the right place and create course should be the button so i should send them back yep so this doesn't make sense right now because the course that is actually there we're kind of lying but you can imagine that if somebody got to this page and i can totally imagine this happening somebody signs up with the app for the first time they get to this this view they spend because people don't read you know it's like it's amazing human psychology is fascinating so they won't read this they probably won't even see this button they'll be like oh how do i get how do i get to the end and they'll be like ah just let me do this and that's where we're gonna catch it that's where people are most likely to do it and it's at that point where they'll see this message and be like oh okay well i guess i gotta go do this and they'll go back and they'll say okay i guess i gotta go back and they'll eventually work their way back to this first one that walks them through the school year so there's no escaping like you have to do these things um and but having this kind of chain of things that you um that are breadcrumbs of you you didn't do this thing yet and you need to do it is a good encouragement okay so what's um what's left let's go clean up the template so that make sure that it has the right logic for the if statements so we want to take out the not there that's not correct we also want to take up the knot here and i already took out the not for this one that gets us to um this is all correct now but as i said i want this to be a flipped version of this so let's do that and the way that i've done that on other [Music] areas is let's go to the start grade level and you can see that we've got we've got the image in front because this is the flex this is a flex box with it in a row set up so it's just going based on the order of the divs here so we can come to the bottom where this div is take this and come up to the top put that there and that's going to flip that around now the downside of this is that this has margin right so it's on the wrong side i think let's see this let's inspect the grade level one yeah so really what we need is to this be the margin needs to move to the left side that create is creating the space that is between the image and the actual text because it's flush together right now so if you look at the other one you can see if i was to have a line which actually i probably can there we go if i hover over this you can see that the the boundary of the image it looks okay here because of the the shape of the image and the the gray silhouette there but it's really really pretty tight there so we want to change this to margin left of 8 which bumps it over a little bit gives it some breathing room the other thing i want to do is how can i show this i'm already using a default uh an image these these you can tell i've got um create a bunch of these stupid courses that have nothing in them just for testing but you'll just the goal is to show you something let's call it for onboarding the image that i have for a first task is this one and i think i might just recycle it because it is kind of showing what i want which is it's a task somebody it's a woman who's creating things that can be checked off those are those sound like tasks to me which is why i used it here and it saves me from having to go out to undraw to look for yet another image that is appropriate in case you're curious and you've never encountered it before there's this amazing service that's put up by katarina limputz sony i probably always pronounce her name wrong but i'm trying um and she has made a bunch of pretty amazing vector art that is searchable and free and you can customize it to like the color of your app so um for example it's the search engine is powered by algolia here so we can type school and get things that are school-like like this should look familiar i use that as my image that should look familiar i use that so i've used this stuff and i have i just selected the primary color of my application and drop dropped it right in and it's really great svg art to add to your project for free highly recommend it the downside of it is that there are so many of them that you need to find the appropriate one and you can't always find one that's a great fit so i'm going to use an existing one that i've already used namely this one to do so i'm going to say and what i use these alt text things to do that seems good things to do as the alternative text now if we return to the make a task page and refresh hey look at that so that's the kind of the final image that i'll be using for this onboarding so let's walk through the flow really quickly and then wrap up for the evening we've got this like onboarding message that um tells you thanks for joining there's a call to action of going to the next thing and each of these forms if i was a new user account would actually be a form here you're just seeing like you already did this you already did this but remember i'm using a test account that's already got a lot of data there's a form there there's a form there there's a form there and there's less last form and after you complete this last form it will take you here and it's when you come here that you're going to eventually be greeted by something else that is going to celebrate the fact that you did it that's what i'm not going to do tonight so we've got the template for a missing course that was the go back the precondition thing we've got the template for the existing task that was the thing we just saw of you're done go check out your school see your school year so that's done um what else do we have we have we pre-populated the duration for the task i've got some really crummy copy to explain the tasks so i'm i'm pretty happy with this flow this flow is like it's a first first cut so um was it the uh the churchill that said you know plans never survive their first encounter with reality or i'm get i'm totally butchering that i'll look that up later no i'm gonna look it up now churchill i think that's what he said something like that plans first encounter quote yeah yeah no plan survives contact with the enemy there it is why did i say that so i'm bringing up that point is like this is my plan this is what i think will work to help guide a user through the flow but reality of actual users testing it out might say your onboarding stinks and it needs to change here here and here but for lack of that information i feel like this flow of building up the basic building blocks that are going to be needed is the right path to get a user engaged um so that that brings us probably the end of the stream for this evening what what are we gonna do next time next time honestly i'm not sure i don't know how much time i'll have to work on the stuff i may be working on that banner i might have done it already by the time i get there and we might not do it no matter what though we'll probably be working through some of these to finish onboarding to it's not just done when you give them the steps that you want them to do you need to have like guidance along the way to encourage people on keep them engaged with your application keep them feeling like i know what i'm doing i feel like i'm equipped with the knowledge that i need without them resorting to documentation because they're not going to read it or just abandoning your application that's the most likely action that will happen and by having these kind of steps in place it really helps to minimize all of that there is other stuff that could be done probably like i don't know you could it could probably be gamified which i don't really want to do like you might see these on some apps that have like a some kind of progress bar of like here you're all you're almost done you need to do such and such task and you'll be done with everything you need um maybe that's appropriate i'm not going to invest that much right now i just want to have remember this is all in service of getting my first beta users working and i don't want to guess any anymore about what they might need until i have some people go through that process so i'm going to stick with my plan of what's on this milestone and then declare declare victory from there i will take this content and post it up on youtube as usual hopefully you found uh some of this valuable this evening if you did give me a like on youtube or a follow on here is that the right word follow subscribe i never get them straight give me a following here i appreciate it thank you for tuning in and i will see you next time take care
Info
Channel: Matt Layman
Views: 407
Rating: 5 out of 5
Keywords: twitch, Python, Django, SaaS
Id: FhTrNoW4n9s
Channel Id: undefined
Length: 93min 21sec (5601 seconds)
Published: Thu Dec 03 2020
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.