2015-07-15 13 views
5

Was ist der richtige Weg, Factory Boy mit Flask-SQLAlchemy und Foreign Key Einschränkungen zu verwenden?Wie richte ich abhängige Fabriken mit Factory Boy und Flask-SQLAlchemy ein?

Betrachten Sie das folgende Flask SQLAlchemy Modell Setup:

# coding=utf-8 
from flask import Flask 
from flask.ext.sqlalchemy import SQLAlchemy 

app = Flask(__name__) 
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///test.db' 
db = SQLAlchemy(app) 

# ------------------------------ 
# SQLAlchemy Table Models 
# ------------------------------ 
class User(db.Model): 
    """ A SQLAlchemy simple model class who represents a user with a ForeignKey Constraint""" 
    __tablename__ = 'UserTable' 

    user_pk = db.Column(db.Integer(), primary_key=True) 
    name = db.Column(db.Unicode(20)) 
    group_fk = db.Column(db.ForeignKey("GroupTable.group_pk"), nullable=False) 


class Group(db.Model): 
    """ A SQLAlchemy simple model class who represents a user """ 
    __tablename__ = 'GroupTable' 

    group_pk = db.Column(db.Integer(), primary_key=True) 
    name = db.Column(db.String(), nullable=False) 


# ------------------------- 
# Create the SQL tables 
# ------------------------- 
db.create_all() 

Der Nutzer Schema erfordert eine Gruppe Fremdschlüssel beim Anlegen eines neuen Benutzers. Da der Gruppenprimärschlüssel aus der Datenbank zugewiesen wird, muss die Factory einen Gruppeneintrag festschreiben und den Primärschlüssel des Eintrags abrufen, damit dieser dem neuen Benutzer bereitgestellt werden kann.

Wie erstelle ich eine Gruppe, speichere sie in der DB und lege sie der User Factory zur Verfügung?

Factory Boy has examples for dealing with Foreign Keys, aber sie scheinen nicht für SQLAlchemy gelten. Hier sind die Fabriken und die Fehlerpunkt:

# ---------------------------------------- 
# Factory-Boy User and Group Factories 
# ---------------------------------------- 
from factory import alchemy, Sequence, RelatedFactory 


class GroupFactory(alchemy.SQLAlchemyModelFactory): 
    class Meta(object): 
     model = Group 
     sqlalchemy_session = db.session # the SQLAlchemy session object 

    name = Sequence(lambda n: "Group {}".format(n)) 
    # group_pk = Sequence(lambda n: n) 


class UserFactory(alchemy.SQLAlchemyModelFactory): 
    class Meta(object): 
     model = User 
     sqlalchemy_session = db.session # the SQLAlchemy session object 

    user_pk = Sequence(lambda n: n) 
    name = Sequence(lambda n: u'User %d' % n) # coding=utf-8 
    group_fk = RelatedFactory(GroupFactory) 


# ---------------------- 
# Factory tests 
# ---------------------- 
# Create a new Group from our factory 
group_from_factory = GroupFactory(name='a new group name') 
assert group_from_factory.group_pk is None 
# Save it to our DB 
db.session.add(group_from_factory) 
db.session.commit() 

# Verify that Group Saved correctly to DB 
group_from_db = db.session.query(Group).filter(Group.group_pk == group_from_factory.group_pk).first() 
assert group_from_db.group_pk is not None 
assert group_from_db.name == 'a new group name' 
assert group_from_db.group_pk == group_from_factory.group_pk 

# Create a new User from our factory 
user_from_factory = UserFactory() 
db.session.add(user_from_factory) 
# ---------------------------------------------- 
# FAILS AT COMMIT() - NOT NULL constraint failed (group_fk is null) 
# ---------------------------------------------- 
db.session.commit() 

assert user_from_factory.user_pk is not None 
assert user_from_factory.name is not None 
assert user_from_factory.group_fk is not None 
+0

** ** SubFactory funktioniert nicht. Siehe dazu Kommentare. Laut dem Factory Boy api sollte es, aber das tut es nicht, weshalb ich darauf gepostet habe. – etiology

Antwort

5

Das Problem kommt von der Verwendung eines RelatedFactory: diejenigen bestimmt sind, für ReverseForeignKey Beziehungen (z, wenn Sie ein Group Objekt bauen wollen, die bereits ein User enthält).

Für eine direkte ForeignKey - wie das Verhältnis von User zu Group, verwenden Sie ein SubFactory:

class UserFactory(factory.alchemy.SQLAlchemyModelFactory): 
    class Meta: 
     model = User 
     sqlalchemy_session = db.session 

    # No need to force the user_pk, it is built automatically from the database 
    # user_pk = Sequence(lambda n: n) 
    name = Sequence(lambda n: u'User %d' % n) # coding=utf-8 
    group_fk = factory.SubFactory(GroupFactory) 

Ich bin nicht sehr vertraut mit Flask-SQLAlchemy, aber ich habe hinzugefügt, nur ein kleines Beispiel zu den Repository (unter), das funktioniert, ist Ihrer Situation ziemlich ähnlich.

+0

Danke für den Vorschlag, aber SubFactory löst das nicht. SQL gibt einen "nicht unterstützten Typ" -Fehler aus, da die SubFactory eine Gruppeninstanz anstelle einer Ganzzahl zurückgibt. Was ich also brauche, ist, den Group.group_pk Integer-Wert an das User.group_fk-Attribut zu übermitteln, damit SQL beim Ausführen der INSERT-Anweisung nicht ausflippt. Auch das von SubFactory zurückgegebene Gruppenobjekt ist ** noch nicht in der Datenbank gespeichert. ** – etiology

+0

sqlalchemy.exc.InterfaceError: (sqlite3.InterfaceError) Fehlerbindeparameter 1 - wahrscheinlich nicht unterstützter Typ. [SQL: INSERT INTO "UserTable" (Name, group_fk) VALUES (?,?) '] [Parameter: (u'User 0', <__ main__.Group Objekt bei 0x101c20650>)] – etiology

+0

Der Github-Code, den Sie geschrieben haben groß. Es sieht so aus, als könnte dies der Weg sein. Es erfordert eine Änderung im SQLAlchemy-Tabellenmodell, weshalb ich SubFactory nicht als Lösung sah. Es ist eine Lösung, wenn das Tabellenmodell in einer bestimmten Weise konstruiert wird, was eine bessere Konstruktion ist. Ich brauche mehr Zeit, um es zu überprüfen, bevor ich die Antwort markiere, aber das ist gut. – etiology

2

Sie können LazyAttribute und ein Lambda verwenden, um eine neue Gruppe zu generieren, und ziehen Sie dann 'group_pk' heraus.

Arbeits Version des Codes unter:

# coding=utf-8 
from flask import Flask 
from flask.ext.sqlalchemy import SQLAlchemy 

app = Flask(__name__) 
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///test.db' 
db = SQLAlchemy(app) 

# ------------------------------ 
# SQLAlchemy Table Models 
# ------------------------------ 
class User(db.Model): 
    """ A SQLAlchemy simple model class who represents a user with a ForeignKey Constraint""" 
    __tablename__ = 'UserTable' 

    user_pk = db.Column(db.Integer(), primary_key=True) 
    name = db.Column(db.Unicode(20)) 
    group_fk = db.Column(db.ForeignKey("GroupTable.group_pk"), nullable=False) 


class Group(db.Model): 
    """ A SQLAlchemy simple model class who represents a user """ 
    __tablename__ = 'GroupTable' 

    group_pk = db.Column(db.Integer(), primary_key=True) 
    name = db.Column(db.String(), nullable=False) 


# ------------------------- 
# Create the SQL tables 
# ------------------------- 
db.drop_all() 
db.create_all() 
# ---------------------------------------- 
# Factory-Boy User and Group Factories 
# ---------------------------------------- 
from factory import alchemy, Sequence, LazyAttribute 


class GroupFactory(alchemy.SQLAlchemyModelFactory): 
    class Meta(object): 
     model = Group 
     sqlalchemy_session = db.session # the SQLAlchemy session object 

    name = Sequence(lambda n: "Group {}".format(n)) 
    group_pk = Sequence(lambda n: n) 


class UserFactory(alchemy.SQLAlchemyModelFactory): 
    class Meta(object): 
     model = User 
     sqlalchemy_session = db.session # the SQLAlchemy session object 

    user_pk = Sequence(lambda n: n) 
    name = Sequence(lambda n: u'User %d' % n) # coding=utf-8 
    group_fk = LazyAttribute(lambda a: GroupFactory().group_pk) 


# ---------------------- 
# Factory tests 
# ---------------------- 
# Create a new Group from our factory 
group_from_factory = GroupFactory(name='a new group name') 
# Save it to our DB 
db.session.add(group_from_factory) 
db.session.commit() 

# Verify that Group Saved correctly to DB 
group_from_db = db.session.query(Group).filter(Group.group_pk == group_from_factory.group_pk).first() 
assert group_from_db.group_pk is not None 
assert group_from_db.name == 'a new group name' 
assert group_from_db.group_pk == group_from_factory.group_pk 

# Create a new User from our factory 
user_from_factory = UserFactory() 
db.session.add(user_from_factory) 
# ---------------------------------------------- 
# FAILS AT COMMIT() - NOT NULL constraint failed (group_fk is null) 
# ---------------------------------------------- 
db.session.commit() 

assert user_from_factory.user_pk is not None 
assert user_from_factory.name is not None 
assert user_from_factory.group_fk is not None 
3

Xelnor der git Link zeigt die beste Antwort so weit, aber es erfordert Änderungen an der sqlalchemy Modell. Hier ist das fertige Arbeitskopie von meinem Beitrag mit Xelnor Lösung:

# coding=utf-8 
from flask import Flask 
from flask.ext.sqlalchemy import SQLAlchemy 

app = Flask(__name__) 
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///test.db' 
db = SQLAlchemy(app) 

SQLAlchemy Tabelle Modelle

class User(db.Model): 
    """ A SQLAlchemy simple model class who represents a user with a ForeignKey Constraint""" 
    __tablename__ = 'user' 

    id = db.Column(db.Integer(), primary_key=True) 
    name = db.Column(db.Unicode(20)) 

    group_id = db.Column(db.Integer, db.ForeignKey('group.id'), nullable=False) 

Die ‚Gruppe‘ db.relationship ist, was die Arbeit SubFactory Anruf tätigt. UserFactory übergibt die Gruppe an das Benutzermodell, das mit dieser relation() - Definition eingerichtet wird.

group = db.relationship('Group', backref=db.backref('groups', lazy='dynamic')) 

    def __init__(self, name, group): 
     self.name = name 
     self.group = group 

    def __repr__(self): 
     return '<Group for %r: %s>' % (self.group, self.name) 


class Group(db.Model): 
    """ A SQLAlchemy simple model class who represents a user """ 
    __tablename__ = 'group' 

    id = db.Column(db.Integer(), primary_key=True) 
    name = db.Column(db.String(), nullable=False) 

    def __init__(self, name): 
     self.name = name 

    def __repr__(self): 
     return '<Group %r>' % self.name 

Erstellen der SQL-Tabellen

db.create_all() 

Fabrik-Boy Benutzer- und Gruppen Fabriken

from factory import alchemy, Sequence, SubFactory, fuzzy 


class BaseFactory(alchemy.SQLAlchemyModelFactory): 
    class Meta(object): 
     abstract = True 
     sqlalchemy_session = db.session 


class GroupFactory(BaseFactory): 
    class Meta(object): 
     model = Group 
     sqlalchemy_session = db.session # the SQLAlchemy session object 

    name = fuzzy.FuzzyText() 


class UserFactory(BaseFactory): 
    class Meta: 
     model = User 
     sqlalchemy_session = db.session 

    name = fuzzy.FuzzyText() 
    group = SubFactory(GroupFactory) 

Werksprüfungen

# Create a new Group from our factory 
group_from_factory = GroupFactory(name='a new group name') 
assert group_from_factory.id is None 
# Save it to our DB 
db.session.add(group_from_factory) 
db.session.commit() 

# Verify that Group Saved correctly to DB 
group_from_db = db.session.query(Group).filter(Group.id == group_from_factory.id).first() 
assert group_from_db.id is not None 
assert group_from_db.name == 'a new group name' 
assert group_from_db.id == group_from_factory.id 

# Create a new User from our factory 
user1_from_factory = UserFactory(name=u'first') 
user2_from_factory = UserFactory(name=u'second') 
db.session.add(user1_from_factory) 
db.session.add(user2_from_factory) 
db.session.commit() 

assert user1_from_factory.id is not None 
assert user1_from_factory.name is not None 
assert user1_from_factory.id is not None 

assert user2_from_factory.id is not None 
assert user2_from_factory.name is not None 
assert user2_from_factory.id is not None