1 00:00:00,750 --> 00:00:04,618 We are almost done with customizing Django templates. 2 00:00:04,618 --> 00:00:09,270 In real world scenarios you'll probably have users pasting long text 3 00:00:09,270 --> 00:00:15,205 descriptions into your admin site using Markdown or another plain text format. 4 00:00:15,205 --> 00:00:20,180 Markdown is what's used on GitHub for example, for read me files. 5 00:00:20,180 --> 00:00:25,431 It allows you to easily type text that will render as html on a webpage, but 6 00:00:25,431 --> 00:00:30,280 in Django you have to do some preparation to get that to happen as expected. 7 00:00:31,340 --> 00:00:32,830 So let's write. 8 00:00:32,830 --> 00:00:35,200 You guessed it, a custom filter for that. 9 00:00:37,010 --> 00:00:41,140 First, we're going to install a Python library called markdown2. 10 00:00:41,140 --> 00:00:45,800 In your console, change directories into learning site and 11 00:00:45,800 --> 00:00:50,750 the run this command, pip install markdown2. 12 00:00:50,750 --> 00:00:51,650 Okay. 13 00:00:51,650 --> 00:00:56,200 Once that finishes running, open up course_extras.py and 14 00:00:56,200 --> 00:01:00,330 add import markdown2 to the top. 15 00:01:00,330 --> 00:01:04,280 Now, just to make sure everything's installed, let's start our server and 16 00:01:04,280 --> 00:01:05,600 load our page. 17 00:01:05,600 --> 00:01:09,410 If we don't get an import error, we know that everything installed correctly. 18 00:01:10,500 --> 00:01:16,180 So we'll do python manage.py runserver, 19 00:01:16,180 --> 00:01:19,868 and then identify our port. 20 00:01:19,868 --> 00:01:20,712 Great. 21 00:01:20,712 --> 00:01:22,770 It looks like we are good to go. 22 00:01:22,770 --> 00:01:24,520 So let's create our function. 23 00:01:24,520 --> 00:01:27,210 We'll put the console down here. 24 00:01:27,210 --> 00:01:31,330 And then down here at the bottom, we'll call our function mark down to html. 25 00:01:31,330 --> 00:01:36,470 And it's going to take an argument, 26 00:01:36,470 --> 00:01:40,940 it'll take a markdown text as an argument. 27 00:01:40,940 --> 00:01:45,565 It will return html, so we'll call what it returns html_body. 28 00:01:47,160 --> 00:01:50,060 It's the stuff in the middle that we need to worry about. 29 00:01:50,060 --> 00:01:51,580 So let's add a dock string, so 30 00:01:51,580 --> 00:01:54,633 that everyone knows what this function is supposed to do. 31 00:01:54,633 --> 00:02:01,200 So, converts markdown text to html. 32 00:02:01,200 --> 00:02:03,040 That's pretty straightforward. 33 00:02:03,040 --> 00:02:06,390 If we look at the documentation for markdown too. 34 00:02:06,390 --> 00:02:08,210 We see that it's pretty simple. 35 00:02:08,210 --> 00:02:13,500 We should be able to just call the mark down method on our passed in text, 36 00:02:13,500 --> 00:02:16,330 and it will do all the processing for us. 37 00:02:16,330 --> 00:02:20,470 I love simple libraries like this that are really powerful, and 38 00:02:20,470 --> 00:02:25,200 do something amazing and useful, but don't require a lot of work on my part. 39 00:02:25,200 --> 00:02:27,640 So let's add that to our function. 40 00:02:27,640 --> 00:02:34,195 So we can say that html body is going to be equal to markdown2.markdown, 41 00:02:34,195 --> 00:02:37,590 and then we pass in our markdown text. 42 00:02:38,640 --> 00:02:43,820 So what this will do, is it will convert our markdown text into html and 43 00:02:43,820 --> 00:02:48,860 store that result in our variable html body, which we then return. 44 00:02:48,860 --> 00:02:54,090 Finally, we need to register our filter, and this is going to be exactly the same 45 00:02:54,090 --> 00:02:57,050 as whenever we registered our time estimate filter. 46 00:02:57,050 --> 00:03:01,045 The only thing we need to change is the name of the filter. 47 00:03:01,045 --> 00:03:04,906 Markdown to html. 48 00:03:04,906 --> 00:03:06,015 There we go. 49 00:03:06,015 --> 00:03:09,490 Can you think of a place in our site where someone might 50 00:03:09,490 --> 00:03:13,366 enter markdown text instead of regular html or plain text? 51 00:03:13,366 --> 00:03:16,070 I can, the course description. 52 00:03:16,070 --> 00:03:19,390 Sometimes those are longer and require some formatting. 53 00:03:19,390 --> 00:03:22,990 So let's create a super user to log into the admin and 54 00:03:22,990 --> 00:03:27,200 replace the current course description with some marked down text. 55 00:03:27,200 --> 00:03:31,380 So down in your consul, stop your server with Ctrl+C. 56 00:03:31,380 --> 00:03:37,790 And then run python manage.py createsuperuser. 57 00:03:37,790 --> 00:03:40,720 I'm going to make my name Lacey, my user name. 58 00:03:40,720 --> 00:03:47,575 And then I'll set my email address to Lacey@teamtreehouse.com. 59 00:03:47,575 --> 00:03:51,570 My password, that's private. 60 00:03:51,570 --> 00:03:52,660 Okay. 61 00:03:52,660 --> 00:03:55,360 Let's restart our server, and 62 00:03:55,360 --> 00:03:59,410 then head into the admin to edit a course description with some markdown text. 63 00:04:00,600 --> 00:04:05,550 So up here in our url we just do /admin and 64 00:04:05,550 --> 00:04:08,940 then we log in using those credentials that we just created. 65 00:04:10,270 --> 00:04:14,000 Here in courses, I'm gonna change Python testing. 66 00:04:14,000 --> 00:04:18,590 So right now it just says, learn to test your Python applications with unittest and 67 00:04:18,590 --> 00:04:19,130 doctests. 68 00:04:20,230 --> 00:04:24,290 I'm gonna replace this with some mark down text that I've created. 69 00:04:24,290 --> 00:04:25,980 And we'll go over what those should do. 70 00:04:27,740 --> 00:04:30,230 Okay. So I've added this header and 71 00:04:30,230 --> 00:04:34,710 this double pound sign means that this line testing is amazing, 72 00:04:34,710 --> 00:04:37,280 should appear inside the header tags. 73 00:04:37,280 --> 00:04:42,165 Then, these next two lines learn to test your Python applications with unitest and 74 00:04:42,165 --> 00:04:47,320 doctest and things you will learn should each show up inside their own P tags. 75 00:04:48,590 --> 00:04:54,530 Then when you put a little dash next to a series of things that are in a list, 76 00:04:54,530 --> 00:04:58,370 in html, this should render as an unordered list. 77 00:04:58,370 --> 00:05:02,790 So now that we've done this, let's scroll down and save. 78 00:05:04,080 --> 00:05:05,750 And let's head back into our course. 79 00:05:06,980 --> 00:05:08,590 When we go to Python Testing, 80 00:05:09,850 --> 00:05:13,600 it looks like those special mark down characters just rendered normally. 81 00:05:13,600 --> 00:05:15,150 They didn't convert. 82 00:05:15,150 --> 00:05:18,020 Oh, but that's because we forgot something important. 83 00:05:18,020 --> 00:05:21,950 We forgot to add this filter that we created to our description, so 84 00:05:21,950 --> 00:05:23,630 let's add that. 85 00:05:23,630 --> 00:05:28,176 We ca go to the course_detail page and then we can just add our new 86 00:05:28,176 --> 00:05:33,008 markdown_to_html filter. 87 00:05:33,008 --> 00:05:35,430 Let's reload. 88 00:05:35,430 --> 00:05:37,360 Oh, invalid filter. 89 00:05:37,360 --> 00:05:42,780 Because up here with this load humanize, we also needed to load our course extras. 90 00:05:42,780 --> 00:05:46,680 Now we can put load course extras on it's own line or 91 00:05:46,680 --> 00:05:49,150 we can just stack it, next to humanize. 92 00:05:49,150 --> 00:05:51,280 So let's do that to save ourselves some time. 93 00:05:51,280 --> 00:05:57,500 Okay, this isn't quite what we expected. 94 00:05:57,500 --> 00:06:03,020 We're seeing the actual html tags instead of the rendered html. 95 00:06:03,020 --> 00:06:06,710 Now this is actually a nice thing that Django is doing for us. 96 00:06:06,710 --> 00:06:11,440 It's Django's way of protecting us from code that might get plugged into our data 97 00:06:11,440 --> 00:06:13,310 and might be malicious. 98 00:06:13,310 --> 00:06:16,460 We just need to tell Django this code is safe, 99 00:06:16,460 --> 00:06:21,730 meaning that it can render this html as html, with no problem. 100 00:06:21,730 --> 00:06:25,090 For that, we can use the safe filter, so 101 00:06:25,090 --> 00:06:29,590 we can chain safe next to markdown to html. 102 00:06:31,990 --> 00:06:37,160 Now, when we reload the page, we get our nice, friendly rendered html. 103 00:06:37,160 --> 00:06:40,390 There's a better way to mark text as safe though. 104 00:06:40,390 --> 00:06:43,380 We should mark our output from the filter as safe, so 105 00:06:43,380 --> 00:06:47,360 that anything that goes through this filter is marked safe and 106 00:06:47,360 --> 00:06:50,870 we don't have to keep using the safe filter over and over again. 107 00:06:50,870 --> 00:06:54,930 So let's import something called mark safe into course extras. 108 00:06:54,930 --> 00:06:57,220 We can lower this console. 109 00:06:57,220 --> 00:07:01,075 Go back up to the top and 110 00:07:01,075 --> 00:07:09,920 from django.utils.safestring we import mark_safe, 111 00:07:09,920 --> 00:07:14,680 and then we'll just pass our output into mark_safe as we return it. 112 00:07:17,110 --> 00:07:21,910 Finally, we can go ahead and delete the safe filter from our variable. 113 00:07:25,160 --> 00:07:28,890 Now when we refresh the page, we have the exact same result, 114 00:07:28,890 --> 00:07:29,820 which is what we wanted. 115 00:07:29,820 --> 00:07:34,170 In the real world, we do a few things differently. 116 00:07:35,190 --> 00:07:39,330 We would probably have the user select the format that they were using to submit 117 00:07:39,330 --> 00:07:43,710 their data, markdown, plain text, or something else, and 118 00:07:43,710 --> 00:07:47,630 we'd use the appropriate conversion filter accordingly. 119 00:07:47,630 --> 00:07:51,110 We would also add that filter to all the text content, 120 00:07:51,110 --> 00:07:55,400 for example, to the step content, as well as to the course content. 121 00:07:56,410 --> 00:08:00,000 But for our purposes, we've proven the concept, and that's enough.