Rest API¶
In this section, interesting parts of the REST code will be explored and commented.
Models¶
Note
Here, only code will be described: for the architecture, head to the database reference.
Operating on users in OngoingMatch¶
In the rest_api_ongoingmatch
table, information about the currently playing users is not easily accessible. Furthermore, inserting and removing users consists of two operations, requiring both a change of role and of the ongoing_match
foreign key of the user.
Rather than repeating often-used queries, Python's computed properties were used in order to simulate having hosts, challengers and spectators readily available and modifiable directly in the OngoingMatch
instance.
This helped to reduce the verbosity of the code, to respect the Don't repeat yourself (DRY) principle and, more importantly, to avoid to forget to change both attributes in the user.
71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 |
|
This snippet enables the use of simple syntax for accessing or setting the host of a match as if it were an attribute of the class:
user = User.objects.get(username='msilla')
ongoing_match.host = user
print(ongoing_match.host)
>>> User(<msilla>)
The setter property also validates the inserting operation, raising ValueError
if incompatibilites arise.
Code equivalents exist for the challenger and spectator roles, with the only exception being the spectator methods permitting multiple players with the same role.
Ending a match¶
In order to complete a match correctly, it is needed to:
- update the players' ELOs;
- set the role and the
ongoing_match
of the players toNone
; - delete the current
OngoingMatch
instance from the database; - create a corresponding
CompletedMatch
instance to keep track of the games history.
It is very important to conduct all of these operations in a single database transaction, in order to avoid leaving the data in an inconsistent state — either for brief durations while during the execution, or until human intervention if any errors happen before completing the set of needed operations.
This is only one example of such a situation, which demonstrates the use of transactions in the Django Model API.
Important
In the following snippet, the ongoing_match
foreign key is not updated because it is automatically set to NULL
by the DBMS due to the configured on_delete
policy.
157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 |
|
Views¶
While much of the views code is straightforward, there are interesting edge cases to take into account.
OngoingMatch creation¶
When an OngoingMatch
is created by a soon-to-be host via a POST
request, it must also be linked to the user that started the request, in order to keep track of the ownership of the match.
Therefore, the match instance is created, and then the user must be updated by setting the new instance as the user's current match.
If the user is already in a game when making the request, or any unanticipated errors happen after having created the match, the database is left in an inconsistent state; a transaction is necessary to avoid this.
53 54 55 56 57 58 59 60 61 62 |
|
HTTP 201
response is returned together with the new object representation in the body, respecting REST design principles.
Clean views
No substantial logic is therefore executed in the views, which only glue together other code: the error validation is done inside the model (just as described in the previous section), the serialization of the new instance to a Python dict
is handled by the serializer, and the JSON serialization is made by the Response
internal handlers.