处理一对多关系时如何使用SQLAlchemy ORM插入?

时间:2019-04-28 19:01:58

标签: orm foreign-keys flask-sqlalchemy

我有一个简单的表结构,其中包含一个父表(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}]

0 个答案:

没有答案