From ef67d2abbf53a9d237981054d4aca83a52f3efdd Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Victor=20L=C3=B6fgren?= <viclo211@student.liu.se>
Date: Wed, 28 Apr 2021 11:19:10 +0000
Subject: [PATCH] Resolve "Replace None with sentinel"

---
 .../PresentationEditorPage.test.tsx           | 11 +++
 .../PresentationEditorPage.tsx                |  6 +-
 .../src/pages/presentationEditor/styled.tsx   |  4 +-
 client/src/pages/views/OperatorViewPage.tsx   | 85 +++++++++----------
 server/app/apis/alternatives.py               | 15 ++--
 server/app/apis/answers.py                    | 19 +++--
 server/app/apis/auth.py                       |  4 +-
 server/app/apis/competitions.py               | 33 +++----
 server/app/apis/components.py                 | 38 +++++----
 server/app/apis/media.py                      |  3 +-
 server/app/apis/questions.py                  | 18 ++--
 server/app/apis/slides.py                     | 17 ++--
 server/app/apis/teams.py                      | 13 +--
 server/app/apis/users.py                      | 24 +++---
 server/app/core/parsers.py                    | 17 ++++
 server/app/database/controller/edit.py        | 17 +---
 16 files changed, 181 insertions(+), 143 deletions(-)

diff --git a/client/src/pages/presentationEditor/PresentationEditorPage.test.tsx b/client/src/pages/presentationEditor/PresentationEditorPage.test.tsx
index c645724e..5426152f 100644
--- a/client/src/pages/presentationEditor/PresentationEditorPage.test.tsx
+++ b/client/src/pages/presentationEditor/PresentationEditorPage.test.tsx
@@ -27,8 +27,19 @@ it('renders presentation editor', () => {
       ],
     },
   }
+  const typesRes: any = {
+    data: {
+      view_types: [
+        {
+          name: '',
+          id: 0,
+        },
+      ],
+    },
+  }
   ;(mockedAxios.get as jest.Mock).mockImplementation((path: string, params?: any) => {
     if (path.startsWith('/api/competitions')) return Promise.resolve(competitionRes)
+    if (path.startsWith('/api/misc/types')) return Promise.resolve(typesRes)
     return Promise.resolve(citiesRes)
   })
   render(
diff --git a/client/src/pages/presentationEditor/PresentationEditorPage.tsx b/client/src/pages/presentationEditor/PresentationEditorPage.tsx
index 65720dc9..80d35c5f 100644
--- a/client/src/pages/presentationEditor/PresentationEditorPage.tsx
+++ b/client/src/pages/presentationEditor/PresentationEditorPage.tsx
@@ -93,9 +93,9 @@ const PresentationEditorPage: React.FC = () => {
   const competition = useAppSelector((state) => state.editor.competition)
   const competitionLoading = useAppSelector((state) => state.editor.loading)
   useEffect(() => {
+    dispatch(getTypes())
     dispatch(getEditorCompetition(competitionId))
     dispatch(getCities())
-    dispatch(getTypes())
   }, [])
 
   const setActiveSlideId = (id: number) => {
@@ -177,7 +177,7 @@ const PresentationEditorPage: React.FC = () => {
               Applicera ändringar på samtliga vyer
             </Typography>
             <ViewButton
-              activeView={activeViewTypeName === 'Audience'}
+              $activeView={activeViewTypeName === 'Audience'}
               variant="contained"
               color="secondary"
               onClick={() => changeView('Audience')}
@@ -185,7 +185,7 @@ const PresentationEditorPage: React.FC = () => {
               Åskådarvy
             </ViewButton>
             <ViewButton
-              activeView={activeViewTypeName === 'Team'}
+              $activeView={activeViewTypeName === 'Team'}
               variant="contained"
               color="secondary"
               onClick={() => changeView('Team')}
diff --git a/client/src/pages/presentationEditor/styled.tsx b/client/src/pages/presentationEditor/styled.tsx
index ea266c5e..f68eb166 100644
--- a/client/src/pages/presentationEditor/styled.tsx
+++ b/client/src/pages/presentationEditor/styled.tsx
@@ -8,12 +8,12 @@ export const ToolBarContainer = styled(Toolbar)`
 `
 
 interface ViewButtonProps {
-  activeView: boolean
+  $activeView: boolean
 }
 
 export const ViewButton = styled(Button)<ViewButtonProps>`
   margin-right: 8px;
-  background: ${(props) => (props.activeView ? '#5a0017' : undefined)};
+  background: ${(props) => (props.$activeView ? '#5a0017' : undefined)};
 `
 
 export const ViewButtonClicked = styled(Button)`
diff --git a/client/src/pages/views/OperatorViewPage.tsx b/client/src/pages/views/OperatorViewPage.tsx
index 29f20917..e965a3ec 100644
--- a/client/src/pages/views/OperatorViewPage.tsx
+++ b/client/src/pages/views/OperatorViewPage.tsx
@@ -143,44 +143,42 @@ const OperatorViewPage: React.FC = () => {
       >
         <DialogTitle id="responsive-dialog-title">{'Koder för tävlingen'}</DialogTitle>
         <DialogContent>
-          <DialogContentText>
-            <ListItem>
-              <ListItemText primary={`Domare: ${presentation.code}`} />
-              <Tooltip title="Kopiera kod" arrow>
-                <Button
-                  onClick={() => {
-                    navigator.clipboard.writeText(presentation.code)
-                  }}
-                >
-                  <FileCopyIcon fontSize="small" />
-                </Button>
-              </Tooltip>
-            </ListItem>
-            <ListItem>
-              <ListItemText primary={`Tävlande: ${presentation.code}`} />
-              <Tooltip title="Kopiera kod" arrow>
-                <Button
-                  onClick={() => {
-                    navigator.clipboard.writeText(presentation.code)
-                  }}
-                >
-                  <FileCopyIcon fontSize="small" />
-                </Button>
-              </Tooltip>
-            </ListItem>
-            <ListItem>
-              <ListItemText primary={`Publik: ${presentation.code}`} />
-              <Tooltip title="Kopiera kod" arrow>
-                <Button
-                  onClick={() => {
-                    navigator.clipboard.writeText(presentation.code)
-                  }}
-                >
-                  <FileCopyIcon fontSize="small" />
-                </Button>
-              </Tooltip>
-            </ListItem>
-          </DialogContentText>
+          <ListItem>
+            <ListItemText primary={`Domare: ${presentation.code}`} />
+            <Tooltip title="Kopiera kod" arrow>
+              <Button
+                onClick={() => {
+                  navigator.clipboard.writeText(presentation.code)
+                }}
+              >
+                <FileCopyIcon fontSize="small" />
+              </Button>
+            </Tooltip>
+          </ListItem>
+          <ListItem>
+            <ListItemText primary={`Tävlande: ${presentation.code}`} />
+            <Tooltip title="Kopiera kod" arrow>
+              <Button
+                onClick={() => {
+                  navigator.clipboard.writeText(presentation.code)
+                }}
+              >
+                <FileCopyIcon fontSize="small" />
+              </Button>
+            </Tooltip>
+          </ListItem>
+          <ListItem>
+            <ListItemText primary={`Publik: ${presentation.code}`} />
+            <Tooltip title="Kopiera kod" arrow>
+              <Button
+                onClick={() => {
+                  navigator.clipboard.writeText(presentation.code)
+                }}
+              >
+                <FileCopyIcon fontSize="small" />
+              </Button>
+            </Tooltip>
+          </ListItem>
         </DialogContent>
         <DialogActions>
           <Button autoFocus onClick={handleClose} color="primary">
@@ -304,11 +302,12 @@ const OperatorViewPage: React.FC = () => {
         }}
       >
         <List>
-          {teams.map((team) => (
-            <ListItem key={team.id}>
-              {team.name} score: {team.question_answers}{' '}
-            </ListItem>
-          ))}
+          {teams &&
+            teams.map((team) => (
+              <ListItem key={team.id}>
+                {team.name} score: {team.question_answers}{' '}
+              </ListItem>
+            ))}
         </List>
       </Popover>
     </OperatorContainer>
diff --git a/server/app/apis/alternatives.py b/server/app/apis/alternatives.py
index 0ce74d53..94f6d6b1 100644
--- a/server/app/apis/alternatives.py
+++ b/server/app/apis/alternatives.py
@@ -4,14 +4,19 @@ from app.apis import item_response, list_response, protect_route
 from app.core.dto import QuestionAlternativeDTO
 from flask_restx import Resource
 from flask_restx import reqparse
+from app.core.parsers import sentinel
 
 api = QuestionAlternativeDTO.api
 schema = QuestionAlternativeDTO.schema
 list_schema = QuestionAlternativeDTO.list_schema
 
-question_alternative_parser = reqparse.RequestParser()
-question_alternative_parser.add_argument("text", type=str, default=None, location="json")
-question_alternative_parser.add_argument("value", type=int, default=None, location="json")
+alternative_parser_add = reqparse.RequestParser()
+alternative_parser_add.add_argument("text", type=str, required=True, location="json")
+alternative_parser_add.add_argument("value", type=int, required=True, location="json")
+
+alternative_parser_edit = reqparse.RequestParser()
+alternative_parser_edit.add_argument("text", type=str, default=sentinel, location="json")
+alternative_parser_edit.add_argument("value", type=int, default=sentinel, location="json")
 
 
 @api.route("")
@@ -24,7 +29,7 @@ class QuestionAlternativeList(Resource):
 
     @protect_route(allowed_roles=["*"])
     def post(self, competition_id, slide_id, question_id):
-        args = question_alternative_parser.parse_args(strict=True)
+        args = alternative_parser_add.parse_args(strict=True)
         item = dbc.add.question_alternative(**args, question_id=question_id)
         return item_response(schema.dump(item))
 
@@ -39,7 +44,7 @@ class QuestionAlternatives(Resource):
 
     @protect_route(allowed_roles=["*"])
     def put(self, competition_id, slide_id, question_id, alternative_id):
-        args = question_alternative_parser.parse_args(strict=True)
+        args = alternative_parser_edit.parse_args(strict=True)
         item = dbc.get.question_alternative(competition_id, slide_id, question_id, alternative_id)
         item = dbc.edit.default(item, **args)
         return item_response(schema.dump(item))
diff --git a/server/app/apis/answers.py b/server/app/apis/answers.py
index 8f72d3bd..3990e4e1 100644
--- a/server/app/apis/answers.py
+++ b/server/app/apis/answers.py
@@ -4,19 +4,20 @@ from app.apis import item_response, list_response, protect_route
 from app.core.dto import QuestionAnswerDTO
 from flask_restx import Resource
 from flask_restx import reqparse
+from app.core.parsers import sentinel
 
 api = QuestionAnswerDTO.api
 schema = QuestionAnswerDTO.schema
 list_schema = QuestionAnswerDTO.list_schema
 
-question_answer_parser = reqparse.RequestParser()
-question_answer_parser.add_argument("answer", type=str, required=True, location="json")
-question_answer_parser.add_argument("score", type=int, required=True, location="json")
-question_answer_parser.add_argument("question_id", type=int, required=True, location="json")
+answer_parser_add = reqparse.RequestParser()
+answer_parser_add.add_argument("answer", type=str, required=True, location="json")
+answer_parser_add.add_argument("score", type=int, required=True, location="json")
+answer_parser_add.add_argument("question_id", type=int, required=True, location="json")
 
-question_answer_edit_parser = reqparse.RequestParser()
-question_answer_edit_parser.add_argument("answer", type=str, default=None, location="json")
-question_answer_edit_parser.add_argument("score", type=int, default=None, location="json")
+answer_parser_edit = reqparse.RequestParser()
+answer_parser_edit.add_argument("answer", type=str, default=sentinel, location="json")
+answer_parser_edit.add_argument("score", type=int, default=sentinel, location="json")
 
 
 @api.route("")
@@ -29,7 +30,7 @@ class QuestionAnswerList(Resource):
 
     @protect_route(allowed_roles=["*"], allowed_views=["*"])
     def post(self, competition_id, team_id):
-        args = question_answer_parser.parse_args(strict=True)
+        args = answer_parser_add.parse_args(strict=True)
         item = dbc.add.question_answer(**args, team_id=team_id)
         return item_response(schema.dump(item))
 
@@ -44,7 +45,7 @@ class QuestionAnswers(Resource):
 
     @protect_route(allowed_roles=["*"], allowed_views=["*"])
     def put(self, competition_id, team_id, answer_id):
-        args = question_answer_edit_parser.parse_args(strict=True)
+        args = answer_parser_edit.parse_args(strict=True)
         item = dbc.get.question_answer(competition_id, team_id, answer_id)
         item = dbc.edit.default(item, **args)
         return item_response(schema.dump(item))
diff --git a/server/app/apis/auth.py b/server/app/apis/auth.py
index 9c9ed24a..d97eab2a 100644
--- a/server/app/apis/auth.py
+++ b/server/app/apis/auth.py
@@ -21,14 +21,14 @@ list_schema = AuthDTO.list_schema
 
 login_parser = reqparse.RequestParser()
 login_parser.add_argument("email", type=inputs.email(), required=True, location="json")
-login_parser.add_argument("password", required=True, location="json")
+login_parser.add_argument("password", type=str, required=True, location="json")
 
 create_user_parser = login_parser.copy()
 create_user_parser.add_argument("city_id", type=int, required=True, location="json")
 create_user_parser.add_argument("role_id", type=int, required=True, location="json")
 
 login_code_parser = reqparse.RequestParser()
-login_code_parser.add_argument("code", type=str, location="json")
+login_code_parser.add_argument("code", type=str, required=True, location="json")
 
 
 def get_user_claims(item_user):
diff --git a/server/app/apis/competitions.py b/server/app/apis/competitions.py
index d1fd9572..c5ff37c9 100644
--- a/server/app/apis/competitions.py
+++ b/server/app/apis/competitions.py
@@ -6,32 +6,35 @@ from app.core.dto import CompetitionDTO
 from app.database.models import Competition
 from flask_restx import Resource
 from flask_restx import reqparse
-from app.core.parsers import search_parser
+from app.core.parsers import search_parser, sentinel
 
 api = CompetitionDTO.api
 schema = CompetitionDTO.schema
 rich_schema = CompetitionDTO.rich_schema
 list_schema = CompetitionDTO.list_schema
 
-competition_parser = reqparse.RequestParser()
-competition_parser.add_argument("name", type=str, location="json")
-competition_parser.add_argument("year", type=int, location="json")
-competition_parser.add_argument("city_id", type=int, location="json")
+competition_parser_add = reqparse.RequestParser()
+competition_parser_add.add_argument("name", type=str, required=True, location="json")
+competition_parser_add.add_argument("year", type=int, required=True, location="json")
+competition_parser_add.add_argument("city_id", type=int, required=True, location="json")
 
-competition_edit_parser = competition_parser.copy()
-competition_edit_parser.add_argument("background_image_id", default=None, type=int, location="json")
+competition_parser_edit = reqparse.RequestParser()
+competition_parser_edit.add_argument("name", type=str, default=sentinel, location="json")
+competition_parser_edit.add_argument("year", type=int, default=sentinel, location="json")
+competition_parser_edit.add_argument("city_id", type=int, default=sentinel, location="json")
+competition_parser_edit.add_argument("background_image_id", default=sentinel, type=int, location="json")
 
-competition_search_parser = search_parser.copy()
-competition_search_parser.add_argument("name", type=str, default=None, location="args")
-competition_search_parser.add_argument("year", type=int, default=None, location="args")
-competition_search_parser.add_argument("city_id", type=int, default=None, location="args")
+competition_parser_search = search_parser.copy()
+competition_parser_search.add_argument("name", type=str, default=sentinel, location="args")
+competition_parser_search.add_argument("year", type=int, default=sentinel, location="args")
+competition_parser_search.add_argument("city_id", type=int, default=sentinel, location="args")
 
 
 @api.route("")
 class CompetitionsList(Resource):
     @protect_route(allowed_roles=["*"])
     def post(self):
-        args = competition_parser.parse_args(strict=True)
+        args = competition_parser_add.parse_args(strict=True)
 
         # Add competition
         item = dbc.add.competition(**args)
@@ -52,9 +55,9 @@ class Competitions(Resource):
 
     @protect_route(allowed_roles=["*"])
     def put(self, competition_id):
-        args = competition_edit_parser.parse_args(strict=True)
+        args = competition_parser_edit.parse_args(strict=True)
         item = dbc.get.one(Competition, competition_id)
-        item = dbc.edit.competition(item, **args)
+        item = dbc.edit.default(item, **args)
 
         return item_response(schema.dump(item))
 
@@ -70,7 +73,7 @@ class Competitions(Resource):
 class CompetitionSearch(Resource):
     @protect_route(allowed_roles=["*"])
     def get(self):
-        args = competition_search_parser.parse_args(strict=True)
+        args = competition_parser_search.parse_args(strict=True)
         items, total = dbc.search.competition(**args)
         return list_response(list_schema.dump(items), total)
 
diff --git a/server/app/apis/components.py b/server/app/apis/components.py
index 43018695..32ab03c7 100644
--- a/server/app/apis/components.py
+++ b/server/app/apis/components.py
@@ -4,25 +4,29 @@ from app.apis import item_response, list_response, protect_route
 from app.core.dto import ComponentDTO
 from flask_restx import Resource
 from flask_restx import reqparse
+from app.core.parsers import sentinel
 
 api = ComponentDTO.api
 schema = ComponentDTO.schema
 list_schema = ComponentDTO.list_schema
 
+component_parser_add = reqparse.RequestParser()
+component_parser_add.add_argument("x", type=str, default=0, location="json")
+component_parser_add.add_argument("y", type=int, default=0, location="json")
+component_parser_add.add_argument("w", type=int, default=1, location="json")
+component_parser_add.add_argument("h", type=int, default=1, location="json")
+component_parser_add.add_argument("type_id", type=int, required=True, location="json")
+component_parser_add.add_argument("view_type_id", type=int, required=True, location="json")
+component_parser_add.add_argument("text", type=str, default=None, location="json")
+component_parser_add.add_argument("media_id", type=str, default=None, location="json")
 
-component_parser = reqparse.RequestParser()
-component_parser.add_argument("x", type=str, default=None, location="json")
-component_parser.add_argument("y", type=int, default=None, location="json")
-component_parser.add_argument("w", type=int, default=None, location="json")
-component_parser.add_argument("h", type=int, default=None, location="json")
-
-component_edit_parser = component_parser.copy()
-component_edit_parser.add_argument("text", type=str, location="json")
-component_edit_parser.add_argument("media_id", type=str, location="json")
-
-component_create_parser = component_edit_parser.copy()
-component_create_parser.add_argument("type_id", type=int, required=True, location="json")
-component_create_parser.add_argument("view_type_id", type=int, required=True, location="json")
+component_parser_edit = reqparse.RequestParser()
+component_parser_edit.add_argument("x", type=str, default=sentinel, location="json")
+component_parser_edit.add_argument("y", type=int, default=sentinel, location="json")
+component_parser_edit.add_argument("w", type=int, default=sentinel, location="json")
+component_parser_edit.add_argument("h", type=int, default=sentinel, location="json")
+component_parser_edit.add_argument("text", type=str, default=sentinel, location="json")
+component_parser_edit.add_argument("media_id", type=str, default=sentinel, location="json")
 
 
 @api.route("/<component_id>")
@@ -35,10 +39,10 @@ class ComponentByID(Resource):
 
     @protect_route(allowed_roles=["*"])
     def put(self, competition_id, slide_id, component_id):
-        args = component_edit_parser.parse_args(strict=True)
+        args = component_parser_edit.parse_args(strict=True)
         item = dbc.get.component(competition_id, slide_id, component_id)
-        args_without_none = {key: value for key, value in args.items() if value is not None}
-        item = dbc.edit.default(item, **args_without_none)
+        args_without_sentinel = {key: value for key, value in args.items() if value is not sentinel}
+        item = dbc.edit.default(item, **args_without_sentinel)
         return item_response(schema.dump(item))
 
     @protect_route(allowed_roles=["*"])
@@ -58,6 +62,6 @@ class ComponentList(Resource):
 
     @protect_route(allowed_roles=["*"])
     def post(self, competition_id, slide_id):
-        args = component_create_parser.parse_args()
+        args = component_parser_add.parse_args()
         item = dbc.add.component(slide_id=slide_id, **args)
         return item_response(schema.dump(item))
diff --git a/server/app/apis/media.py b/server/app/apis/media.py
index c7de8c4d..49d20608 100644
--- a/server/app/apis/media.py
+++ b/server/app/apis/media.py
@@ -10,6 +10,7 @@ from flask_restx import Resource
 from flask_uploads import UploadNotAllowed
 from sqlalchemy import exc
 import app.core.files as files
+from app.core.parsers import sentinel
 
 api = MediaDTO.api
 image_set = MediaDTO.image_set
@@ -17,7 +18,7 @@ schema = MediaDTO.schema
 list_schema = MediaDTO.list_schema
 
 media_parser_search = search_parser.copy()
-media_parser_search.add_argument("filename", type=str, default=None, location="args")
+media_parser_search.add_argument("filename", type=str, default=sentinel, location="args")
 
 
 @api.route("/images")
diff --git a/server/app/apis/questions.py b/server/app/apis/questions.py
index de40a310..b14849d2 100644
--- a/server/app/apis/questions.py
+++ b/server/app/apis/questions.py
@@ -4,15 +4,21 @@ from app.apis import item_response, list_response, protect_route
 from app.core.dto import QuestionDTO
 from flask_restx import Resource
 from flask_restx import reqparse
+from app.core.parsers import sentinel
 
 api = QuestionDTO.api
 schema = QuestionDTO.schema
 list_schema = QuestionDTO.list_schema
 
-question_parser = reqparse.RequestParser()
-question_parser.add_argument("name", type=str, default=None, location="json")
-question_parser.add_argument("total_score", type=int, default=None, location="json")
-question_parser.add_argument("type_id", type=int, default=None, location="json")
+question_parser_add = reqparse.RequestParser()
+question_parser_add.add_argument("name", type=str, default=None, location="json")
+question_parser_add.add_argument("total_score", type=int, default=None, location="json")
+question_parser_add.add_argument("type_id", type=int, required=True, location="json")
+
+question_parser_edit = reqparse.RequestParser()
+question_parser_edit.add_argument("name", type=str, default=sentinel, location="json")
+question_parser_edit.add_argument("total_score", type=int, default=sentinel, location="json")
+question_parser_edit.add_argument("type_id", type=int, default=sentinel, location="json")
 
 
 @api.route("/questions")
@@ -34,7 +40,7 @@ class QuestionListForSlide(Resource):
 
     @protect_route(allowed_roles=["*"])
     def post(self, competition_id, slide_id):
-        args = question_parser.parse_args(strict=True)
+        args = question_parser_add.parse_args(strict=True)
         item = dbc.add.question(slide_id=slide_id, **args)
         return item_response(schema.dump(item))
 
@@ -49,7 +55,7 @@ class QuestionById(Resource):
 
     @protect_route(allowed_roles=["*"])
     def put(self, competition_id, slide_id, question_id):
-        args = question_parser.parse_args(strict=True)
+        args = question_parser_edit.parse_args(strict=True)
 
         item_question = dbc.get.question(competition_id, slide_id, question_id)
         item_question = dbc.edit.default(item_question, **args)
diff --git a/server/app/apis/slides.py b/server/app/apis/slides.py
index 52553796..52ce4cce 100644
--- a/server/app/apis/slides.py
+++ b/server/app/apis/slides.py
@@ -4,16 +4,17 @@ from app.apis import item_response, list_response, protect_route
 from app.core.dto import SlideDTO
 from flask_restx import Resource
 from flask_restx import reqparse
+from app.core.parsers import sentinel
 
 api = SlideDTO.api
 schema = SlideDTO.schema
 list_schema = SlideDTO.list_schema
 
-slide_parser = reqparse.RequestParser()
-slide_parser.add_argument("order", type=int, default=None, location="json")
-slide_parser.add_argument("title", type=str, default=None, location="json")
-slide_parser.add_argument("timer", type=int, default=None, location="json")
-slide_parser.add_argument("background_image_id", default=None, type=int, location="json")
+slide_parser_edit = reqparse.RequestParser()
+slide_parser_edit.add_argument("order", type=int, default=sentinel, location="json")
+slide_parser_edit.add_argument("title", type=str, default=sentinel, location="json")
+slide_parser_edit.add_argument("timer", type=int, default=sentinel, location="json")
+slide_parser_edit.add_argument("background_image_id", default=sentinel, type=int, location="json")
 
 
 @api.route("")
@@ -40,10 +41,10 @@ class Slides(Resource):
 
     @protect_route(allowed_roles=["*"])
     def put(self, competition_id, slide_id):
-        args = slide_parser.parse_args(strict=True)
+        args = slide_parser_edit.parse_args(strict=True)
 
         item_slide = dbc.get.slide(competition_id, slide_id)
-        item_slide = dbc.edit.slide(item_slide, **args)
+        item_slide = dbc.edit.default(item_slide, **args)
 
         return item_response(schema.dump(item_slide))
 
@@ -60,7 +61,7 @@ class Slides(Resource):
 class SlideOrder(Resource):
     @protect_route(allowed_roles=["*"])
     def put(self, competition_id, slide_id):
-        args = slide_parser.parse_args(strict=True)
+        args = slide_parser_edit.parse_args(strict=True)
         order = args.get("order")
 
         item_slide = dbc.get.slide(competition_id, slide_id)
diff --git a/server/app/apis/teams.py b/server/app/apis/teams.py
index c951ae53..71ca715d 100644
--- a/server/app/apis/teams.py
+++ b/server/app/apis/teams.py
@@ -3,14 +3,17 @@ import app.database.controller as dbc
 from app.apis import item_response, list_response, protect_route
 from app.core.dto import TeamDTO
 from flask_restx import Resource, reqparse
-from flask_restx import reqparse
+from app.core.parsers import sentinel
 
 api = TeamDTO.api
 schema = TeamDTO.schema
 list_schema = TeamDTO.list_schema
 
-team_parser = reqparse.RequestParser()
-team_parser.add_argument("name", type=str, location="json")
+team_parser_add = reqparse.RequestParser()
+team_parser_add.add_argument("name", type=str, required=True, location="json")
+
+team_parser_edit = reqparse.RequestParser()
+team_parser_edit.add_argument("name", type=str, default=sentinel, location="json")
 
 
 @api.route("")
@@ -23,7 +26,7 @@ class TeamsList(Resource):
 
     @protect_route(allowed_roles=["*"])
     def post(self, competition_id):
-        args = team_parser.parse_args(strict=True)
+        args = team_parser_add.parse_args(strict=True)
         item_team = dbc.add.team(args["name"], competition_id)
         return item_response(schema.dump(item_team))
 
@@ -45,7 +48,7 @@ class Teams(Resource):
 
     @protect_route(allowed_roles=["*"])
     def put(self, competition_id, team_id):
-        args = team_parser.parse_args(strict=True)
+        args = team_parser_edit.parse_args(strict=True)
         name = args.get("name")
 
         item_team = dbc.get.team(competition_id, team_id)
diff --git a/server/app/apis/users.py b/server/app/apis/users.py
index 50470db7..dc26ac5b 100644
--- a/server/app/apis/users.py
+++ b/server/app/apis/users.py
@@ -5,23 +5,23 @@ from app.core.dto import UserDTO
 from flask_jwt_extended import get_jwt_identity
 from flask_restx import Resource
 from flask_restx import inputs, reqparse
-from app.core.parsers import search_parser
+from app.core.parsers import search_parser, sentinel
 
 api = UserDTO.api
 schema = UserDTO.schema
 list_schema = UserDTO.list_schema
 
-user_parser = reqparse.RequestParser()
-user_parser.add_argument("email", type=inputs.email(), location="json")
-user_parser.add_argument("name", type=str, location="json")
-user_parser.add_argument("city_id", type=int, location="json")
-user_parser.add_argument("role_id", type=int, location="json")
+user_parser_edit = reqparse.RequestParser()
+user_parser_edit.add_argument("email", type=inputs.email(), default=sentinel, location="json")
+user_parser_edit.add_argument("name", type=str, default=sentinel, location="json")
+user_parser_edit.add_argument("city_id", type=int, default=sentinel, location="json")
+user_parser_edit.add_argument("role_id", type=int, default=sentinel, location="json")
 
 user_search_parser = search_parser.copy()
-user_search_parser.add_argument("name", type=str, default=None, location="args")
-user_search_parser.add_argument("email", type=str, default=None, location="args")
-user_search_parser.add_argument("city_id", type=int, default=None, location="args")
-user_search_parser.add_argument("role_id", type=int, default=None, location="args")
+user_search_parser.add_argument("name", type=str, default=sentinel, location="args")
+user_search_parser.add_argument("email", type=str, default=sentinel, location="args")
+user_search_parser.add_argument("city_id", type=int, default=sentinel, location="args")
+user_search_parser.add_argument("role_id", type=int, default=sentinel, location="args")
 
 
 def _edit_user(item_user, args):
@@ -47,7 +47,7 @@ class UsersList(Resource):
 
     @protect_route(allowed_roles=["*"])
     def put(self):
-        args = user_parser.parse_args(strict=True)
+        args = user_parser_edit.parse_args(strict=True)
         item = dbc.get.user(get_jwt_identity())
         item = _edit_user(item, args)
         return item_response(schema.dump(item))
@@ -63,7 +63,7 @@ class Users(Resource):
 
     @protect_route(allowed_roles=["Admin"])
     def put(self, ID):
-        args = user_parser.parse_args(strict=True)
+        args = user_parser_edit.parse_args(strict=True)
         item = dbc.get.user(ID)
         item = _edit_user(item, args)
         return item_response(schema.dump(item))
diff --git a/server/app/core/parsers.py b/server/app/core/parsers.py
index c695dd23..160d67a0 100644
--- a/server/app/core/parsers.py
+++ b/server/app/core/parsers.py
@@ -1,5 +1,22 @@
 from flask_restx import inputs, reqparse
 
+
+class Sentinel:
+    """
+    Sentinel is used as default argument to parsers if it isn't necessary to
+    supply a value. This is used instead of None so that None can be supplied
+    as value.
+    """
+
+    def __repr__(self):
+        return "Sentinel"
+
+    def __bool__(self):
+        return False
+
+
+sentinel = Sentinel()
+
 ###SEARCH####
 search_parser = reqparse.RequestParser()
 search_parser.add_argument("page", type=int, default=0, location="args")
diff --git a/server/app/database/controller/edit.py b/server/app/database/controller/edit.py
index 94bc6008..efb49096 100644
--- a/server/app/database/controller/edit.py
+++ b/server/app/database/controller/edit.py
@@ -3,6 +3,7 @@ This file contains functionality to get data from the database.
 """
 
 from app.core import db
+from app.core.parsers import sentinel
 
 
 def switch_order(item1, item2):
@@ -46,22 +47,8 @@ def default(item, **kwargs):
     for key, value in kwargs.items():
         if not hasattr(item, key):
             raise AttributeError(f"Item of type {type(item)} has no attribute '{key}'")
-        if value is not None:
+        if value is not sentinel:
             setattr(item, key, value)
     db.session.commit()
     db.session.refresh(item)
     return item
-
-
-def competition(item, **kwargs):
-    if kwargs["background_image_id"] == -1:
-        item.background_image_id = None
-        del kwargs["background_image_id"]
-    return default(item, **kwargs)
-
-
-def slide(item, **kwargs):
-    if kwargs["background_image_id"] == -1:
-        item.background_image_id = None
-        del kwargs["background_image_id"]
-    return default(item, **kwargs)
\ No newline at end of file
-- 
GitLab