WEBVTT

1
00:00:00.560 --> 00:00:02.630
<v Jose>Now finally there's quite a lot of changes</v>

2
00:00:02.630 --> 00:00:05.910
to app.py as a result of this.

3
00:00:05.910 --> 00:00:08.600
But most changes that we're gonna make are simplifications

4
00:00:08.600 --> 00:00:12.240
to allow to reason about the code more easily.

5
00:00:12.240 --> 00:00:13.490
From now on we're gonna be dealing

6
00:00:13.490 --> 00:00:16.090
with objects and their actions or methods

7
00:00:16.090 --> 00:00:18.883
rather than database operations directly.

8
00:00:20.170 --> 00:00:24.293
So lets go over to app.py and start making our changes.

9
00:00:25.650 --> 00:00:28.700
The first thing is that down here at the very bottom

10
00:00:28.700 --> 00:00:32.390
we've got this way of passing connections about.

11
00:00:32.390 --> 00:00:34.380
Passing a connection that we create up here

12
00:00:34.380 --> 00:00:37.470
to every function but we no longer need that because

13
00:00:37.470 --> 00:00:40.320
our models are in charge of creating the connection.

14
00:00:40.320 --> 00:00:42.143
So we're gonna get rid of that.

15
00:00:43.620 --> 00:00:45.170
Now over here in connections.py

16
00:00:46.120 --> 00:00:48.900
we are loading the connection, creating one,

17
00:00:48.900 --> 00:00:52.010
so what we're gonna do is go back to app.py

18
00:00:52.010 --> 00:00:54.640
and instead of using this line we're

19
00:00:54.640 --> 00:00:58.410
gonna use create-connection, we do have to import that

20
00:00:58.410 --> 00:01:00.860
so I'm going to do that.

21
00:01:00.860 --> 00:01:04.960
Now note that we're no longer calling load_dotenv here,

22
00:01:04.960 --> 00:01:05.910
so what I'm gonna do is I'm gonna

23
00:01:05.910 --> 00:01:08.110
move this over to connections.py

24
00:01:08.110 --> 00:01:12.080
and we're no longer going to need all of this.

25
00:01:12.080 --> 00:01:13.880
For now I'm gonna get rid of this input

26
00:01:13.880 --> 00:01:15.190
but we're gonna bring it back later on

27
00:01:15.190 --> 00:01:17.620
so I'm just gonna copy load_dotenv

28
00:01:17.620 --> 00:01:20.110
and bring it over to connections.py

29
00:01:21.110 --> 00:01:23.170
and of course we do have to import it

30
00:01:23.170 --> 00:01:24.753
so we're gonna import that.

31
00:01:26.590 --> 00:01:28.430
That simplifies our menu quite a bit.

32
00:01:28.430 --> 00:01:30.550
Notice the PyCharm is complaining that

33
00:01:30.550 --> 00:01:32.440
we're not passing in the required arguments

34
00:01:32.440 --> 00:01:33.970
but that's 'cause we're gonna go through

35
00:01:33.970 --> 00:01:36.890
and get rid of all of these connections

36
00:01:36.890 --> 00:01:38.963
that we don't need anymore.

37
00:01:41.310 --> 00:01:44.880
Lets start with the top function prompt_create_poll.

38
00:01:44.880 --> 00:01:48.550
Notice that prompt_create_poll is directly calling

39
00:01:48.550 --> 00:01:51.920
the database.create_poll function passing in the poll_title,

40
00:01:51.920 --> 00:01:56.070
owner and the list of strings each representing an option.

41
00:01:56.070 --> 00:01:59.010
We now want to use our poll and option

42
00:01:59.010 --> 00:02:00.890
classes to do this instead.

43
00:02:00.890 --> 00:02:02.690
First things we're going to go ahead

44
00:02:02.690 --> 00:02:04.540
and fix our import slightly here

45
00:02:04.540 --> 00:02:07.350
and we're going to import our models.

46
00:02:07.350 --> 00:02:11.940
So we're gonna do from models.option import_Option

47
00:02:11.940 --> 00:02:16.570
and from models our poll import_Poll.

48
00:02:16.570 --> 00:02:19.670
Now we can come back down here and start making our changes.

49
00:02:19.670 --> 00:02:22.710
The first thing, when we have the title and the owner

50
00:02:22.710 --> 00:02:24.360
we can create a poll and we can say

51
00:02:24.360 --> 00:02:29.360
poll = Poll with title or rather poll_title and poll_owner.

52
00:02:30.230 --> 00:02:32.580
Then we can save this poll to the database.

53
00:02:32.580 --> 00:02:35.380
So we're gonna do poll.save.

54
00:02:35.380 --> 00:02:38.240
And now we no longer need this options list,

55
00:02:38.240 --> 00:02:40.870
because what we're gonna do is admittedly

56
00:02:40.870 --> 00:02:43.270
a bit less efficient but just down to

57
00:02:43.270 --> 00:02:45.370
how we've re-architectured our code.

58
00:02:45.370 --> 00:02:47.900
While we've got a new option valid

59
00:02:47.900 --> 00:02:52.900
then we're gonna do poll.add.option(new_option)

60
00:02:53.050 --> 00:02:55.700
and we no longer need this call down here.

61
00:02:55.700 --> 00:02:58.630
So now you can see we've simplified our code a fair bit

62
00:02:58.630 --> 00:03:01.680
in exchange for losing some efficiency

63
00:03:01.680 --> 00:03:03.670
when it comes to dealing with our database.

64
00:03:03.670 --> 00:03:05.700
This is a trade off that you're gonna make a lot

65
00:03:05.700 --> 00:03:08.690
when you're working with an ORM, for example.

66
00:03:08.690 --> 00:03:10.450
And if you don't know what an ORM is,

67
00:03:10.450 --> 00:03:12.440
we'll talk about that later on.

68
00:03:12.440 --> 00:03:14.767
Let's take a look at list_open_polls.

69
00:03:14.767 --> 00:03:16.550
At the moment it's accessing this

70
00:03:16.550 --> 00:03:19.130
because it wants the polls to be coming from the database.

71
00:03:19.130 --> 00:03:21.710
But now we're gonna use our model to do that

72
00:03:21.710 --> 00:03:24.723
so will do polls = Poll.all,

73
00:03:25.780 --> 00:03:27.380
and that is gonna go into the database

74
00:03:27.380 --> 00:03:29.063
and fetch all the polls.

75
00:03:30.080 --> 00:03:31.270
Note that you don't really need that

76
00:03:31.270 --> 00:03:33.520
you could just put that directly in here

77
00:03:33.520 --> 00:03:34.970
and that shortens you code a bit more

78
00:03:34.970 --> 00:03:37.410
and I think that's still pretty reasonable.

79
00:03:37.410 --> 00:03:40.410
But we can't use the destructuring here

80
00:03:40.410 --> 00:03:43.290
because a poll is not a destructurable thing

81
00:03:43.290 --> 00:03:45.160
so we do have to do poll,

82
00:03:45.160 --> 00:03:50.122
but then you can do (poll.id) like that,

83
00:03:50.122 --> 00:03:53.490
(poll.title) (poll.owner) instead.

84
00:03:53.490 --> 00:03:55.800
Alternatively you could put all of this

85
00:03:55.800 --> 00:03:58.830
inside the STR method of the poll object

86
00:03:58.830 --> 00:04:00.050
and then you can just print the poll,

87
00:04:00.050 --> 00:04:01.680
if that makes it simpler for you

88
00:04:01.680 --> 00:04:03.870
then by all means you can do that.

89
00:04:03.870 --> 00:04:06.360
Now let's move over to prompt_vote_poll.

90
00:04:06.360 --> 00:04:07.410
Note that the first thing we're

91
00:04:07.410 --> 00:04:10.180
doing here is grab a poll ID.

92
00:04:10.180 --> 00:04:11.430
That means that it's very likely

93
00:04:11.430 --> 00:04:12.590
the next thing we're gonna do

94
00:04:12.590 --> 00:04:15.160
is get the associated poll object

95
00:04:15.160 --> 00:04:18.290
so that we can interact with the database through that.

96
00:04:18.290 --> 00:04:22.357
So let's do that poll = Poll.get with the (poll.id).

97
00:04:23.760 --> 00:04:26.610
Now the poll options, we know that we no longer

98
00:04:26.610 --> 00:04:28.730
need to access the database here like that

99
00:04:28.730 --> 00:04:31.730
because we can just .options.

100
00:04:31.730 --> 00:04:34.660
So when it comes to printing the poll options

101
00:04:34.660 --> 00:04:37.630
we could just grab all of this and put them in there,

102
00:04:37.630 --> 00:04:39.530
we don't need this variable at all.

103
00:04:39.530 --> 00:04:41.400
So now we've got the printing of the poll options

104
00:04:41.400 --> 00:04:44.740
is Poll.get the (poll.id) access the options of it

105
00:04:44.740 --> 00:04:46.470
that goes into the database as well

106
00:04:46.470 --> 00:04:47.960
and we're printing them out.

107
00:04:47.960 --> 00:04:50.220
Now we get the option ID and the username

108
00:04:50.220 --> 00:04:51.580
that they wanna a vote for.

109
00:04:51.580 --> 00:04:56.091
And what we wanna do here is do Option.get, with (option.id)

110
00:04:56.091 --> 00:04:58.520
that's gonna give us the option that they voted for

111
00:04:58.520 --> 00:04:59.917
and we do .vote(username).

112
00:05:01.320 --> 00:05:03.200
And we no longer need this there.

113
00:05:03.200 --> 00:05:06.320
Personally I prefer this way of coding much more,

114
00:05:06.320 --> 00:05:09.550
I find it more readable, more clear what's going on

115
00:05:09.550 --> 00:05:13.530
but I understand that not everybody likes that like I do.

116
00:05:13.530 --> 00:05:15.470
So if you don't like this way of doing things,

117
00:05:15.470 --> 00:05:18.870
you prefer the databases then you can continue doing that,

118
00:05:18.870 --> 00:05:20.680
but do remember that the code is gonna get

119
00:05:20.680 --> 00:05:23.220
messier over time if you have a lot of different

120
00:05:23.220 --> 00:05:25.860
database functions all working from the same file,

121
00:05:25.860 --> 00:05:27.963
so this is a way to simplify that.

122
00:05:28.800 --> 00:05:30.740
When we're calling print_poll_options,

123
00:05:30.740 --> 00:05:33.740
we are now passing a list of options objects.

124
00:05:33.740 --> 00:05:36.720
So we should change that and reflect it in here,

125
00:05:36.720 --> 00:05:39.802
the type hinting, so we'll do List of (Option).

126
00:05:39.802 --> 00:05:43.240
Note that option here is not the option tuple in

127
00:05:43.240 --> 00:05:46.950
database.py, this is the option class that we've imported.

128
00:05:46.950 --> 00:05:49.440
So now this should not be called poll_with_options

129
00:05:49.440 --> 00:05:52.560
it should just be called options, we can iterate over them

130
00:05:52.560 --> 00:05:54.507
and now instead of (option[3]) we can do (option.id),

131
00:05:55.580 --> 00:05:58.173
to print the ID and instead of four we can .text.

132
00:06:00.370 --> 00:06:02.970
Again, you can put this inside the STr method

133
00:06:02.970 --> 00:06:04.830
of your option class if you want

134
00:06:04.830 --> 00:06:06.900
and then you can print the option directly.

135
00:06:06.900 --> 00:06:09.143
Let's go in to show_poll_votes,

136
00:06:09.143 --> 00:06:12.060
this is gonna see its biggest change

137
00:06:12.060 --> 00:06:14.310
and it's not necessarily gonna make it

138
00:06:14.310 --> 00:06:16.520
a simpler piece of code,

139
00:06:16.520 --> 00:06:18.480
but it is gonna make it a little more obvious

140
00:06:18.480 --> 00:06:21.570
what's going on in terms of the Python code.

141
00:06:21.570 --> 00:06:24.300
This is where the biggest trade-off happens

142
00:06:24.300 --> 00:06:25.620
because we have a lot of joins

143
00:06:25.620 --> 00:06:27.800
and database operations happening in the query

144
00:06:27.800 --> 00:06:29.780
and now we're gonna have them in a Python code

145
00:06:29.780 --> 00:06:31.670
which is admittedly slower,

146
00:06:31.670 --> 00:06:35.750
but the gain that we get elsewhere, I think offsets this.

147
00:06:35.750 --> 00:06:37.090
So what we were doing here is

148
00:06:37.090 --> 00:06:39.500
we were getting the poll and the votes.

149
00:06:39.500 --> 00:06:41.960
That was basically, all the things

150
00:06:41.960 --> 00:06:43.700
that we needed in order to print out.

151
00:06:43.700 --> 00:06:45.520
That gave us, even the percentage.

152
00:06:45.520 --> 00:06:47.470
We accepted on the division by zero

153
00:06:47.470 --> 00:06:49.440
to see if there were no votes cast

154
00:06:49.440 --> 00:06:51.570
and then we just went through the list

155
00:06:51.570 --> 00:06:53.770
returned and printed them out.

156
00:06:53.770 --> 00:06:57.750
Now we have to do the same thing but with our models.

157
00:06:57.750 --> 00:07:01.300
We've got a poll ID, so let's get a poll

158
00:07:01.300 --> 00:07:03.780
we need it in order to interact with the database.

159
00:07:03.780 --> 00:07:05.853
The options are poll.options.

160
00:07:06.900 --> 00:07:09.880
And the votes_per_option, we can get

161
00:07:10.750 --> 00:07:13.487
with the length of (option.votes).

162
00:07:14.910 --> 00:07:17.820
Remember, option.votes is gonna give us

163
00:07:17.820 --> 00:07:19.110
all the votes that are coming from

164
00:07:19.110 --> 00:07:22.190
the database directly for each option,

165
00:07:22.190 --> 00:07:24.530
for option in options and we're gonna

166
00:07:24.530 --> 00:07:27.070
make this into a list comprehension.

167
00:07:27.070 --> 00:07:29.710
Then the total number of votes

168
00:07:29.710 --> 00:07:31.810
is gonna be the sum of (votes_per_option).

169
00:07:32.880 --> 00:07:34.510
So now what we have here

170
00:07:34.510 --> 00:07:39.510
are three very useful variables, a list of option objects,

171
00:07:39.790 --> 00:07:43.980
for each object we have the number of votes cast

172
00:07:43.980 --> 00:07:45.440
and then we also have the total.

173
00:07:45.440 --> 00:07:47.650
So now what we wanna do is iterate

174
00:07:47.650 --> 00:07:49.470
essentially over these two at the same time

175
00:07:49.470 --> 00:07:51.540
the options and the votes_per_option.

176
00:07:51.540 --> 00:07:54.440
So that we can match them, one to the other

177
00:07:54.440 --> 00:07:57.290
the order of the elements is gonna be the same

178
00:07:57.290 --> 00:07:59.094
so the first element in this list

179
00:07:59.094 --> 00:08:01.410
is gonna be the number of votes

180
00:08:01.410 --> 00:08:03.750
for the first element in this list.

181
00:08:03.750 --> 00:08:06.546
So what we're gonna do with the try block here

182
00:08:06.546 --> 00:08:10.310
we're gonna doe for option, votes in zip

183
00:08:11.300 --> 00:08:12.850
of options and votes_per_option

184
00:08:14.850 --> 00:08:17.070
and then what we're gonna do is calculate the percentage

185
00:08:17.070 --> 00:08:19.290
that these votes are out of the total.

186
00:08:19.290 --> 00:08:24.230
So we do percentage = votes / total_votes * 100

187
00:08:24.230 --> 00:08:25.390
and then we're gonna print out.

188
00:08:25.390 --> 00:08:27.883
So we'll do (option.text) for (votes)

189
00:08:30.483 --> 00:08:32.610
or got this many votes

190
00:08:32.610 --> 00:08:35.110
and that is the percentage:.2f

191
00:08:36.410 --> 00:08:37.630
of the total.

192
00:08:37.630 --> 00:08:39.183
Then we'll except on zero division error,

193
00:08:39.183 --> 00:08:41.640
ZeroDivisionError like that

194
00:08:41.640 --> 00:08:42.550
and say

195
00:08:42.550 --> 00:08:46.620
No votes cast for this poll yet.

196
00:08:46.620 --> 00:08:48.370
A small simplification we can do here

197
00:08:48.370 --> 00:08:51.230
is we can move that over to one line,

198
00:08:51.230 --> 00:08:54.090
making sure to rename this to options.

199
00:08:54.090 --> 00:08:57.500
And now you can see that the code here

200
00:08:57.500 --> 00:09:00.080
is slightly longer

201
00:09:00.080 --> 00:09:03.190
but it is more obvious what's going on.

202
00:09:03.190 --> 00:09:05.330
We've got the options, we've got the votes per options,

203
00:09:05.330 --> 00:09:08.840
the total votes and then we calculate the percentage there.

204
00:09:08.840 --> 00:09:10.130
However, like I mentioned earlier

205
00:09:10.130 --> 00:09:12.720
it is a bit slower, a bit less efficient

206
00:09:12.720 --> 00:09:14.440
that's the trade-off that we're making.

207
00:09:14.440 --> 00:09:17.240
Finally randomising a pole winner changes slightly,

208
00:09:17.240 --> 00:09:19.130
instead of accessing the database directly

209
00:09:19.130 --> 00:09:20.450
we go through the model classes

210
00:09:20.450 --> 00:09:23.470
and instead of using order by random in a database query,

211
00:09:23.470 --> 00:09:26.010
we get all the voters and use random.choice,

212
00:09:26.010 --> 00:09:28.420
which we're gonna import in a moment.

213
00:09:28.420 --> 00:09:30.490
So here we are printing the poll options,

214
00:09:30.490 --> 00:09:32.757
all we have to do here is do (Poll.get(poll.id).options),

215
00:09:36.006 --> 00:09:38.170
and we can get rid of this.

216
00:09:38.170 --> 00:09:40.513
And then we've got the option ID that we wanna

217
00:09:40.513 --> 00:09:42.720
pick a random voter for.

218
00:09:42.720 --> 00:09:44.870
What we have to do is get the votes

219
00:09:44.870 --> 00:09:48.917
so we'll do votes = Option.get with (option.id).votes,

220
00:09:51.420 --> 00:09:55.249
and then we say winner = random.choice out of (votes)

221
00:09:55.249 --> 00:09:58.800
random.choice this is a function inside the random module

222
00:09:58.800 --> 00:10:02.400
just grabs a random element from the sequence provided.

223
00:10:02.400 --> 00:10:03.960
So here we're gonna give it the list of votes

224
00:10:03.960 --> 00:10:06.670
and it's gonna grab one of them at random.

225
00:10:06.670 --> 00:10:08.640
And then we're just gonna print

226
00:10:08.640 --> 00:10:11.270
the randomly selected winner is winner zero.

227
00:10:11.270 --> 00:10:14.020
Remember, votes is a list of tuples

228
00:10:14.020 --> 00:10:15.600
so when we do random choice of votes

229
00:10:15.600 --> 00:10:17.330
that's gonna give us a single topple.

230
00:10:17.330 --> 00:10:19.060
We're still accessing zero here

231
00:10:19.060 --> 00:10:21.890
not winner.name or something like that,

232
00:10:21.890 --> 00:10:25.060
because we don't have a model class for votes.

233
00:10:25.060 --> 00:10:27.550
Let's import the random module up here at the top

234
00:10:27.550 --> 00:10:32.470
and we can go ahead and do import random

235
00:10:32.470 --> 00:10:35.290
and we no longer need this division by zero error

236
00:10:36.380 --> 00:10:38.650
because we're using the Python division,

237
00:10:38.650 --> 00:10:41.660
We use the built in zero division error instead.

238
00:10:41.660 --> 00:10:44.520
Now I know we've made a lot of changes in this video

239
00:10:44.520 --> 00:10:45.830
and this has been a pretty long video

240
00:10:45.830 --> 00:10:48.930
so I've got a full summary of all the changes

241
00:10:48.930 --> 00:10:51.960
over in the Ebook as well as a code diff

242
00:10:51.960 --> 00:10:54.340
that you can check out that has the changes.

243
00:10:54.340 --> 00:10:57.870
Especially in apps.PY where they are the most complicated.

244
00:10:57.870 --> 00:10:59.350
So I do recommend you do that

245
00:10:59.350 --> 00:11:03.470
but am also gonna put up the code as it stands right now

246
00:11:03.470 --> 00:11:05.180
in the resources section of this lecture

247
00:11:05.180 --> 00:11:07.290
so you can download it and continue from here

248
00:11:07.290 --> 00:11:09.050
in case you have any typos or anything like that

249
00:11:09.050 --> 00:11:10.550
so you can avoid them.

250
00:11:10.550 --> 00:11:13.660
But hopefully everything we've done is clear

251
00:11:13.660 --> 00:11:16.730
and it makes sense why we're doing this.

252
00:11:16.730 --> 00:11:18.730
Again, just for the last time

253
00:11:18.730 --> 00:11:20.240
I know I've said this a few times,

254
00:11:20.240 --> 00:11:24.210
this is the trade-off between performance and efficiency

255
00:11:24.210 --> 00:11:27.480
by using custom database queries for everything

256
00:11:27.480 --> 00:11:30.710
versus simplicity and ease of maintaining

257
00:11:30.710 --> 00:11:33.180
by using the models approach.

258
00:11:33.180 --> 00:11:36.010
As applications get larger and larger,

259
00:11:36.010 --> 00:11:37.810
it starts to make more sense particularly

260
00:11:37.810 --> 00:11:39.400
if you're working with more people

261
00:11:39.400 --> 00:11:41.630
to move to a model-based approach

262
00:11:41.630 --> 00:11:43.630
since that's a bit simpler to work with.

263
00:11:44.470 --> 00:11:46.190
Thank you guys for joining in this video.

264
00:11:46.190 --> 00:11:47.370
I hope you've enjoyed it.

265
00:11:47.370 --> 00:11:50.020
Thanks for watching and I'll see you in the next one.

