#100DaysOfWeb in Python Transcripts
Chapter: Days 81-84: Unit testing web apps
Lecture: Adding view models
Login or
purchase this course
to watch this video and the rest of the course contents.
0:00
Let's begin by digging into this idea of a view model, and you might wonder why am I introducing this now? Why didn't we talk about earlier?
0:08
Well, one, I wanted to stay focussed but two, I want to bring this up in the context of testing because what you'll see is this
0:15
idea of a view model is a class that is the go between the HTML templates, the HTML forms and your view methods, and what that means is all the
0:25
data that's exchanged there as well as most of the validation that happens will be moved out of those web methods into a separate class that we
0:33
can test all the validation super easily. It also means you can have crazy amounts of validation and other data exchange code
0:42
without making your view methods ridiculously long so our goal will be to add this concept of a view method to our web application
0:49
before we start testing because it will let us separate certain types of testing to one area or the other, and both of them will get simpler.
0:56
With that in mind, let's get started. Create a new Python sub-package called viewmodels and we'll create a file called viewmodelbase.py
1:03
Now this is going to be a super simple class. The goal is to have some class that get some data and then gives it to the template, the HTML view
1:12
and remember what we're exchanging here if we go look real quick, is we're returning dictionaries. So we're going to create this thing called a
1:20
class ViewModelBase, like that just empty, and it's going to have a function to_dict. That returns a dict, and all it does is return
1:32
self.__dict__. If you're not familiar with this this is just the fields as a dictionary for this class.
1:38
The other thing that we need to do is, often these view models need to have hold of the request to interact with, like cookies or the forms
1:46
or whatever, so we'll have an init function here it's going to have a request and we explicitly type that make everything a little easier, like so
1:54
and we can let PyCharm assign the field. One more thing, often you have to return an error so let's go ahead and add an error property that's empty
2:02
maybe even be better to be None for starting out so we're going to have our request, it's going to have this empty error, which we may decide to set
2:09
in derive classes. Okay, this is our view model base. Next, we want to create a view model for the various piece that we have our default part
2:17
of our application, this is going to be our home and our details, so let's go ahead and create a sub-folder, or even a sub-package called default.
2:25
So all that stuff that's in here is going to have it's view models in here, so let's have a new view model. Now let's start out with the simplest one
2:32
the index_viewmodel.py So this will be a class called IndexViewModel which derives from a ViewModelBase.
2:40
I'm going to put an init method here and we'll go ahead and make a call up to super in a second.
2:45
Let's see what data that we're going to need to pass over. If we look over here at our index, we'll see that
2:52
this user_id is being passed along, is like being used in here. So let's assume that this is getting passed in, in some way.
2:59
Now we're kind of hacking it right, this would come from a cookie, maybe the view model would actually grab
3:03
the cookie, something like that, but let's just go over here and say it's going to have a user_id, which is an int.
3:10
We can add that field, PyCharm's also warning that we need to call the super by passing that request in. Good, so what do are we doing in here?
3:18
We're working with this repository so notice this data access bit right there, let's move that over here
3:24
say self.user as this repository, which we can import at the top, and for some reason, PyCharm wants to put it like this relative form, so let's say
3:33
billtracker.data, so we got our user and our user_id. Now if the user's not found maybe we want to specify an error message, we'll say
3:42
if not self.user: self.error, or something like this. Okay well how does this make things simpler? Let's come over and use this, finally, in our code.
3:53
So we'll start out and I'll say the view model is one of these and we need to import at the top again let's do it more explicitly
3:59
and it says you need to pass in the request and the user_id, we're just going to say it's equal to one.
4:05
Probably like I said, you'd get that from a cookie all of this database access is happening there this exchange is happening here
4:11
so we'll say, to_dict. Now notice originally we weren't using the request, sort of hidden it there let's put this back.
4:20
Okay, well first of all, notice this is simpler. If we want to test it or put more validation or whatever
4:26
we can extend this other class without complicating our view, but let's verify this actually still works. Stars, that's a good start, and look at that
4:33
here it's pulling back the user, it's pulling back the user's bills, all of these stuff is still working.
4:38
Let us separate these things, now you might think Michael, that's a lot of work for that little bit
4:42
of improvement, yes here it is, but let's look at this one. Let's look at this one about the post. All of this stuff about returning the errors
4:51
and setting the errors in this situation or that, and returning this and then adding all of these payments, all of this stuff
4:57
that's happening down here turns out to get much simpler and we actually are missing some validation like on this one here, for example
5:04
what do we do if say the user's not found? What do we do if the bill is not found? Well we do return to 404 here, what about if
5:11
somebody tries to hack it? They're logged in but they try to put in someone else's bill ID, there's a bunch of validation
5:15
and things like that, that we still need to add that would make this more complicated. So we're going to come over here and look at this next
5:22
and it will make concept of view models even better.