Skip to content

Commit 8dc3ebb

Browse files
Merge pull request #18 from piyush-jaiswal/feature/db-error-handling-category
Feature/db error handling category
2 parents e5a815a + 819a812 commit 8dc3ebb

File tree

2 files changed

+57
-12
lines changed

2 files changed

+57
-12
lines changed

app/migrated_routes/category.py

Lines changed: 42 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,16 @@
11
from flask.views import MethodView
22
from flask_jwt_extended import jwt_required
33
from flask_smorest import Blueprint, abort
4-
from sqlalchemy import exists
4+
from psycopg2.errors import UniqueViolation
5+
from sqlalchemy import UniqueConstraint, exists
6+
from sqlalchemy.exc import IntegrityError
57

68
from app import db
79
from app.models import (
810
Category,
911
Product,
1012
Subcategory,
1113
category_subcategory,
12-
subcategory_product,
1314
)
1415
from app.schemas import (
1516
CategoriesOut,
@@ -27,6 +28,19 @@
2728
class CategoryCollection(MethodView):
2829
init_every_request = False
2930

31+
@staticmethod
32+
def _get_name_unique_constraint():
33+
name_col = Category.__table__.c.name
34+
return next(
35+
con
36+
for con in Category.__table__.constraints
37+
if isinstance(con, UniqueConstraint)
38+
and len(con.columns) == 1
39+
and con.columns.contains_column(name_col)
40+
)
41+
42+
_NAME_UNIQUE_CONSTRAINT = _get_name_unique_constraint()
43+
3044
@bp.response(200, CategoriesOut)
3145
def get(self):
3246
"""
@@ -88,8 +102,18 @@ def post(self, data):
88102

89103
category.subcategories = subcategories
90104

91-
db.session.add(category)
92-
db.session.commit()
105+
try:
106+
db.session.add(category)
107+
db.session.commit()
108+
except IntegrityError as ie:
109+
db.session.rollback()
110+
if (
111+
isinstance(ie.orig, UniqueViolation)
112+
and ie.orig.diag.constraint_name
113+
== CategoryCollection._NAME_UNIQUE_CONSTRAINT.name
114+
):
115+
abort(409, message="Category with this name already exists")
116+
raise
93117

94118
return category
95119

@@ -176,7 +200,18 @@ def put(self, data, id):
176200

177201
category.subcategories.extend(subcategories)
178202

179-
db.session.commit()
203+
try:
204+
db.session.commit()
205+
except IntegrityError as ie:
206+
db.session.rollback()
207+
if (
208+
isinstance(ie.orig, UniqueViolation)
209+
and ie.orig.diag.constraint_name
210+
== category_subcategory.primary_key.name
211+
):
212+
abort(409, message="Category and subcategory already linked")
213+
raise
214+
180215
return category
181216

182217
@jwt_required()
@@ -277,14 +312,9 @@ def get(self, id, page):
277312
abort(404)
278313

279314
products = (
280-
Product.query.join(subcategory_product)
281-
.join(
282-
category_subcategory,
283-
onclause=subcategory_product.c.subcategory_id
284-
== category_subcategory.c.subcategory_id,
315+
Product.query.filter(
316+
Product.subcategories.any(Subcategory.categories.any(id=id))
285317
)
286-
.filter(category_subcategory.c.category_id == id)
287-
.distinct()
288318
.order_by(Product.id.asc())
289319
.paginate(page=page, per_page=CategoryProducts._PER_PAGE, error_out=False)
290320
)

tests/test_relationships.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1+
import sqlite3
2+
13
import pytest
4+
from sqlalchemy.exc import IntegrityError
25

36
from app.models import Category, Product, Subcategory
47

@@ -96,6 +99,18 @@ def test_update_category_adds_subcategories(self, create_authenticated_headers,
9699

97100
assert self._category_subcategory_ids(category["id"]) == sorted([subcategory1["id"], subcategory2["id"]])
98101

102+
def test_update_category_adds_linked_subcategories(self, create_authenticated_headers, create_category, create_subcategory):
103+
headers = create_authenticated_headers()
104+
subcategory = create_subcategory("U_SC1", headers=headers).get_json()
105+
category = create_category("U_Cat", subcategories=[subcategory["id"]], headers=headers).get_json()
106+
107+
with pytest.raises(IntegrityError) as ie:
108+
self.client.put(f"/categories/{category['id']}", json={"subcategories": [subcategory["id"]]}, headers=headers)
109+
110+
assert isinstance(ie.value.orig, sqlite3.IntegrityError)
111+
assert "UNIQUE constraint failed" in str(ie.value.orig)
112+
assert self._category_subcategory_ids(category["id"]) == [subcategory["id"]]
113+
99114
def test_update_subcategory_adds_categories_and_products(self, create_authenticated_headers, create_category, create_product, create_subcategory):
100115
category1 = create_category("UC1").get_json()
101116
category2 = create_category("UC2").get_json()

0 commit comments

Comments
 (0)