2017-09-02 132 views
0

我一直在尋找在SQLAlchemy中使用PyMySQL驅動程序和MariaDB 10.0在數據庫中使用InnoDB的下面的UsersAccessMapping模型中實現CONSTRAINT FOREIGN KEY ON DELETE CASCADE的方法。Flask-SQLAlchemy如何在MySQL(InnoDB)中執行級聯約束外鍵?

Python = 3.5.2 
SQLAlchemy = 1.1.13 
Flask-SQLAlchemy = 2.2 

的SQL:

CREATE TABLE Users (
    UserID int AUTO_INCREMENT, 
    Name varchar(200) NOT NULL, 
    Email varchar(200), 
    Username varchar(200) NOT NULL, 
    Password text NOT NULL, 
    Created datetime, 
    Updated datetime, 
    PRIMARY KEY (UserID) 
); 

CREATE TABLE UsersAccessLevels (
    UsersAccessLevelID int AUTO_INCREMENT, 
    LevelName varchar(100) NOT NULL, 
    AccessDescription text, 
    PRIMARY KEY (UsersAccessLevelID) 
); 

CREATE TABLE UsersAccessMapping (
    UsersAccessMappingID int AUTO_INCREMENT, 
    UserID int NOT NULL, 
    UsersAccessLevelID int NOT NULL, 
    PRIMARY KEY (UsersAccessMappingID), 
    CONSTRAINT fk_useraccess FOREIGN KEY (UserID) 
     REFERENCES Users(UserID) ON DELETE CASCADE, 
    CONSTRAINT fk_useraccess_level FOREIGN KEY (UsersAccessLevelID) 
     REFERENCES UsersAccessLevels(UsersAccessLevelID) ON DELETE CASCADE 
); 

我在我現在的models.py什麼:

from app import db 


class Users(db.Model): 
    """All users' information is stored here""" 
    __tablename__ = "Users" 
    UserID = db.Column(db.Integer(), primary_key=True) 
    Name = db.Column(db.String(200), nullable=False) 
    Email = db.Column(db.String(200)) 
    Username = db.Column(db.String(200), nullable=False) 
    Password = db.Column(db.Text, nullable=False) 
    Created = db.Column(db.DateTime) 
    Updated = db.Column(db.DateTime) 


class UsersAccessLevels(db.Model): 
    """This defines the various access levels users can have""" 
    __tablename__ = "UsersAccessLevels" 
    UsersAccessLevelID = db.Column(db.Integer, primary_key=True) 
    LevelName = db.Column(db.String(100), nullable=False) 
    AccessDescription = db.Column(db.Text) 


class UsersAccessMapping(db.Model): 
    """Each users' access level is defined here""" 
    __tablename__ = "UsersAccessMapping" 
    UsersAccessMappingID = db.Column(db.Integer, primary_key=True) 
    UserID = db.Column(db.Integer, nullable=False) 
    UsersAccessLevelID = db.Column(db.Integer, nullable=False) 
    __table_args__ = (
     db.ForeignKeyConstraint(
      ["fk_useraccess", "fk_useraccess_level"], 
      ["Users.UserID", "UsersAccessLevels.UsersAccessLevelID"], 
      ondelete="CASCADE" 
     ) 
    ) 

也有一些是錯誤的table_args語法,但我一直沒能找到它應該如何的任何例子。我發現一個非常相似,但第三個參數是一個空字典。不過,我想使用ondelete =「CASCADE」。那將如何增加?

當運行python3 manage.py db init,它拋出這樣的:

File "/srv/vortech-backend/venv/lib/python3.5/site-packages/sqlalchemy/ext/declarative/base.py", line 196, in _scan_attributes 
    "__table_args__ value must be a tuple, " 
sqlalchemy.exc.ArgumentError: __table_args__ value must be a tuple, dict, or None 

我試圖改變ondelete="cascade"到字典{"ondelete": "cascade"},但這並不能工作。它給出了與上面相同的錯誤。

更新: 的問題是,ondelete應該是元組之外,就像這樣:

__table_args__ = (
    db.ForeignKeyConstraint(
     ["fk_useraccess", "fk_useraccess_level"], 
     ["Users.UserID", "UsersAccessLevels.UsersAccessLevelID"] 
    ), 
    ondelete="CASCADE" 
) 

然而,這種變化仍然存在語法錯誤,如ondelete="CASCADE"不定義。它更改爲一個字典{"ondelete": "cascade"}拋出這個:

File "/srv/vortech-backend/venv/lib/python3.5/site-packages/sqlalchemy/sql/base.py", line 282, in _validate_dialect_kwargs 
    "named <dialectname>_<argument>, got '%s'" % k) 
TypeError: Additional arguments should be named <dialectname>_<argument>, got 'ondelete' 
+0

有趣的是,在http://docs.sqlalchemy.org/en/latest/core/constraints.html#on-update-and-on-delete,它顯示了我最初的方式(不起作用)。 –

+0

您已經鏈接到Core docs,瞭解如何將外鍵約束作爲參數傳遞給「Table」構造函數。它與你有的聲明類定義有很大的不同,如果傳遞這樣的話(就像錯誤說的那樣),它應該有一個序列或參數映射爲'__table_args__'。通常會傳遞一個元組或字典。您已經傳遞了一個'ForeignKeyConstraint',因爲您忘記在它後面添加逗號,所以您在冗餘括號中有一個表達式。而ondelete關鍵字參數將屬於'ForeignKeyConstraint'調用中,而不是它的外部。 –

+0

第一次嘗試還有很多錯誤,例如試圖爲2個不同的表創建複合外鍵。 –

回答

0

好了,一些測試和閱讀後,得到的答覆是,SQLAlchemy的做一些內部的魔法來實現它。所以,這將完成相同的結果SQL:

from app import db # The value is from: db = SQLAlchemy(app) 


class Users(db.Model): 
    """All users' information is stored here""" 
    __tablename__ = "Users" 
    UserID = db.Column(db.Integer(), primary_key=True) 
    Name = db.Column(db.String(200), nullable=False) 
    Email = db.Column(db.String(200)) 
    Username = db.Column(db.String(200), nullable=False) 
    Password = db.Column(db.Text, nullable=False) 
    Created = db.Column(db.DateTime) 
    Updated = db.Column(db.DateTime) 


class UsersAccessLevels(db.Model): 
    """This defines the various access levels users can have""" 
    __tablename__ = "UsersAccessLevels" 
    UsersAccessLevelID = db.Column(db.Integer, primary_key=True) 
    LevelName = db.Column(db.String(100), nullable=False) 
    AccessDescription = db.Column(db.Text) 


class UsersAccessMapping(db.Model): 
    """Each users' access level is defined here""" 
    __tablename__ = "UsersAccessMapping" 
    UsersAccessMappingID = db.Column(db.Integer, primary_key=True) 
    UserID = db.Column(
     db.Integer, db.ForeignKey("Users.UserID", ondelete="CASCADE"), nullable=False 
    ) 
    UsersAccessLevelID = db.Column(
     db.Integer, 
     db.ForeignKey("UsersAccessLevels.UsersAccessLevelID", ondelete="CASCADE"), 
     nullable=False 
    ) 

的約束和這樣的會被自動地與在列定義的db.ForeignKey()參數處理。它不需要直接在表上完成,就像在SQL中一樣。

外鍵的名稱似乎也是由SQLAlchemy自動生成的。下面是它看起來像在數據庫:

enter image description here