back to indexFastHTML advapp.py walkthru - - Answer.AI dev chat #6
00:00:31.120 |
I am going to help you understand first HTML. 00:00:52.760 |
What's special about this app is that it's really 00:01:05.520 |
you might come across in a normal CRUD database app. 00:01:12.880 |
So if you understand every line of code here, 00:01:36.680 |
you just run Python and the name of the application. 00:02:46.520 |
are the best for this, creating a Cloud project in which 00:02:49.120 |
you grab the llms-ctx.txt file and dump it into this project. 00:03:09.720 |
And you can basically ask anything at that point. 00:03:11.920 |
So can you give me a basic skeleton fastHTML app 00:03:21.960 |
That's a bit more than a skeleton, but that's cool. 00:03:44.240 |
is actually the application we're looking at. 00:03:54.760 |
that, actually, the fastHTML standard library itself 00:03:57.560 |
does for UI toolkits, which is just to import star. 00:04:01.840 |
That's actually what the official Python docs 00:04:08.640 |
that only the stuff you actually need are going to appear. 00:04:19.920 |
OK, so a to-do list needs to store the to-do somewhere. 00:04:37.920 |
So I'll show you when we get there, if that's OK. 00:04:42.600 |
So for SQLite, we do most things using this tiny little library 00:04:47.720 |
called fastLite, which is a thin wrapper over really 00:04:51.800 |
This is done by Simon Willison, who created something 00:04:56.400 |
But basically, it kind of makes your database 00:05:09.760 |
Actually, there's a way I kind of prefer nowadays, 00:05:22.960 |
to see lots of examples, as is gallery.fastHT.ml. 00:05:27.400 |
Yeah, so-- and there's a to-dos here, as well. 00:05:54.360 |
you're going to have to say, this is where my database lives. 00:06:03.780 |
And then-- so users contains name and password, 00:06:17.460 |
and to-dos contains ID, title, done, name, details, parity. 00:06:36.940 |
By default, the primary key is assumed to be called ID. 00:06:39.660 |
So if you want something else, you just have to say-- 00:06:46.420 |
OK, user-- OK, so ID is always the primary key, 00:07:37.380 |
It can be quite helpful to just look at the code 00:07:56.300 |
So it calls fastHTML, just passes everything across to it, 00:08:19.420 |
All right, I don't know if I followed the fastHTML 00:08:26.140 |
fast_app's just three lines of code that calls fastHTML. 00:08:35.460 |
Because you want static routes, which you normally do. 00:08:38.420 |
So if you want a .txt, or a favicon.ico, or whatever, 00:08:43.540 |
you want your application to respond by serving up 00:09:09.260 |
manually for this example, just to show people-- 00:09:16.900 |
yeah, otherwise, we wouldn't get people to show-- 00:09:18.980 |
have a reason to show people how to do static routes, 00:09:26.340 |
Reason two is it kind of gets the route for you. 00:09:37.380 |
OK, so one thing that happens, then, in our to-do list 00:09:52.140 |
is, if I try to go to slash, I'm not logged in. 00:09:58.420 |
So you can basically specify that some code runs 00:10:13.980 |
So code that runs before every request is called before_where. 00:10:23.340 |
is one or more functions to call before your request runs. 00:10:37.860 |
And so before_where and basically every other type 00:10:49.900 |
what you want to get access to in these parentheses here. 00:11:08.140 |
The session is just a signed cookie, basically, 00:11:17.420 |
So we're going to be saving whether they're logged in or not 00:11:28.980 |
And if they're not, then we return redirect to slash login. 00:11:40.220 |
Otherwise, this happens, which we'll leave for now. 00:11:42.700 |
OK, so that's how come we can see the login screen. 00:11:45.500 |
Do you say just to ignore the todos.extra right now? 00:12:10.140 |
And it comes in by default. All you have to do is say headers. 00:12:13.940 |
So this is the stuff in the head section of your HTML. 00:12:27.140 |
And the nice thing is, because these are just 00:12:32.380 |
normal HTML applications, you can always just use source 00:12:46.100 |
And so we saw some JavaScript like this, right? 00:12:56.220 |
That JavaScript comes from this library called sortable.js, 00:13:04.540 |
And so in this app, I'm showing two ways of doing JavaScript. 00:13:15.580 |
And one is by things that we define ourselves. 00:13:19.540 |
So sortable.js, it's really kind of there as an example. 00:13:30.260 |
And so if I search for sortable, this is the whole thing, right? 00:13:37.420 |
This is all it took to add sortable.js to fastHTML. 00:13:43.740 |
And it's just basically a case of just returning a script 00:14:27.340 |
So here you can see markdown.js is just a string of markdown. 00:14:53.260 |
So the easiest-- well, there's lots of different ways 00:15:01.620 |
But app.route is one convenient way to do it. 00:15:05.380 |
And I use it so often that I normally just give it a name. 00:15:14.100 |
And then here for the name, you say what HTTP verb. 00:15:30.580 |
So we're going to need a form with an action, a method. 00:15:35.380 |
So this is all just plain HTML, HTTP, whatever. 00:15:39.820 |
So rather than having to write to a form, slash form, 00:15:45.460 |
you just use a function with the exact same name, 00:15:58.980 |
We use that type by default so I didn't have to type it in. 00:16:08.580 |
there's different ways of defining the routes. 00:16:24.420 |
it wants to anchor on the name of the function in a file. 00:16:29.700 |
And there's multiple functions in the same name. 00:16:37.060 |
Yeah, there's lots of different ways of doing it. 00:16:53.420 |
By default, it adds both a get and a post method. 00:17:29.420 |
And also, it causes an H1 with the title to be added. 00:17:39.500 |
it's good just to look at them so you can just 00:17:43.020 |
>> I did look at the source code for that function today. 00:17:47.300 |
Yeah, so it's just a tuple of the title, a main, H1. 00:18:33.340 |
>> This indentation, sorry, this indentation or-- 00:19:02.180 |
because it'll be more familiar, and also because I've 00:19:04.300 |
set up this thing called Blockman, which is nice. 00:19:28.100 |
>> Yeah, it just works by parentheses, exactly. 00:19:31.660 |
So for stuff like this, it's nice to be able to see. 00:20:11.700 |
are going to be passed to my post handler, which is here. 00:20:20.140 |
And so we've added lots and lots of comments to all of these, 00:20:31.940 |
So we saw that there's going to be a name and a password. 00:20:37.300 |
You could just type in here name colon str comma password 00:20:47.740 |
Although I'm showing the slightly trickier thing here, 00:20:49.980 |
which is where you can create a class with those things in it. 00:21:07.500 |
>> The benefits will come when we get to some of the other stuff, 00:21:38.540 |
submitting the POST request is sending a login type? 00:22:05.340 |
So this is the best way to understand what's going on, 00:22:21.340 |
And so the result was a 303c other, so it's a redirect. 00:22:47.700 |
it constructed it by passing in those parameters 00:23:02.140 |
If they didn't fill it in, then obviously they can't log in. 00:23:09.340 |
OK, so then we try to see whether the user exists. 00:23:38.460 |
just have a little notebook I can fickle around with. 00:24:06.860 |
So Fastlight basically lets you search by primary key 00:24:26.340 |
>> Wait, when you had square brackets without anything in it, 00:24:43.180 |
>> There's a dot, and specifically dot C gives me the-- 00:24:53.580 |
>> That's awesome that it's available like that. 00:25:14.460 |
actually, Jono recently added a slightly nicer way 00:25:17.540 |
to do this, which is instead you can say if login.name 00:25:41.220 |
You could do that way nowadays instead of using try except. 00:25:55.220 |
the logic we have is just that if a user doesn't exist, 00:26:06.860 |
So that's how we create new users in the system. 00:26:11.260 |
OK, so now we're up to your question from earlier, 00:26:20.380 |
is all the things like insert and update and stuff 00:26:26.060 |
So this has returned the user that's been inserted. 00:26:31.300 |
So we want to check whether their password is correct. 00:26:37.820 |
But there's a technical security issue there, 00:26:41.220 |
which is depending on the length of the password, 00:26:47.300 |
takes to finish calling equals equals varies, 00:26:51.300 |
which actually basically gives some information 00:26:59.020 |
So this is a more secure way of comparing two passwords. 00:27:29.020 |
if you put auth as a parameter, you'll be passed-- 00:27:32.860 |
VICTOR: What made you know about this Python security thing? 00:27:43.620 |
recommend reading to know about different things like that? 00:27:46.860 |
Well, for FastMail, I had to know all that stuff. 00:28:52.500 |
When we went to Slash, we call this one, our get. 00:29:03.700 |
And just to show off, I wanted to say jph is to-do list. 00:30:10.220 |
you want to be able to see the Australian flag 00:30:13.380 |
That's not going to work unless you have a static file 00:30:16.860 |
Like a .ico file should be just passed directly. 00:30:23.300 |
And so this is a bit of a long way of doing it. 00:30:38.220 |
So if you look up Starlet, it'll teach you what this means. 00:30:41.140 |
But this basically says, in the path, one of the bits 00:30:49.220 |
And then another bit we're going to call ext. 00:31:14.980 |
If you use FastApp, you don't have to add this. 00:31:29.620 |
OK, so then along the top, we've got JPH's to-do list, logout. 00:32:37.140 |
Yeah, so you can see it's all just generating HTML, right? 00:32:47.380 |
So then we've got this new to-do here with an Add button. 00:32:57.940 |
And to make them look nicely together like this, 00:33:32.020 |
Yeah, basically, no one's going to write all of this CSS 00:33:38.220 |
So just pick a framework and stick to it for a while 00:33:43.580 |
And I would say just use Pico for a while, because it's fine. 00:34:05.900 |
write to-dos, parenthesis, parenthesis, like so. 00:34:10.580 |
So that's how you return the contents of your table. 00:34:42.260 |
So there's a few-- you can pass in a WHERE clause 00:34:46.580 |
and arguments for that, ORDER BY, LIMIT, OFFSET, SELECT. 00:34:50.580 |
So it's just like a very, very simple one-table query 00:34:55.060 |
that, by default, just returns the whole table. 00:34:56.980 |
Because I'm kind of terrible at remembering all the security 00:35:02.300 |
details, and I think everybody else is as well, 00:35:04.620 |
we've tried to make that really pretty automatic. 00:35:17.580 |
will add this criteria to all of your queries, 00:35:25.340 |
So to-dos.extra.jph-- sorry, you've got to say name equals 00:35:39.740 |
And you can see now it's only displaying my to-dos. 00:36:20.460 |
OK, so now we want to order them by priority, which-- 00:36:35.820 |
So I find it much easier to write these kinds of things 00:36:41.020 |
if you have a little notebook or REPL attached to the database 00:36:47.900 |
So all of the fast HTML tags, we call them fast tags or FT, 00:36:57.940 |
Oh, wait, that thing that was global, the extras, 00:37:10.900 |
OK, is there a way to not add it to all the routes, 00:37:17.340 |
at once without being global or whatever that you recommend? 00:37:31.420 |
Decorator would probably be your best option, I think. 00:37:51.460 |
All tags, or FT, or fast tags as we call them, 00:38:04.620 |
putting database objects as arguments to a tag. 00:38:13.420 |
And the answer is that FastHTML automatically 00:38:26.620 |
And so we actually added a DunderFT to our to-do class. 00:38:32.140 |
So this is what's called to automatically turn 00:39:11.500 |
a hyperlink with the word edit, optionally a tick if it's done. 00:39:33.140 |
- I'm just watching, but I'm not familiar with FastCore. 00:39:38.260 |
And I want to explain what that patch is doing. 00:39:42.380 |
- Yeah, so it just adds this method to this class. 00:39:46.900 |
Although, if you use the newer style approach I described, 00:39:58.220 |
So you don't have to use patch if you don't want to. 00:40:03.780 |
- The newer style approach that I showed you earlier. 00:40:16.820 |
So you can just go dot, dot, dot, dot, dot, dot, dot, dot. 00:40:29.780 |
So we could just move that and put it just straight inside 00:41:08.020 |
Just another reason this is kind of nice approach. 00:41:28.220 |
that you just put your to-dos directly into your form. 00:41:30.420 |
And then the form, we can give it an ID, a class. 00:41:36.620 |
The form has a post, which we'll come back to why that's there. 00:41:45.940 |
When you're doing something like that to-do component, 00:41:49.220 |
or whatever the list, the to-do list, or the thing, 00:42:08.340 |
I think I probably just did it directly here, 00:42:14.780 |
So generally, I actually only use notebooks and stuff 00:42:27.260 |
- Like if you wanted to fiddle around with the way it looks, 00:42:39.980 |
I guess, you just kind of hot reload and just-- 00:42:43.220 |
- Yeah, so you can use fast tags directly in a notebook, 00:42:52.300 |
going to look like, you can just work a show on them, 00:42:58.060 |
But it does depend on having the style sheets available. 00:43:05.940 |
I think the easiest way to really see what it's actually 00:43:08.180 |
going to look like with the styling and stuff 00:43:12.140 |
And we have live reloading, so as soon as you edit your app, 00:43:16.740 |
Yeah, again, lots of comments here explaining exactly what's 00:43:50.420 |
So I guess, yeah, what happens when you add a new one? 00:44:01.740 |
It's going to be clicking the Add button inside the form. 00:44:19.460 |
And so this is kind of cool, because the form has a title, 00:44:35.020 |
and we say we want to get a to-do passed to us. 00:44:47.620 |
So that's how we can have a to-do automatically. 00:44:51.180 |
Can you talk a little bit about when to use async 00:45:02.940 |
find that you need to type the word, oh, wait, 00:45:04.860 |
and if you ever type the word, oh, wait, and don't have async, 00:45:09.460 |
So don't type async unless Python yells at you. 00:46:08.180 |
And remember, I mentioned that when you insert something, 00:46:15.940 |
And after it's been inserted, it's now got an ID added. 00:46:22.900 |
And then the other thing that's going to happen when we-- 00:46:26.380 |
so when I click Add, two things should happen. 00:46:36.180 |
So the reason why is because we return the new to-do. 00:46:39.460 |
And that gets rendered with Dunder FT automatically. 00:46:42.660 |
And we also return a new input box, which is empty. 00:46:57.540 |
I guess, how does it know to do the replacement? 00:47:09.620 |
It means to replace one that's already there, 00:47:29.620 |
>>No, this is inserting it into the to-do's table. 00:47:34.900 |
>>And after-- and then it returns the inserted to-do. 00:47:39.420 |
And then it gets automatically rendered as a list item 00:47:51.140 |
because when we hear this button, when the button that we 00:47:58.380 |
clicked has an HX swap after begin and target to-do list, 00:48:17.660 |
So you should definitely, definitely, definitely 00:48:33.780 |
Don't just do the HTMX blog post tutorials, whatever. 00:48:49.300 |
And as per usual, build things as you read it, of course. 00:49:12.300 |
It's just using HTML and HTTP, because that's 00:49:15.740 |
kind of like understanding that's a prerequisite. 00:49:24.940 |
>>Yeah, so once you've read that book, you know HTMX. 00:49:46.700 |
OK, it's going to get this thing back, this to-dos back. 00:50:24.500 |
OK, so it did a post to Slash with this payload. 00:50:50.380 |
is inserted to replace whatever has that ID, which is this. 00:50:57.140 |
And this one is inserted wherever the requesting button 00:51:01.900 |
asked it to be inserted, which was after the beginning 00:51:36.300 |
So we can't hope to explain all of HTMX here. 00:52:10.100 |
And it does that because our ThunderFT says so-- 00:52:33.060 |
say what special things in the path, variables in the path. 00:52:35.620 |
And it's going to get stuck into your parameters. 00:52:42.020 |
And we pop up our form with the title, and the save, 00:52:58.700 |
in FastHTML, which, as it says here, it populates the form. 00:53:03.620 |
So it takes the form called res and populates it 00:53:24.860 |
What's the hidden thing about this hidden element? 00:53:35.260 |
Again, this is just like-- this isn't even HTML. 00:54:45.820 |
because that's kind of a starting point for this. 00:55:07.140 |
So you don't necessarily have to know all these details 00:55:09.540 |
when you have a language model at your disposal. 00:55:15.660 |
Yeah, otherwise, maintenance and debugging and all that 00:55:21.460 |
Yeah, I'm still somewhat skeptical about the ability 00:55:24.860 |
for people to actually run businesses on the back of code 00:55:28.180 |
if they had a model create and they don't understand. 00:55:32.860 |
Can't imagine that really works in real life. 00:55:47.300 |
I kind of know a little bit about where to look in a way. 00:55:51.900 |
But if I didn't know anything at all, I think I would get stuck.