Статьи

Простой SqlAlchemy 0,7 / 0,8 учебник

Пару лет назад я написал довольно некорректное руководство по SQLAlchemy. Я решил, что пришло время пересмотреть это руководство с нуля и, надеюсь, лучше справиться с ним на этот раз. Поскольку я музыкальный болван, мы будем создавать простую базу данных для хранения информации об альбомах. База данных не является базой данных без каких-либо связей, поэтому мы создадим две таблицы и соединим их. Вот еще несколько вещей, которые мы будем изучать:

  • Добавление данных в каждую таблицу
  • Изменение данных
  • Удаление данных
  • Основные запросы

Но сначала нам нужно создать базу данных, поэтому мы и начнем наше путешествие. Обратите внимание, что SQLAlchemy является сторонним пакетом, поэтому вам нужно установить его, если вы хотите следовать.

Как создать базу данных

Создать базу данных с помощью SQLAlchemy очень просто. Теперь они полностью используют декларативный метод создания баз данных, поэтому мы не будем рассматривать старый метод. Вы можете прочитать код здесь, а затем мы объясним его после листинга. Если вам нужен способ просмотра базы данных SQLite, я бы порекомендовал плагин SQLite Manager для Firefox. Или вы можете использовать простое приложение wxPython, которое я создал месяц назад.

# table_def.py
from sqlalchemy import create_engine, ForeignKey
from sqlalchemy import Column, Date, Integer, String
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import relationship, backref

engine = create_engine('sqlite:///mymusic.db', echo=True)
Base = declarative_base()

########################################################################
class Artist(Base):
    """"""
    __tablename__ = "artists"

    id = Column(Integer, primary_key=True)
    name = Column(String)  

    #----------------------------------------------------------------------
    def __init__(self, name):
        """"""
        self.name = name    

########################################################################
class Album(Base):
    """"""
    __tablename__ = "albums"

    id = Column(Integer, primary_key=True)
    title = Column(String)
    release_date = Column(Date)
    publisher = Column(String)
    media_type = Column(String)

    artist_id = Column(Integer, ForeignKey("artists.id"))
    artist = relationship("Artist", backref=backref("albums", order_by=id))

    #----------------------------------------------------------------------
    def __init__(self, title, release_date, publisher, media_type):
        """"""
        self.title = title
        self.release_date = release_date
        self.publisher = publisher
        self.media_type = media_type

# create tables
Base.metadata.create_all(engine)

Если вы запустите этот код, вы должны увидеть следующий вывод, отправленный на стандартный вывод:

2012-06-27 16: 34: 24,479 ИНФОРМАЦИЯ sqlalchemy.engine.base.Engine PRAGMA table_info («artist»)
2012-06-27 16: 34: 24,479 INFO sqlalchemy.engine.base.Engine ()
2012-06-27 16: 34: 24,480 ИНФОРМАЦИЯ sqlalchemy.engine.base.Engine PRAGMA table_info («альбомы»)
2012-06-27 16: 34: 24,480 ИНФОРМАЦИЯ sqlalchemy.engine.base.Engine ()
2012-06-27 16: 34: 24,480 ИНФОРМАЦИЯ sqlalchemy.engine.base.Engine
CREATE TABLE исполнителей (
id INTEGER NOT NULL,
имя VARCHAR,
PRIMARY KEY (id)
)

2012-06-27 16: 34: 24,483 INFO sqlalchemy.engine.base.Engine ()
2012-06-27 16: 34: 24,558 INFO sqlalchemy.engine.base.Engine COMMIT
2012-06-27 16: 34: 24,559 INFO sqlalchemy.engine.base.Engine
CREATE TABLE альбомы (
id INTEGER NOT NULL,
название VARCHAR,
release_date DATE,
издатель VARCHAR,
media_type VARCHAR,
artist_id INTEGER, первичный ключ
(id),
иностранный ключ (artist_id) ссылки на артистов (id
)

2012-06-27 16: 34: 24,559 ИНФОРМАЦИЯ sqlalchemy.engine.base.Engine ()
2012-06-27 16: 34: 24,615 ИНФОРМАЦИЯ sqlalchemy.engine.base.Engine COMMIT

Почему это случилось? Потому что когда мы создали объект engine, мы установили для его параметра echo значение True. Двигатель , где информация соединения с базой данных , и она имеет всю DBAPI вещи в нем , что делает общение с возможными в вашей базе данных. Вы заметите, что мы создаем базу данных SQLite. Начиная с Python 2.5, SQLite поддерживается языком. Если вы хотите подключиться к какой-либо другой базе данных, вам нужно отредактировать строку подключения. На тот случай, если вы не понимаете, о чем мы говорим, вот код, о котором идет речь:

engine = create_engine('sqlite:///mymusic.db', echo=True)

Строка ‘sqlite: ///mymusic.db’ является нашей строкой подключения. Затем мы создаем экземпляр декларативной базы, на которой мы будем основывать наши классы таблиц. Затем у нас есть два класса Artist и Album, которые определяют, как будут выглядеть таблицы нашей базы данных. Вы заметите, что у нас есть столбцы, но нет имен столбцов. SQLAlchemy фактически использовал имена переменных в качестве имен столбцов, если вы не укажете их в определении столбца. Вы заметите, что мы используем целочисленное поле «id» в качестве нашего первичного ключа в обоих классах. Это поле будет автоматически увеличиваться. Другие столбцы довольно понятны, пока вы не дойдете до ForeignKey. Здесь вы увидите, что мы привязываем artist_id к идентификатору в Artistстол. Отношения директива говорит SQLAlchemy , чтобы связать класс / таблицу Альбом для таблицы Artist. Благодаря тому, как мы настроили ForeignKey, директива отношений сообщает SQLAlchemy, что это отношение многие-к-одному , и именно этого мы и хотим. Много альбомов одному исполнителю. Вы можете прочитать больше о табличных отношениях здесь .

Последняя строка скрипта создаст таблицы в базе данных. Если вы запустите этот скрипт несколько раз, он не сделает ничего нового после первого раза, поскольку таблицы уже созданы. Вы можете добавить еще одну таблицу, и тогда она создаст новую.

Как вставить / добавить данные в ваши таблицы

База данных не очень полезна, если в ней нет данных. В этом разделе мы покажем вам, как подключиться к вашей базе данных и добавить некоторые данные в две таблицы. Намного проще взглянуть на некоторый код и затем объяснить его, так что давайте сделаем это!

import datetime
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from table_def import Album, Artist

engine = create_engine('sqlite:///mymusic.db', echo=True)

# create a Session
Session = sessionmaker(bind=engine)
session = Session()

# Create an artist
new_artist = Artist("Newsboys")
new_artist.albums = [Album("Read All About It", 
                           datetime.date(1988,12,01),
                           "Refuge", "CD")]

# add more albums
more_albums = [Album("Hell Is for Wimps",
                     datetime.date(1990,07,31),
                     "Star Song", "CD"),
               Album("Love Liberty Disco", 
                     datetime.date(1999,11,16),
                     "Sparrow", "CD"),
               Album("Thrive",
                     datetime.date(2002,03,26),
                     "Sparrow", "CD")]
new_artist.albums.extend(more_albums)

# Add the record to the session object
session.add(new_artist)
# commit the record the database
session.commit()

# Add several artists
session.add_all([
    Artist("MXPX"),
    Artist("Kutless"),
    Artist("Thousand Foot Krutch")
    ])
session.commit()

Сначала нам нужно импортировать наши определения таблиц из предыдущего скрипта. Затем мы подключаемся к базе данных с помощью нашего движка и создаем что-то новое, объект Session. Сессия — это наш дескриптор базы данных, и мы будем с ней взаимодействовать. Мы используем его для создания, изменения и удаления записей, а также используем сеансы для запросов к базе данных. Затем мы создаем объект Artist и добавляем альбом. Вы заметите, что для добавления альбома вы просто создаете список объектов Album и устанавливаете для этого свойства свойство «album» объекта Artist или можете расширить его, как вы видите во второй части примера. В конце сценария мы добавляем еще трех исполнителей, используя add_all . Как вы, наверное, уже заметили, вам нужно использовать коммит сеансового объектаспособ записи данных в базу данных. Теперь пришло время обратить наше внимание на изменение данных.

Как изменить записи с помощью SQLAlchemy

Что произойдет, если вы сохранили плохие данные. Например, вы неправильно ввели название вашего любимого альбома или неправильно указали дату выпуска для этого фан-издания? Ну, вы должны узнать, как изменить эту запись! На самом деле это станет отправной точкой в ​​изучении запросов SQLAlchemy, поскольку вам нужно найти запись, которую нужно изменить, а это значит, что вам нужно написать запрос для нее. Вот некоторый код, который показывает нам путь:

from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from table_def import Album, Artist

engine = create_engine('sqlite:///mymusic.db', echo=True)

# create a Session
Session = sessionmaker(bind=engine)
session = Session()

# querying for a record in the Artist table
res = session.query(Artist).filter(Artist.name=="Kutless").first()
print res.name

# changing the name
res.name = "Beach Boys"
session.commit()

# editing Album data
artist, album = session.query(Artist, Album).filter(Artist.id==Album.artist_id).filter(Album.title=="Thrive").first()
album.title = "Step Up to the Microphone"
session.commit()

Our first query goes out and looks up an Artist by name using the filter method. The “.first()” tells SQLAlchemy that we only want the first result. We could have used “.all()” if we thought there would be multiple results and we wanted all of them. Anyway, this query returns an Artist object that we can manipulate. As you can see, we changed the name from “Kutless” to “Beach Boys” and then committed out changes.

Querying a joined table is a little bit more complicated. This time we wrote a query that queries both our tables. It filters using the Artist id AND the Album title. It returns two objects: an artist and an album. Once we have those, we can easily change the title for the album. Wasn’t that easy? At this point, we should probably note that if we add stuff to the session erroneously, we can rollback our changes/adds/deletes by using session.rollback(). Speaking of deleting, let’s tackle that subject!

How to Delete Records in SQLAlchemy

Sometimes you just have to delete a record. Whether it’s because you’re involved in a cover-up or because you don’t want people to know about your love of Britney Spears music, you just have to get rid of the evidence. In this section, we’ll show you how to do just that! Fortunately for us, SQLAlchemy makes deleting records really easy. Just take a look at the following code!

# deleting_data.py
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from table_def import Album, Artist

engine = create_engine('sqlite:///mymusic.db', echo=True)

# create a Session
Session = sessionmaker(bind=engine)
session = Session()

res = session.query(Artist).filter(Artist.name=="MXPX").first()

session.delete(res)
session.commit()

As you can see, all you had to do was create another SQL query to find the record you want to delete and then call session.delete(res). In this case, we deleted our MXPX record. Some people think punk will never die, but they must not know any DBAs! We’ve already seen queries in action, but let’s take a closer look and see if we can learn anything new.

The Basic SQL Queries of SQLAlchemy

SQLAlchemy provides all the queries you’ll probably ever need. We’ll be spending a little time just looking at a few of the basic ones though, such as a couple simple SELECTs, a JOINed SELECT and using the LIKE query. You’ll also learn where to go for information on other types of queries. For now, let’s look at some code:

# queries.py
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from table_def import Album, Artist

engine = create_engine('sqlite:///mymusic.db', echo=True)

# create a Session
Session = sessionmaker(bind=engine)
session = Session()

# how to do a SELECT * (i.e. all)
res = session.query(Artist).all()
for artist in res:
    print artist.name

# how to SELECT the first result
res = session.query(Artist).filter(Artist.name=="Newsboys").first()

# how to sort the results (ORDER_BY)
res = session.query(Album).order_by(Album.title).all()
for album in res:
    print album.title

# how to do a JOINed query
qry = session.query(Artist, Album)
qry = qry.filter(Artist.id==Album.artist_id)
artist, album = qry.filter(Album.title=="Thrive").first()
print

# how to use LIKE in a query
res = session.query(Album).filter(Album.publisher.like("S%a%")).all()
for item in res:
    print item.publisher

The first query we run will grab all the artists in the database (a SELECT *) and print out each of their name fields. Next you’ll see how to just do a query for a specific artist and return just the first result. The third query shows how do a SELECT * on the Album table and order the results by album title. The fourth query is the same query (a query on a JOIN) we used in our editing section except that we’ve broken it down to better fit PEP8 standards regarding line length. Another reason to break down long queries is that they become more readable and easier to fix later on if you messed something up. The last query uses LIKE, which allows us to pattern match or look for something that’s “like” a specified string. In this case, we wanted to find any records that had a Publisher that started with a capital “S”, some character, an “a” and then anything else. So this will match the publishers Sparrow and Star, for example.

SQLAlchemy also supports IN, IS NULL, NOT, AND, OR and all the other filtering keywords that most DBAs use. SQLAlchemy also supports literal SQL, scalars, etc, etc.

Wrapping Up

At this point you should know SQLAlchemy well enough to get started using it confidently. The project also has excellent documentation that you should be able to use to answer just about anything you need to know. If you get stuck, the SQLAlchemy users group / mailing list is very responsive to new users and even the main developers are there to help you figure things out.