Skip to content
This repository was archived by the owner on Dec 19, 2021. It is now read-only.

Commit 7b2ca06

Browse files
Implement strategy for unique validations (#391)
* Implement strategy to use unique validations * Refactor app to use validation context * Update schemas/property.py Co-authored-by: Chris Boe <cboe15@gmail.com> Co-authored-by: Chris Boe <cboe15@gmail.com>
1 parent 92fee6d commit 7b2ca06

File tree

12 files changed

+35
-22
lines changed

12 files changed

+35
-22
lines changed

models/base_model.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ class BaseModel(db.Model):
1212
db.DateTime, onupdate=datetime.utcnow, default=datetime.utcnow, nullable=False
1313
)
1414

15+
def validation_context(self):
16+
return {}
17+
1518
@classmethod
1619
def find(cls, id):
1720
return cls.query.get_or_404(id, f"{cls._name()} not found")
@@ -31,8 +34,10 @@ def create(cls, schema, payload):
3134

3235
return obj
3336

34-
def update(self, schema, payload, context=None):
35-
attrs = self.validate(schema, payload, context=context, partial=True)
37+
def update(self, schema, payload):
38+
attrs = self.validate(
39+
schema, payload, context=self.validation_context(), partial=True
40+
)
3641

3742
for k, v in attrs.items():
3843
setattr(self, k, v)

models/emergency_contact.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@ class EmergencyContactModel(BaseModel):
1919
collection_class=NobiruList,
2020
)
2121

22+
def validation_context(self):
23+
return {"name": self.name}
24+
2225
def json(self):
2326
return {
2427
"id": self.id,

models/property.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,9 @@ class PropertyModel(BaseModel):
3333
collection_class=NobiruList,
3434
)
3535

36+
def validation_context(self):
37+
return {"name": self.name}
38+
3639
def json(self, include_tenants=False, include_managers=True):
3740
property = {
3841
"id": self.id,

models/user.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,9 @@ def password(self, plaintext_password):
7373
bcrypt.gensalt(current_app.config["WORK_FACTOR"]),
7474
)
7575

76+
def validation_context(self):
77+
return {"email": self.email}
78+
7679
def update_last_active(self):
7780
self.lastActive = datetime.utcnow()
7881
db.session.commit()

resources/property.py

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,7 @@ def delete(self, id):
2020
def put(self, id):
2121
property = PropertyModel.find(id)
2222

23-
return property.update(
24-
schema=PropertySchema,
25-
context={"name": property.name},
26-
payload=request.json,
27-
).json()
23+
return property.update(schema=PropertySchema, payload=request.json).json()
2824

2925

3026
class Properties(Resource):

schemas/emergency_contact.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,10 @@ class Meta:
1212
contact_numbers = fields.List(fields.Nested(ContactNumberSchema), required=True)
1313

1414
@validates("name")
15-
def validate_name(self, value):
16-
if EmergencyContactModel.find_by_name(value):
15+
def validates_uniqueness_of_name(self, value):
16+
if self.context.get("name") != value and EmergencyContactModel.find_by_name(
17+
value
18+
):
1719
raise ValidationError(f"{value} is already an emergency contact")
1820

1921
@validates("contact_numbers")

schemas/property.py

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,11 @@ def validates_property_manager_ids(self, value):
2727

2828
@validates("name")
2929
def validates_uniqueness_of_name(self, value):
30-
def _assigning_name():
31-
if "name" in self.context and value == self.context["name"]:
32-
return False
33-
return True
34-
35-
if _assigning_name() and PropertyModel.find_by_name(value):
30+
if self.context.get("name") != value and PropertyModel.find_by_name(value):
3631
raise ValidationError("A property with this name already exists")
32+
33+
@validates("name")
34+
def validates_presence_of_name(self, value):
3735
if blank(value):
3836
raise ValidationError("Property name cannot be blank")
3937

schemas/user.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
from ma import ma
2-
from models.user import UserModel, RoleEnum
32
from marshmallow import fields, validates, ValidationError
43

4+
from models.user import UserModel, RoleEnum
5+
56

67
class UserSchema(ma.SQLAlchemyAutoSchema):
78
class Meta:
@@ -12,7 +13,7 @@ class Meta:
1213

1314
@validates("email")
1415
def validate_uniqueness_of_email(self, value):
15-
if UserModel.find_by_email(value):
16+
if self.context.get("email") != value and UserModel.find_by_email(value):
1617
raise ValidationError(f"A user with email '{value}' already exists")
1718

1819
def get_role_value(self, obj):

tests/integration/test_properties.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,6 @@ def test_put(self, valid_header, create_property):
4646

4747
mock_update.assert_called_once_with(
4848
schema=PropertySchema,
49-
context={"name": property.name},
5049
payload={"num_units": 2},
5150
)
5251
assert response.status_code == 200

tests/integration/test_users.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,9 @@ def test_role_update(self, valid_header, create_unauthorized_user):
132132
payload = {"role": RoleEnum.ADMIN.value}
133133
self.client.patch(f"api/user/{id}", json=payload, headers=valid_header)
134134

135-
assert Admin.find(id).role == RoleEnum.ADMIN
135+
user = Admin.find(id)
136+
assert user.role == RoleEnum.ADMIN
137+
assert user.type == "admin"
136138

137139

138140
@pytest.mark.usefixtures("client_class", "empty_test_db")

0 commit comments

Comments
 (0)