Crafting Emails - Building SaaS with Python and Django #107

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 am the streamer on this stream where we build django apps and we're going to continue on that this evening i've got a project that last week i talked about maybe not finishing up here but you know a week went by and i didn't do anything about it so what better time than the present to finish out a project and explore something different that we don't normally do on the stream so we're going to focus a little bit on copywriting tonight a bit of product documentation probably things that are definitely part of building a software as a service which is what we're about but i normally cover a lot of coding techniques and testing and stuff so it's going to be a little bit of a change of speed um but it's work that needs to be done and is relevant to anyone building a sas project um you're free to ask me anything about python django or web development and that is the theme of this stream and your you can ask me about emails too as we have them um also if uh if you are watching from youtube later on please like subscribe comment do all those things that makes youtube happy that can help this content be discovered by others i'd really appreciate that i also want to take a moment to thank my patrons on patreon if you want to support me on patreon if you like what i'm doing and you can finally financially support me that'd be awesome but i want to thank rupert andrew a new superhero tier patron named dragos and patrick eric phillip miguel and peter so thank you all for supporting me it really helps out a lot i appreciate it okay you also have to bear with me tonight i've got a fruit smoothie so i'm going to not have my usual water but that's just what's going on if you see me sipping from a different kind of cup than normal but you probably don't care about that very much okay let's jump to the code we finished off last time with me stating that we have to build in some email stuff and i think i was at the point where i need to build the content for the email if i recall correctly and get that all going and then configure a scheduler and do some other stuff oh github's got some cool new ui for task work that's pretty cool um so we're here we're at this step of creating the emails and we've got some text emails that i've i've created and they need the copy fleshed out on them my project is not going to use very fancy emails maybe someday i will spruce up the email templates and things but for today they are going to be very basic and that's that should be totally fine we'll go through the process of testing those emails and i'm going to show you the tools that i use to check the the look of the emails to make sure that the text version and the html version both both work out and once those emails are complete we can um set this up make sure that it's going it's all the stuff is protected behind a feature flag so nobody's gonna be able to use it yet but i want this ready to go and then we can write the some of the product documentation that explains how this referral system is going to work so if you haven't joined me in the past i'm planning to send out a referral system or or include a referral system so that a customer can send an email out to somebody that they are would like to try schooldesk the name of the product and it will send an email if they sign up eventually they will offer that person a discount so that's what we've been working on okay let's open up the send referral i think i had done something in a text template and we need to finish it off all right so we've got some this is text and this is um it needs to be formatted exactly as it would appear this is something different about normal templates normal html stuff is that there's various uh a varying output but this is a little different for a text template aside from of course where django template syntax is where there's replacements i think i noted on the last stream is that this url right here if we rendered this today would not actually incur include the domain so we need to inject that in as context to this template so that we can get the link and yeah not have to do duplication when we get to the html part it's a banana strawberry smoothie in case anybody's curious all right so um we have this this basic layout here and this is basically the system that's going to happen the customer is going to be the one that triggers this but the email that's going is being sent out is the recipient on the other side so the customer puts in an email address that they want to send to so really this is the the invitation to that person so i'm um thinking through this in terms of like a from a copywriter's perspective i want this to be um well how would i describe this let me draw some notes here um i guess welcoming is a feeling that i want to go for concise people don't have good attention spans so it needs to be really short and sweet with a clear clear call to action cta so let me put that out in case you've never seen it before call to action and that's just prompting the user for what you want them to do which is is a it's a commonly used expression in building products so we have a very simple greeting and um an exclamation that says that is telling them why they're receiving the email that's really important i don't want to like make it feel like i'm just spamming them is actually coming from a person and so i want to be right off the bat saying like someone you know a friend is sending you um a recommendation in theory a customer who's sending a referral probably has told the person that they want to refer that it's coming they're probably not going to go through their you know email contact list and just email everybody so i don't think i need to include anything about the specific person who is doing the sending which is good because the product um i collect very little information about the actual users so i have the user's email address but that's about it and i don't like i don't want to advertise the customer may not want me to advertise that that email address they might keep that kind of stuff private and i don't have their name um so the best i can do is that says you know something generic of a friend so that's why i've got that early message and i think that's that's going to continue probably and all right so step two is um we've got account sign up here i don't even think i have that over in the management command yep so i noted that this so when this resolves this url template command it's going to look something like account i think it's accounts sign up something like that and you can see that there's no domain there so if somebody's clicking in their email client that's not going to take them where they need to go so this is an insufficient use of the url template so i want to give them something more specific and remember this is the email sorry this is the text template so it will be a literal html or http link hyperlink that's what i'm trying to say um so let's call it the account signup link this doesn't exist we're going to create this context you can see it's empty right now we will build that and put that in the management command but it is part of the contract that this expects so let's do this let's do this correctly um before i want to i want to get down the name to be consistent go ahead and put that in and put a comma on as yeah good the black did not reformat that for me because i included the comma basically to say like this is uh i want this formatting okay so i'm trying to think so we already have the kind of the call to action check out the the link to sign up for your free trial and let's do this and say and and get started with school desk maybe that's all i want to say very simple message nothing complicated there's already a a prompt for why are they getting why are they getting it and there's a simple call to action and then i think clearly they need some kind of sign off so i can say well i i've generally been using my own name as to say that to indicate that this is a not a big nameless company this is actually like up a person who is doing this product and that messaging comes through in my onboarding flows i direct them with my name in fact if you go to the school desk app right now um hey shipments93 welcome back i'll i'll get your your comment in just a second um if you go to the site right now and you go to my about page it's not like it's not like a corporatey about page it's like a letter um to the person telling them like here's what school desk is about and it's clearly got my name plastered all over it so i think i want to use similar kind of language in the emails that i write so we'll do that in a second let me get your your comment there shivens um you may you cool glad you tried out hdmx and you've got a calculator and when you say it's not responsive do you mean that it's not working or it oh is now responsive okay that makes a lot more sense that's really awesome i'm glad you got that going that's exciting good work yeah hdmx i've been really pleased with htmx as a product um yeah it is responsive good good um all right let's so let's finish this off um you're welcome i'll finish it with just the standard sign-off that i use on all my personal emails frankly i'll just say regards and i'll say my name um i don't need to give them my last name at this point but i will say i'll give myself a title did i give myself a title on that about page um i don't know no i just said matt but there was another place where i in the onboarding template so templates start welcome or something like that um nope templates start course let's try this one start page so i also greet them with a kind of same similar kind of letter mentality in the very first page that they see so user flow as they sign up they get a link that or a page that says you need to go confirm your account with an email so that's the typical flow um and then they click the confirmation link in their email and they get dumped to a start page and that's what this template is right here and you can see that i say i've got this sign off here that says my name and founder of school desk and so i think what i want to use is i want to continue using the founder of school desk part as the persona that i want to display obviously i'm the founder i'm the developer that i'm the tester i'm all of the things but this gives you them a clear indicator of who's who's messaging them what service do i use to send emails i use sendgrid i think i might have mentioned this last time but it's a good reminder so appreciate the question um i use it in combination with any mail anymail is the django package that allows you to do this and i think i talked about this a little bit you with any mail you essentially uh you configure your system by pointing you add it to your installed apps you set some details about an email and then you tell django to use the any mail backend for your appropriate service so mine is any mail.backends.sendgrid.emailbackend and it works with all those sendgrid credentials and does all the email sending with the api keys and such so that's my my tool of choice and i picked it because they have a free tier that works for me with really low traffic right now and when i need to pay for it it's also proven to be a pretty reliable service with api keys how do i store them good another good question so let me show you really quickly and then we'll get back to these email templates so let's go to the project settings file and we can find in here uh send grid should be there it is so here's my an email configuration and it calls for the send grid api key as you can see and i have this env function looking thing which is actually kind of it's actually an object that has a callable and you give it a string which is a an environment variable key name so i'm using a tool called django environ which allows you to have this environment and it has this this env object that reads from your environment variables and because i'm using it directly as a as like a function i don't have to define this section right here is for defining defaults but the send grid api key is not one that i want to have a default it doesn't make sense to have a default so i don't give it one which means that if i haven't defined that environment variable for my app then the app will blow up with a key error it'll say like i can't find this environment variable in the environment variables dictionary that's available and so it's required attribute that way so that's how i store the api key got a um so i post a series of articles on django that some of you may have seen before i'm not going to do too much shameless self-promotion but um and i also can't really visit it's weird i do have ads on the site and if i found that if i visit my own site um google will penalize me like thinking i'm trying to like spam them with my own ads when i really just want to look at my own site which is pretty frustrating but i think i'm running i'm running the local development version and um so this is a work in progress but i have a series called understand django where i write a lot about the django it's kind of a story from top to bottom of how you can learn django and my next article sneak preview is one all about settings management so i will have that out there in the future if you want to read that when it comes out you can go to my website mattleyman.com and sign up for my mailing list and i send it out there it will also be on the site of course if you'd rather not sign up for a mailing list i totally get it although i don't i don't spam people it's very low volume only the good stuff um okay let's get back to this uh email generation process shall we so we've got our simple message here we have our wire why am i writing you we have the call to action with the account link and then we have the sign off so i think that from my point of view that is the whole email i want to check there's another i've got emails elsewhere um i want to see how i did the html email and use that as the basis i probably should extract this into a base email so i'm going to pay a penalty here by not doing that but uh when i need it in the future oh apparently i'm already doing it a little differently in different places um okay so let's open up the text version that we just had and in fact that's so small let's put them side by side okay so here we go we've got the same hello i want to change it to that we'll change this content to put that right there isn't the cleanest process there's probably better ways to do this but this is this is fine for now and you can see like it's a lot of the same feel of of how i've done this very simple email over here so we can then say uh let's delete that paragraph and let's add in i'm not going to say matt from school desk i'm going to do the same kind of titling that i did on the other one and say founder of school desk and here we can say what kind of i might need to rework the copy a little bit so check out check out what check out the signup page um no because the copy says all right let's rethink this check out this the link to sign up for your free free trial what can we say instead we can say check out or should we just be direct um let's say you can sign up and that'll be where the link text is um i'm also noticing some bad pros there get started okay you can sign up to for your free trial to get started with school desk and instead of activate url we want to change this to the account signup link um okay how does that look all right so we now have a link we've got some text that the copy the copy is a little different because one's email an html that you just have to i've i've found i've made a number of of html and text in templates in the past and i i think it's good to be comfortable just reworking the copywriting because um sometimes because of just the the mode of you with a text template you're going to see the actual link you just have to treat it differently as like here's this object that has to be considered and explained versus with a a hyperlink in you know a standard html it's very evident that it is a hyperlink in their browser so i don't need to call it out as the thing you have to click or um whatever they can they can figure that out or most people can they they know what a what a hyperlink is all right so we have this stuff the next thing we have to do is is what we need to we need to define this account signup link and i'm i'm kind of saying about it because i'm not i can never quite remember the best way to do this so on the other email template that we had that i was just stealing from a second ago well i'm getting really crazy with this window layout here but you can still get the idea this activate url is actually coming from django all off and django all auth is able to deduce if it is a secure or insecure context and they do that by having the an http request available when they're sending the confirmation link but this management command this place where i'm actually running this and if you didn't join me for the last stream i'm using a django management command that i'm going to hook up into the heroku scheduler that will run on a periodic basis and there's no web context there there's no person that's coming to this page and like at that same synchronous time getting the referral email to go out so i don't have a request that i can use to say is this secure or insecure um what i need to do though and maybe what i should just do by default no no no i need to rethink this all right what i was about to suggest is maybe i should just use https by default and the reason for that is that here is here's my product it's called school desk the domain you can notice is a dot app domain and so dot app must be https um well okay okay but i typed out that message and twitch didn't like that because maybe because of some kind of command the dot app must be https will it accept that there we go okay can't just start a command with a dot and the the reason for that is that i think google is the registrar or something some something like that and they basically have mandated that any um website using the dot app sub um the dot top level domain must be secure by default which is really great like it means i can only do this properly and i can only ever have a secure connection if i and if i don't the app breaks and you know it's going to be on me to make it fit make it work but it means that for my customers sake that they get a secure by default experience i was thinking about using that for this link and building it up that way but at the same time i want to be able to test this out and test that actually works and when i'm doing this locally on localhost i don't have a secure certificate set up so that's not the right thing to do trying to think if i've done anything dynamic i have some examples here where here's an example where i'm using the fact that the request is secure and i'm switching on that and then that's that's one way there so can't depend on that what else could we do we could maybe there's something else i can depend upon well actually this this is a good opportunity to go into into the code and figure out why request is how request is secure is working so what can we do we've got this secure proxy ssl header i'm trying to see if i don't remember if i make use of that or not um let's go back to the settings and look at secure maybe i could yeah i do set it here the secure proxy ssl header i do have that setting oh oh but that's always going to be a static value looking at my way i've got the project settings configured that's never going to change so that's not something we can rely on we could maybe rely on [Music] maybe i just need to create a setting to like i can consistently rely on or or before we do that let's let's back up let's rethink this i don't want to reinvent the wheel like i'm not i'm not the first person to have this problem no doubt so let's see if we can search for it django um what can i search for check https on local non-web context i don't know that's a terrible search and oh no this has nothing to do with it sometimes you just gotta search for things so we have the security topic that's probably a decent place to check let's go down to the https section i mean there's probably something i could do so we've got some settings here that are about security but they're they're also about other things and i'm afraid that i don't want to like mix up a setting and make some chunk of code depend on some random other setting that just happens to be in a secure context i think that's it doesn't really speak the right meaning so i think what i want to do instead is um is maybe i put in an explicit setting or and that i can essentially define for my application like a is it in a secure mode or not is what i'm going for so let's do that and then what i can do yeah yeah let's try that so let's come into the settings of this app and let's give ourselves some more room and i want to say right here is secure and i want that to be a boolean and i want it to be true by default because i try and make my application settings reflect the production app as much as possible because that means that if i deployed to my live site there's less for me to configure there's fewer possibilities to make a mistake and so that means though that when i'm working in a different mode i have to opt in to the more insecure behavior so i think that's a reasonable way to go and the next thing that we can do is we can go to the environment example template this is where i define all of my dev settings and you know these are what you see some secret keys and stuff here these are junk these are dev only settings so i'm not scared of showing these off and we can come in here and we can go to the and do it alphabetically and put in is secure and we'll say um off is the way to to do that so that it will be false by the django environment project that i'm using so that's that i need to put it on secret mode for just a second because i have to apply it to the existing environment file that is actually read the example is just something that i can commit to code but that's not what actually is that the local host is really reading so i need to put the equivalent thing in the actual environment file but i can't show you that one because that one does have actual secret data in it and i don't want to like show you my send grid api key for example i'd rather not have people sending me uh sending emails with my stuff or have to rotate keys or all that okay so now i have um is secure set to false locally and now now that gives me something that i can depend upon in my code that has the submit the right semantic meaning that i want um that allows me to in these non-web contexts to um to do what i want to do so we can come back to this code and we can say get rid of that and we can say the scheme is going to be https um if settings is secure else it will be http colon okay i need to import the settings so from django conf import settings and that gives us the scheme so that's part of this so we need the scheme we also need the um the domain wow jeez how did i did i did the with the site here dang it ah psy oh maybe i can do the same thing get current request comes from django contrib sites shortcuts okay django contrib sites shortcuts import get current site i wonder if i can do it without providing a request object though [Music] all right let's go take a look keep keep digging in tonight which is good sometimes you just got to explore some stuff um documentation sites framework is what we're looking for and get current site it takes a request showing it takes a request here all right what am i doing i got the code let's go look at the code contrib sites shortcuts it definitely is taking the request object and it's saying all right so it's calling if it's on which it will be get a site object from the request okay let's keep going we've got the models where's get request or no get current it said get current now they have here aha well this is very deceptive so it doesn't actually like really need the request if the site id is defined and i know that my site id is defined so i think i can do this but just by passing with request equal none it's going to look pretty weird but it should work okay so we want site is equal to get current site request equal none yeah that looks very gnarly and then we need the url url will get from a reverse on account sign up i think it is all right so now we have all the pieces we can do an f string and we can say scheme and site and url and we guess we need one more we need the actual reverse lookup so let's go to where um verse is in is that a shortcut or is that in urls i think it's in urls if i'm wrong we'll find out quickly django urls import reverse reverse import yep there it is okay so we have got everything that i think we need man that is that is a ton of work though to get that kind of stuff that's pretty ridiculous there's maybe an argument for me for for my app to refactor that and pull out that thing as a function that i could use in a future place because that just seems silly to do that any time that i want to do a full look up we'll do one step at a time we'll we'll use that as it is make sure that it's actually producing the right url and then we'll extract it into a little function that i can use over and over because i think it will need it in the future all right so um what do we need what do we need to do the to-do is done we want to check the test [Music] and so let's go to the actual test that we had we want to test the it's in the test command and i just want to see that it still actually sends that we didn't blow it up whoops blew it up settings object has no attribute is secure you're right it doesn't and i have screwed up okay and i know exactly where i screwed up all i did was i defined it in the environment uh i did not define it in the actual settings so the env object knows about this thing but the application itself doesn't so let's come down to this area where i have like actual app settings so i can group it as django settings and then third-party settings and then i have this app settings um so let's put it here and say um is the app in a secure context or not so is secure equals env is secure all right let's try running the test command again oh okay yep yep i took a guess at the reverse it's wrong i believe it's actually an underscore i think um django olaf does not actually have namespace of any kind they just prefix everything so it's not account call and sign up it is account underscore sign up okay we've got a passing test command cool we've got the new setting in there that's also cool and i think i think now we might be ready to actually try and try it out so let's do a couple things yeah let's do a couple things so we can um what can we do i'm going to have to run a management command with a certain email parameter so that we've got um i have this mode where i can say email testing and by setting that variable it's going to turn on the app for a mode that will send email to an actual email a local email smtp server and i have such a low local email smtp server with a program called mailhog so mailhog will run it's going to run on port 1025 and there is a little app that we can look at on 80 25 here so essentially there's a there's a server that's listening and it's displaying to this client here that are connected this is a good way to test out emails and so what i need to do is uh two things i guess i want to make sure that there's actually a referral that can be sent out and what will happen is it will send the referral to this smtp server this local web server sorry it's not a web server this local smtp server which is an email server and that is email server was just going to like display whatever it got so we should see that it as if we sent it to somebody else and what what they would have seen um so we need to run that command but we also need to make sure that there's an actually a referral in place first so let's let's do that let's bring up the actual site and head over to the site i'm going to replace the live site with the test one and we'll go over to the django admin and look at the referral section and they're both in the sense state so that's not going to work so we need to switch this one i'm going to go actually go into it we need to put it in a pending state this is the state that it will actually send something out and i'm going to say save and continue editing i'm going to leave this up because we're going to try this multiple times potentially and i want to be able to reset this pretty easily so let's open up a new tab let's turn activate our virtual environment and we're going to say email was it email testing yep email testing is true and we want to run the management command the management command is called send referrals it doesn't take any parameters and what this should do is send one email and it says it sent it that's great now if we go over to mailhog here it is and we've got oh i might have already messed this up oh no i did put in the subject so that's good subject is right there a friend has invited you to try school desk and here we have the actual html content so you can see the thing we just wrote it's a very simple email they're not not a lot going on there and that's probably if if i do make a fancier template in the future um it would probably be to use um a different font kind of just clean ups maybe some maybe add some coloring that's more aligned with what the actual application uses but i'd really i don't like super image heavy emails so i never really want to do that um i just want it to be look classy i guess is what i'm going for and and simple because again the whole message of the app is simple so if i hover over this the link you can see in the corner is for port 5000 account signup and so let's click that and i was already logged in so it redirected me the signup page redirects me to the actual app so there was nothing to see there um i could log out but that would be a pain because then my admin wouldn't work and you know it went to the right place it just did a redirect does the does it show the redirect no it doesn't okay that's fine so i'm convinced though that that link is correct and then we've also got um well here's the full link it's just proof positive that the link that i built is exactly what i expected it to be it's using the http scheme it's using the site that is defined here uh which is uh coming from an object that's that is defined and if i went to the real app you could see it but i'll show it on the django admin here so the sites framework has an object that allows you to like define the domain oh that's that's a good thing actually go check out the real thing i actually need to make sure this this is right and it says that for a local host it's on zero zero zero port 5000 which is what i use for my testing on the real site it should be www.theschooldesk.app and i want to confirm that right now i gotta be careful actually you know what i don't know what i might stumble upon so i'm going to just turn put on secret mode for a minute i'm going to check the real thing to confirm that it is what i want to be but i don't want you to actually accidentally see like customer info i don't expect you would but um yeah best the best to play it safe there okay i can show you this now there's nothing nothing really oh yeah there's still some sensitive stuff on here the the domain name i'll just verbally confirm it is www.theschooldesk.app which is what i want it to be so it should produce the links that i expect okay um and all i saw was virtually the same thing here and i just wanted to not not not show off more than i needed to so we've got this very simple email it's all done the link is correct i've seen it in both places i don't even think i want to write more of a test than that like this test i'm not checking the content maybe i could be maybe i should be i don't know i could like dig into send mail and see that the overall url is in there but i don't think i want to do that so instead what are we going to do um i'm going to call that done which is great and we're going to do a little bit of refactoring now to get out the the url so we will test this a little bit here and so the way i want to treat this is well how can i treat this i can write this function for now such that it just expects the um the route name so in this case account signup that's going to fall over though if i ever need to actually pass along the args and keyword args that go into route maybe i will do that in the future but for now i'll just keep the function simple and and make it serve my needs and then make this more advanced in the future okay so we don't need mail hog anymore we're done with that and we can come back over here and and do this refactoring so where do i want to put this good question i think this is kind of a core utility so i have a core app and the core app is for stuff that is core to all the apps like a shared kind of functionality this feels like that to me and this is a essentially um what am i trying to make here what like what is the function name that i'm trying to capture and really what i'm trying to go for is it's a reverse function but it is a full url reverse i think that's probably what i want to call it so i think what maybe i could do well i can't reuse urls even though that would be the most logical name because that's what it's called over here it's from django urls but the core app already has a urls file and it's different it's the actual set of routes so what's a better maybe site um i don't know i don't want to think too hard about the naming here we'll call it site because that's kind of what it's going for it's like a function that is for the in the site context of the whole whole website and so we'll say um full url reverse and this is the url name generate a url that includes scheme and domain name site name to kind of hint that it comes with the sites framework okay then we can take the code that's over here and plop it right there you can see it's missing a bunch of stuff so we'll fix up the imports it's missing settings it is missing get current site it's missing reverse and then it needs to return um [Music] return that f string of scheme site url okay now in this area we can call this function so we can say from homeschool core site import full url reverse and then we will replace this all of this code right here with full url reverse of account sign up and then we can delete these three which then means that those things that we just imported a little while ago are no longer needed i think that has a nice effect there it does the cleanup we can go check really quickly that this still worked we can say i refresh this page that's sent so put it back to pending and this time what i'm going to do is run the management command manage send referrals and we're not going to go to see through mailhog this time but it will use the the console email backend which is what my localhost settings are set to do so we should be able to find the url in here just to confirm visually that um it didn't change and it didn't it's still there so that refactoring was good probably should have a test for this uh yeah i think we're done with the actual command for now so let's write a test for this so core tests test views that's fine and we'll say test site we'll make a new class from home school test import test case class test full url reverse which is going to subclass from test case and we can do we can do what test builds url the utility builds a url that includes the site and the scheme and site so i want to use i want to do from homeschool core site import full url reverse and the test is going to be probably a one-liner well we can give it the we can do two so full url reverse and we'll give it terms like the terms and condition site that's a page that's not going to go away and i want to assert that the actual value url is equal to http colon i don't know what site is going to be for the test stuff but it should be something like that all right that's going to fail unless i happen to guess it right example.com oh that's not good oh there's a bug the bug is that i didn't actually connect full url reverse to what it's supposed to be connected to which means that the send referrals thing that i did is not right account sign up well it's it's right in that it just happened to have the right value but this is why we write tests because that was wrong so it's not site.com it's example.com that'll still fail so we need to change the actual function instead of this we want to be url name great so i hard-coded this for a reason and the reason is that i always expect my test to run in an insecure context i'm not going to set up each you know asserts and https for that so it's always going to be http the example.com i'm pretty sure that that comes from the pi test django setup i believe it does a site auto use fixture maybe i'm wrong about that but i think that's where it comes from example.com i don't think i define that myself uh yeah it's coming from somewhere else and the actual terms part the last part of the url is a page that i i have great confidence that will be there but that was a pretty easy test to write let's um take a look and i think that might that might finish off this feature finish it off functionally there's more to do but um it's a good start so let's add all these let's take a look at the diff before we commit oh man i forgot i have the readme in there um hit restore read me oh get restore staged read me okay that's from other stuff that i just don't really feel like explaining right now so we added this new setting to the example we've got this new utility function and coresight and it builds the url that we want we've got a test for it that's great that gives us coverage for it and here's our command so last time we actually did the the rendering of the template so that's we didn't cover that today but that's what's happening here so the content is being rendered by picking the template and picking the two versions passing in the context the context has the account signup link which has that full reversal and we replaced the message of the email with the real message and html with the real html stuff um whoops that student's view i don't want to commit that either or just a second that's from some other work that i was doing we added a new setting for is secure that will be secure by default which is what the app expects to be we've got our html version of this email which we saw it's pretty simple but it worked and we've also got the text version so let me do git restore staged and then say homeschool students we don't want to commit that part either oops not stages staged okay leaving those two files alone but otherwise we're good so this will clear out the the second task we'll say create referral emails and this is for issue 399. okay i didn't run the actual coverage so let's do that and we should be able to get as far as actually like deploying this out to the site um i will try i might have to put it in like a private mode for a minute to show to get to the actual right page on heroku but i'm going to try and get up the heroku scheduler and see if i can show that show you what that interface looks like if i get to that page and and it's got too much sensitive data on it i'll just say sorry but um i'm at least going to try and complete the full arc of this feature but before i before i actually deploy i want to make sure my tests are passing locally github is probably running this too right now well no they aren't because i haven't actually pushed it up um i think everything should be working with full coverage yep okay so we can run i've got a makefile target to deploy and this what this is going to do is going to push out this new send referrals command to heroku it's going to take a minute it's going to build there's actually three stages to my deployment process and i can talk through them while it's going through the first thing it's doing is actually building the css stuff for tailwind and so it starts up a node node.js heroku build pack and that does everything and actually installs npm packages and then just runs the the tailwind command that i gave it to actually build my css file for my site so that i don't ship a huge css bundle it's optimized it's only 32 kilobytes then after that it moves on to the actual blog on the site which is a static blog that is built with hugo and so i use a hugo build pack that is community supported but it's been really real reliable for me um you can see it's already done it only it takes hugo is so fast um built the whole site in a couple of seconds and then here we are um with the python stuff so the hugo part doesn't do any like compression on it on the generated files um so but i wanted to have that compression to make it more efficient for my web server so i hooked into the python build pack and it takes all of the files that were from the build output of the heroku step and i believe it also is going to take the build output of my documentation site and it's going to run them all through compression and try and make optimized versions of that so that when it comes time to actually serve those static pages with my regular django web server using white noise those will be served as efficiently as possible the last thing it has to do is put together the entire artifact that heroku uses they call it a slug is the output and the slug is the thing that gets installed into the heroku servers that that's the actual app that runs um so it did that it's 158 megabytes worth of stuff and it runs a final release command before it actually puts that thing live and that release command runs my django migrations so if i have any new code that is expecting a new migration to be applied a new column or whatever before that code goes live the migrations run to make sure that the database is prepared for that so that's the process the site should now be live we can go visit it check it out here it is and i'm this now i'm going to switch it quickly into the secret mode again and bring up heroku it's my actual app and it's making me log in blah blah blah click on some stuff i'm just in my app not really doing anything talking through it and when i'm on my app in the overview page i'll describe it here there's a section of like showing you what add-ons you have and i have a deploy hooks add-on that sends information to slack i have my postgres database and then i have an add-on called the heroku scheduler in the scheduler is um yeah this should be fine to show the scheduler is where you can configure things that you want to run on a schedule so you can see i i'm currently only running two things on the schedule and i'm doing this in lieu of a background task process for now because that would be an uh yet another machine that i'd have to be running constantly to do background task works work and i just don't have a need for that so you can see i've got an expire trials command for exactly what it sounds like and this is clearing sessions so i'm using database back sessions in django and periodically you kind of have to you want to clean up that table so it doesn't have too too many rows in it and what it does is it clears out accessions sessions that have reached an expired uh date in their record but that's not what we really really care about we want to add a new job the job we want to add is um we want to run it let's see i want to run it not every hour uh let's try this can i do man every hour that's not what i want that's a bummer i was hoping that you could do i had forgotten there was this this i thought it was more like a cron syntax that allowed you to do something more specific um do i want this to run every hour okay let's think through this it's quick but the the reason that the scheduler is free this is a free add-on is the the add-on works they make money with this by using compute time for your whatever schedule tasks that you have and um yeah if i run it every hour that is 24 times a day that it will run which is pretty insignificant but it does start to add up even though it's quick it'll probably be just a few seconds but a few seconds times 24 times every day that does add up to a little bit and frankly at this stage of this i don't think i i need that i think every day should be sufficient i'm going to say every day and we'll do every day at um let's see that says daily at that utc time so we'll do i don't know six in the morning sure why not uh tck three what am i building i'm building a homeschool application for so we homeschool my kids i have two kids and my wife is the primary teacher and she wanted a scheduling tool so this is a scheduling tool that um builds she she feeds it a schedule of tasks that she wants to do and the thing that i wrote was a pretty dynamic schedule that says like okay it's monday you need to do this this and this to get everything done and it has features to know like what days of the week should a class run on so if um if she's not doing science on a certain day of the week with my kids then it won't show up on the task list that's so that's kind of what's what it's about um all right getting back to the job so i'm gonna let it run for every day and just one time that should be okay for now as long as we really communicate that and if i want to change in the future i can just update the documentation to say that it will do something else so we'll say manage.pi send referrals i think that was the name of the command let's verify that manage send referrals yep it's going to run the hobby tier it's going to run there and we'll just say save so there are no there aren't going to be any referrals yet because the feature is not activated yet and this isn't hasn't run yet so this will start running and i could go check that out but later um but that's that's where that is all right so let's let's close out our heroku um we're done done here and i think what that does i think that finishes off this feature so we got the emails created we got the scheduler done um let's actually push up the code that would be a good thing so there should be an association that would pop here on this issue in a second maybe i need to refresh the page there it is and we're done so let's close it okay so i just took over an hour to build some emails so i actually don't think i really want to get into another one we've been going for about an hour and 20 minutes probably a good place to stop so the next time what we'll focus on if i haven't done it and this project has not been a super pressing priority at least on the code front i've been trying to do some marketing stuff behind the scenes but that's a different topic is to document this so i need to explain how this referral process works if i'm in the actual app right now let's show the app locally and not use the real site let's use the local site so i've got the referral feature here refer a friend um that's a good reminder document okay sorry y'all i just saw something it's like if i forget that it'd be pretty embarrassing um update the template to point to the docs that's what i was missing so we've seen this template section here where they provide their email they send click the button and it sends the thing if you were on the live site right now this is this is all behind feature flag so somebody else seeing the site they would actually see um they would see something different let's take a look at what that looks like within off so that's now off we've refreshed the page and that section is gone so this is what it currently looks like and um i don't want to turn this feature on yet until the documentation is there and available so that people can understand what it is so we will write that documentation i also want to have an announcement the django admin is truly a glorious thing like the thought of having to build an admin or i hear about other projects that just use like a sequel a you know not even a database shell that just sounds horrible to me so it's really really nice so that flag will continue to be off on my live site for now and once i have this documented and have also a product post planned and set out and announced i will turn on the actual feature for all of my users so that they can try it um wow they're really like pushing this task list thing like what i don't understand anyway that's where i'm i'm going i so that's what we'll try next time so i want to thank you all for joining in this stream hopefully uh you'll learn something along the way um and i will take this content and i'll post it up on youtube so if you want to find something that or maybe you joined in later and you didn't see the earlier part the content usually goes up on youtube about within 24 hours or so after the stream and um if you're over there you can give me a like or subscribe if you're here you want and have enjoyed it please please consider following me i would appreciate that and um with that i'll wish you all a good evening take care
Info
Channel: Matt Layman
Views: 509
Rating: 5 out of 5
Keywords: games, Python, Django, SaaS, emails
Id: 7mcDc9jA1YY
Channel Id: undefined
Length: 72min 14sec (4334 seconds)
Published: Thu Jun 24 2021
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.