Ein SQLAlchemy-Spickzettel | Komentor

Einführung

SQLAlchemy ist eine tiefe und mächtige Sache, die aus vielen Schichten besteht. Dieser Spickzettel hält sich an Teile der ORM-Schicht (Object Relational Mapper) und soll eine Referenz und kein Tutorial sein. Wenn Sie jedoch mit SQL vertraut sind, sollte Sie dieser Spickzettel auf einen guten Weg bringen, SQLAlchemy zu verstehen.

Grundmodelle

Ein Modell wird verwendet, um eine Datenbanktabelle zu beschreiben. Zum Beispiel:

from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import scoped_session,sessionmaker
from zope.sqlalchemy import ZopeTransactionExtension
from sqlalchemy import (
    Column,
    Integer,
    String,
    Boolean,
    ForeignKey,
    DateTime,
    Sequence,
    Float
)
import datetime

DBSession = scoped_session(sessionmaker(extension=ZopeTransactionExtension()))
Base = declarative_base()

class Book(Base):  
    __tablename__  = "books"    
    id             = Column(Integer,Sequence('book_seq'),primary_key=True) 
    name           = Column(String(50))                                    
    author_id      = Column(Integer,ForeignKey('authors.id'))              
    price          = Column(Float)
    date_added     = Column(DateTime, default=datetime.datetime.now)       
    promote        = Column(Boolean,default=False)                         

Anfragen und Interaktionen

Auswählen und Filtern

#fetch everything
lBooks = DBSession.query(Book)  #returns a Query object. 
for oBook in lBooks:
    print oBook.name

#simple filters
lBooks = DBSession.query(Book).filter_by(author_id=1) #returns all the books for a specific author

#more complex filters
lBooks = DBSession.query(Book).filter(Book.price<20) #returns all the books with price <20. Note we use filter, not filter_by

#filters can be combined
lBooks = DBSession.query(Book).filter_by(author_id=1).filter(Book.price<20) #all books by a specific author, with price<20

#logical operations can be used in filters
from sqlalchemy import or_
lBooks = DBSession.query(Book).filter(or_(Book.price<20,promote==True)) # returns all books  that cost less than 20 OR are being promoted

#ordering
from sqlalchemy import desc
DBSession.query(Book).order_by(Book.price) #get all books ordered by price
DBSession.query(Book).order_by(desc(Book.price)) #get all books ordered by price descending

#other useful things
DBSession.query(Book).count() #returns the number of books
DBSession.query(Book).offset(5) #offset the result by 5
DBSession.query(Book).limit(5) # return at most 5 books
DBSession.query(Book).first() #return the first book only or None
DBSession.query(Book).get(8) #return the Book with primary key = 8, or None 

Beziehungen

Beziehungen zwischen SQL-Tabellen werden in Form von Fremdschlüsselbeziehungen beschrieben. Aus dem Beispiel, der books Die Tabelle hat ein Fremdschlüsselfeld, das auf das ID-Feld der Tabelle authors zeigt. SQLAlchemy macht die Nutzung und Untersuchung dieser Beziehungen ziemlich einfach.

Eine zu vielen Beziehungen

Angenommen, wir verfolgen die Bücher verschiedener Autoren. Einer Autor haben kann viele Bücher.

class Book(Base):
    __tablename__  = "books"    
    id             = Column(Integer,Sequence('book_seq'),primary_key=True) 
    name           = Column(String(50))                                    
    author_id      = Column(Integer,ForeignKey('authors.id'))              
    author = relationship("Author",backref="books")             
    
class Author(Base):
    __tablename__  = "books"    
    id             = Column(Integer,Sequence('book_seq'),primary_key=True) 
    name           = Column(String(50))

Die markierte Linie konfiguriert die Beziehung zwischen den Modellen. Beachten Sie, dass "Author" ist eine Zeichenfolge. Es muss nicht sein, es kann auch eine Klasse sein. Die Verwendung einer Zeichenfolge hier beseitigt die Möglichkeit bestimmter NameErrors. Beachten Sie, dass die Beziehung in beide Richtungen in einer Zeile konfiguriert wird. Auf den Autor eines Buches kann über die zugegriffen werden author -Attribut, und auf die Bücher eines Autors kann über das des Autors zugegriffen werden books Attribut.

Hier sind einige Möglichkeiten, wie Sie die Beziehung nutzen können, sobald sie konfiguriert ist:

oBook = DBSession.query(Book).filter_by(name="Harry Potter and the methods of rationality").first()
oAuthor = oBook.author   


oAuthor = DBSession.query(Author).filter_by(name="Orsan Scott Card")
for oBook in oAuthor.books:
    print oBook.name


oNewBook = Book()
oBook.name = "Ender's Game"
oBook.author = oAuthor


oNewBook = Book()
oBook.name = "Ender's Shadow"
oAuthor.books.append(oBook)

Eins-zu-eins-Beziehungen

class Parent(Base):
    __tablename__ = 'parent'
    id = ColumnColumn(Integer,Sequence('p_seq'),primary_key=True) 
    child_id = Column(Integer, ForeignKey('child.id'))
    child = relationship("Child", backref=backref("parent", uselist=False)) 

class Child(Base):
    __tablename__ = 'child'
    id = Column(Integer,Sequence('c_seq'),primary_key=True) 

Beachten Sie, dass die Zeile, die die Beziehung konfiguriert, einen Ausdruck statt nur eine Zeichenfolge für das Backref-Argument enthält. Wenn die Zeichenfolge "parent" verwendet wurde, wäre es eine normale Viele-zu-Eins-Beziehung. Wir können dies wie folgt nutzen:

oChild = DBSession.query(Child).get(1)
oParent = oChild.parent

oParent2 = Parent()
oParent.child = Child()

Viele zu viele Beziehungen

Eine Viele-zu-Viele-Beziehung erfordert eine zusätzliche Tabelle, um Zuordnungen zwischen Zeilen zu erstellen. Dazu gibt es zwei Möglichkeiten:

Zuerst nur Modelle verwenden:

class Category(Base):
    __tablename__ = 'categories'
    id = Column(Integer,Sequence('cat_seq'),primary_key=True) 
    name = Column(String(20))

class Product(Base):
    __tablename__ = 'products'
    id = Column(Integer,Sequence('prod_seq'),primary_key=True) 
    name = Column(String(20))
    
class Map(Base):
    __tablename__ = 'map'
    id = Column(Integer,Sequence('map_seq'),primary_key=True) 
    cat_id = Column(Integer,ForeignKey('categories.id'))
    prod_id = Column(Integer,ForeignKey('products.id'))

Hier können Sie Beziehungen zu den angeben Map Klasse. Der Vorteil dieses Ansatzes besteht darin, dass Sie die instanziieren können Map Klasse, dies ist nützlich, wenn Sie mit interagieren möchten Map Objekte auf nicht triviale Weise.

Dieser nächste Ansatz ist besser, wenn Ihre Kartentabelle nur eine Kartentabelle ist und keine komplexen Interaktionen erfordert:

map_table = Table('maps', Base.metadata,
    Column('cat_id', Integer, ForeignKey('categories.id')),
    Column('prod_id', Integer, ForeignKey('products.id'))
)

class Category(Base):
    __tablename__ = 'categories'
    id = Column(Integer,Sequence('cat_seq'),primary_key=True) 
    name = Column(String(20))

    products = relationship("Product",
                    secondary=map_table,   
                    backref="categories")

class Product(Base):
    __tablename__ = 'products'
    id = Column(Integer,Sequence('prod_seq'),primary_key=True) 
    name = Column(String(20))

Sie können die Beziehung wie folgt verwenden:


oCat = Category()
oCat.name = "Books"

oProduct = Product()
oProduct.name = "Ender's Game - Orsan Scott Card"
oCat.products.append(oProduct)

oProduct = Product()
oProduct.name = "Harry Potter and the methods of Rationality"
oProduct.categories.append(oCat)


for oProduct in oCat.products:
    print oProduct.name
    

oProduct = DBSession.query(Product).filter_by(name="")
for oCat in oProduct.categories:
    print oCat.name
    

Selbstreferenzielle Beziehungen

Manchmal haben Sie eine Tabelle mit einem Fremdschlüssel, der auf dieselbe Tabelle zeigt. Angenommen, wir haben eine Reihe von Knoten in einem gerichteten Baum. Ein Knoten kann viele Kindknoten haben, aber höchstens einen Elternknoten

class TreeNode(Base):
    __tablename__ = 'nodes'
    id = Column(Integer,Sequence('node_seq'),primary_key=True) 
    parent_id = Column(Integer,ForeignKey('nodes.id'))
    name = Column(String(20))

    children = relationship("TreeNode",
                backref=backref('parent', remote_side=[id])
            )

Sie können diese Beziehung wie jede beliebige Viele-zu-Eins-Beziehung verwenden:


oRootNode = DBSession.query(TreeNode).filter_by(parent_id=None).first()


for oChild in oRootNode.children:
    print oChild.name



oParent = TreeNode()
oParent.name = "parent"
oRootNode.children.append(oParent)

oChild = TreeNode()
oChild.name = "Child"
oChild.parent = oParent

Mehrere Beziehungen mit derselben Tabelle

class WikiPost(Base):
    __tablename__ = 'posts'
    id = Column(Integer,Sequence('post_seq'),primary_key=True) 
    name = Column(String(20))
    author_id = Column(Integer,ForeignKey('users.id'))
    editor_id = Column(Integer,ForeignKey('users.id'))

    editor = relationship("User", primaryjoin = "WikiPost.editor_id == User.id",backref="edited_posts")
    author = relationship("User", primaryjoin = "WikiPost.author_id == User.id",backref="authored_posts")

class User(Base):
    __tablename__ = 'users'
    id = Column(Integer,Sequence('usr_seq'),primary_key=True) 
    name = Column(String(20))

Sie können damit genauso interagieren wie mit zwei Viele-zu-Eins-Beziehungen.

oAuthor = DBSession.query(User).filter_by(name="Sheena O'Connell")


oPost = WikiPost()
oPost.name = "Sqlalchemy Cheat Sheet"
oPost.author = oAuthor


oEditor = DBSession.query(User).filter_by(name="Yi-Jirr Chen")
oEditor.edited_posts.append(oPost)


for oPost in oAuthor.authored_posts:
    print oPost.name
    
for oPost in oEditor.edited_posts:
    print oPost.name

Motorkonfiguration

Verbindungszeichenfolgen


`dialect+driver://username:password@host:port/database` 


'sqlite:///:memory:' 
'sqlite:////absolute/path/to/project.db')  
'sqlite:///C:\\path\\to\\project.db' 
r'sqlite:///C:\path\to\project.db' 



'postgresql://user:pass@localhost/mydatabase'
'postgresql+psycopg2://user:pass@localhost/mydatabase'
'postgresql+pg8000://user:pass@localhost/mydatabase'


'oracle://user:pass@127.0.0.1:1521/sidname'
'oracle+cx_oracle://user:pass@tnsname'


'mssql+pyodbc://user:pass@mydsn'
'mssql+pymssql://user:pass@hostname:port/dbname'

Engine, Sitzung und Basis


engine = create_engine(sConnectionString, echo=True)   


DBSession.configure(bind=engine)




Base.metadata.create_all(engine)       

Fazit

Es gibt viele weitere Möglichkeiten, Beziehungen zu konfigurieren, viele weitere Möglichkeiten, eine Datenbank abzufragen, und viele Themen, die dieser Spickzettel einfach nicht behandelt hat. SQLAlchemy ist ziemlich umfangreich, aber für viele Anwendungen müssen Sie nichts Komplexeres tun als das, was hier gezeigt wird. Wenn Sie hierher gekommen sind, um zu lernen, wie man SQLAlchemy verwendet, und Sie glauben, dass Sie in der Lage sind, die hier beschriebenen Methoden anzuwenden, dann würde ich vorschlagen, dass Sie sich als nächsten Schritt ein wenig über die Verwendung der Sitzung informieren Transaktionen zu verwalten.

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *