我有一个简单的表结构,其中包含一个父表(Category)和一个子表(Transfer)。
我认为我已经按照SQLAlchemy ORM指南正确设置了模型(super_category_id
关系可能是错误的,我还没有解决这个问题)。
TLDR有以下两点:
当使用表示嵌套对象的嵌套类的Transfer对象的Json发出POST请求时,SQLAlchemy ORM引擎会尝试再次插入类别,但由于类别已经存在而失败,因此违反了唯一约束。
当使用代表传输对象的Json进行POST请求时没有嵌套的Category对象,我得到一个空约束错误,因为外键(不可为空)看起来像转换成查询期间丢失
这是我第一次使用任何ORM引擎,所以我发布了所有相关代码,因为如果其中包含几个明显的错误,这也不会令我感到惊讶。
类别
class BaseModel(db.Model):
__abstract__ = True
id = db.Column(db.Integer, primary_key=True)
def get_id(model):
return model.id
class Category(BaseModel):
__tablename__ = 'category'
name = db.Column('category_name', db.String, unique=True, nullable=False)
super_category_id = db.Column(db.Integer, db.ForeignKey('category.id'))
transfers = db.relationship("Transfer", back_populates="category")
def to_dict(self):
return {
"id": self.id,
"name": self.name,
"super_category_id": self.super_category_id,
"transfers": json.dumps(self.transfers[0].to_dict() if len(self.transfers) > 0 else {})
}
@staticmethod
def from_dict(data):
c = Category()
c.id = data.get("id")
c.name = data["name"]
c.super_category_id = data.get("super_category_id")
return c
转移
class Transfer(BaseModel):
__tablename__ = 'transfer'
date = db.Column('transfer_date', db.Date, nullable=False)
amount = db.Column(db.Numeric(8, 2))
comment = db.Column(db.String)
category_id = db.Column(db.Integer, db.ForeignKey('category.id'), nullable=False)
category = db.relationship('Category', back_populates='transfers')
def to_dict(self):
return {
"id": self.id,
"date": self.date.strftime("%Y-%m-%d"),
"amount": str(self.amount),
"comment": self.comment,
"category_id": self.category_id,
"category_name": self.category.name
}
@staticmethod
def from_dict(data):
t = Transfer()
t.id = data.get("id")
t.date = data["date"]
t.amount = data["amount"]
t.comment = data.get("comment")
t.category_id = data["category_id"]
t.category = Category.from_dict(json.loads(json.dumps(data.get("category")))) if data.get("category") is not None else None
return t
API端点
@bp.route("/transfers", methods=["POST"])
def add_transfer():
data = request.get_json() or {}
if 'amount' not in data or 'category_id' not in data:
return bad_request('must include amount and category_id')
t = Transfer.from_dict(data)
print("ADDING %s" % t)
db.session.add(t)
try:
db.session.commit()
resp = jsonify(t.to_dict())
resp.status_code = 201
except IntegrityError as e:
db.session.rollback()
resp = bad_request(str(e))
resp.headers['Location'] = url_for('core.index')
return resp
第一种情况 使用嵌套类别对象发布请求
{
"amount": "60.00",
"category_id": 1,
"comment": null,
"date": "2019-04-18",
"category": {
"id": 1,
"name": "bolletta"
}
}
print
(API端点,第7行)显示以下消息:
ADDING Transfer{id:None, date:2019-04-18, amount:60.00, category_id:1, category:Category{id:1, name:bolletta, super_category_id:None}}
,错误是
psycopg2.errors.UniqueViolation) duplicate key value violates unique constraint \"category_pkey\"\nDETAIL: Key (id)=(1) already exists.
第二种情况没有嵌套类别对象的发布请求
{
"amount": "60.00",
"category_id": 1,
"comment": null,
"date": "2019-04-18"
}
print
(API端点,第7行)显示以下消息:
ADDING Transfer{id:None, date:2019-04-18, amount:60.00, category_id:1, category:None}
,错误是
(psycopg2.errors.NotNullViolation) null value in column \"category_id\" violates not-null constraint\nDETAIL: Failing row contains (8, 2019-04-18, 60.00, null, null).\n\n[SQL: INSERT INTO transfer (transfer_date, amount, comment, category_id) VALUES (%(transfer_date)s, %(amount)s, %(comment)s, %(category_id)s) RETURNING transfer.id]\n[parameters: {'transfer_date': '2019-04-18', 'amount': '60.00', 'comment': None, 'category_id': None}]