SQLA2: WIP
This commit is contained in:
parent
64799ccc61
commit
b7111d8a3b
@ -137,31 +137,31 @@
|
|||||||
# return min(trim_ms, len(audio_segment))
|
# return min(trim_ms, len(audio_segment))
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
# def ms_to_mmss(ms: int, decimals: int = 0, negative: bool = False) -> str:
|
def ms_to_mmss(ms: int, decimals: int = 0, negative: bool = False) -> str:
|
||||||
# """Convert milliseconds to mm:ss"""
|
"""Convert milliseconds to mm:ss"""
|
||||||
#
|
|
||||||
# minutes: int
|
minutes: int
|
||||||
# remainder: int
|
remainder: int
|
||||||
# seconds: float
|
seconds: float
|
||||||
#
|
|
||||||
# if not ms:
|
if not ms:
|
||||||
# return "-"
|
return "-"
|
||||||
# sign = ""
|
sign = ""
|
||||||
# if ms < 0:
|
if ms < 0:
|
||||||
# if negative:
|
if negative:
|
||||||
# sign = "-"
|
sign = "-"
|
||||||
# else:
|
else:
|
||||||
# ms = 0
|
ms = 0
|
||||||
#
|
|
||||||
# minutes, remainder = divmod(ms, 60 * 1000)
|
minutes, remainder = divmod(ms, 60 * 1000)
|
||||||
# seconds = remainder / 1000
|
seconds = remainder / 1000
|
||||||
#
|
|
||||||
# # if seconds >= 59.5, it will be represented as 60, which looks odd.
|
# if seconds >= 59.5, it will be represented as 60, which looks odd.
|
||||||
# # So, fake it under those circumstances
|
# So, fake it under those circumstances
|
||||||
# if seconds >= 59.5:
|
if seconds >= 59.5:
|
||||||
# seconds = 59.0
|
seconds = 59.0
|
||||||
#
|
|
||||||
# return f"{sign}{minutes:.0f}:{seconds:02.{decimals}f}"
|
return f"{sign}{minutes:.0f}:{seconds:02.{decimals}f}"
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
# def open_in_audacity(path: str) -> Optional[bool]:
|
# def open_in_audacity(path: str) -> Optional[bool]:
|
||||||
|
|||||||
@ -275,8 +275,12 @@ class Playlists(Base):
|
|||||||
name: str = Column(String(32), nullable=False, unique=True)
|
name: str = Column(String(32), nullable=False, unique=True)
|
||||||
last_used = Column(DateTime, default=None, nullable=True)
|
last_used = Column(DateTime, default=None, nullable=True)
|
||||||
loaded: bool = Column(Boolean, default=True, nullable=False)
|
loaded: bool = Column(Boolean, default=True, nullable=False)
|
||||||
rows = relationship("PlaylistRows", back_populates="playlist",
|
rows = relationship(
|
||||||
cascade="all, delete-orphan")
|
"PlaylistRows",
|
||||||
|
back_populates="playlist",
|
||||||
|
cascade="all, delete-orphan",
|
||||||
|
order_by="PlaylistRows.row_number"
|
||||||
|
)
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
return f"<Playlists(id={self.id}, name={self.name}>"
|
return f"<Playlists(id={self.id}, name={self.name}>"
|
||||||
@ -317,10 +321,6 @@ class Playlists(Base):
|
|||||||
# ).all()
|
# ).all()
|
||||||
#
|
#
|
||||||
# @classmethod
|
# @classmethod
|
||||||
# def get_by_id(cls, session: Session, playlist_id: int) -> "Playlists":
|
|
||||||
# return (session.query(cls).filter(cls.id == playlist_id)).one()
|
|
||||||
#
|
|
||||||
# @classmethod
|
|
||||||
# def get_closed(cls, session: Session) -> List["Playlists"]:
|
# def get_closed(cls, session: Session) -> List["Playlists"]:
|
||||||
# """Returns a list of all closed playlists ordered by last use"""
|
# """Returns a list of all closed playlists ordered by last use"""
|
||||||
#
|
#
|
||||||
@ -529,7 +529,7 @@ class Tracks(Base):
|
|||||||
silence_at = Column(Integer, index=False)
|
silence_at = Column(Integer, index=False)
|
||||||
path = Column(String(2048), index=False, nullable=False)
|
path = Column(String(2048), index=False, nullable=False)
|
||||||
mtime = Column(Float, index=True)
|
mtime = Column(Float, index=True)
|
||||||
lastplayed = Column(DateTime, index=True, default=None)
|
# lastplayed = Column(DateTime, index=True, default=None)
|
||||||
playlistrows = relationship("PlaylistRows", back_populates="track")
|
playlistrows = relationship("PlaylistRows", back_populates="track")
|
||||||
playlists = association_proxy("playlistrows", "playlist")
|
playlists = association_proxy("playlistrows", "playlist")
|
||||||
playdates = relationship("Playdates", back_populates="track")
|
playdates = relationship("Playdates", back_populates="track")
|
||||||
|
|||||||
206
app/playlists.py
206
app/playlists.py
@ -4,7 +4,7 @@ from collections import namedtuple
|
|||||||
# from typing import Dict, List, Optional, Set, Tuple, Union
|
# from typing import Dict, List, Optional, Set, Tuple, Union
|
||||||
#
|
#
|
||||||
# from PyQt5 import QtCore
|
# from PyQt5 import QtCore
|
||||||
# from PyQt5.QtCore import Qt
|
from PyQt5.QtCore import Qt
|
||||||
# from PyQt5.Qt import QFont
|
# from PyQt5.Qt import QFont
|
||||||
# from PyQt5.QtGui import QColor, QDropEvent
|
# from PyQt5.QtGui import QColor, QDropEvent
|
||||||
# from PyQt5 import QtWidgets
|
# from PyQt5 import QtWidgets
|
||||||
@ -21,7 +21,7 @@ from PyQt5.QtWidgets import (
|
|||||||
QTableWidgetItem,
|
QTableWidgetItem,
|
||||||
)
|
)
|
||||||
#
|
#
|
||||||
# import helpers
|
import helpers
|
||||||
# import os
|
# import os
|
||||||
# import re
|
# import re
|
||||||
# import subprocess
|
# import subprocess
|
||||||
@ -34,8 +34,8 @@ from config import Config
|
|||||||
from models import (
|
from models import (
|
||||||
# Notes,
|
# Notes,
|
||||||
# Playdates,
|
# Playdates,
|
||||||
# Playlists,
|
Playlists,
|
||||||
# PlaylistTracks,
|
PlaylistRows,
|
||||||
Settings,
|
Settings,
|
||||||
# Tracks,
|
# Tracks,
|
||||||
# NoteColours
|
# NoteColours
|
||||||
@ -55,16 +55,18 @@ from dbconfig import Session
|
|||||||
# Columns
|
# Columns
|
||||||
Column = namedtuple("Column", ['idx', 'heading'])
|
Column = namedtuple("Column", ['idx', 'heading'])
|
||||||
columns = {}
|
columns = {}
|
||||||
columns["userdata"] = Column(idx=0, heading=Config.COLUMN_NAME_AUTOPLAY),
|
columns["userdata"] = Column(idx=0, heading=Config.COLUMN_NAME_AUTOPLAY)
|
||||||
columns["mss"] = Column(idx=1, heading=Config.COLUMN_NAME_LEADING_SILENCE),
|
columns["start_gap"] = Column(
|
||||||
columns["title"] = Column(idx=2, heading=Config.COLUMN_NAME_TITLE),
|
idx=1, heading=Config.COLUMN_NAME_LEADING_SILENCE)
|
||||||
columns["artist"] = Column(idx=3, heading=Config.COLUMN_NAME_ARTIST),
|
columns["title"] = Column(idx=2, heading=Config.COLUMN_NAME_TITLE)
|
||||||
columns["duration"] = Column(idx=4, heading=Config.COLUMN_NAME_LENGTH),
|
columns["artist"] = Column(idx=3, heading=Config.COLUMN_NAME_ARTIST)
|
||||||
columns["start_time"] = Column(idx=5, heading=Config.COLUMN_NAME_START_TIME),
|
columns["duration"] = Column(idx=4, heading=Config.COLUMN_NAME_LENGTH)
|
||||||
columns["end_time"] = Column(idx=6, heading=Config.COLUMN_NAME_END_TIME),
|
columns["start_time"] = Column(idx=5, heading=Config.COLUMN_NAME_START_TIME)
|
||||||
columns["last_played"] = Column(idx=7, heading=Config.COLUMN_NAME_LAST_PLAYED),
|
columns["end_time"] = Column(idx=6, heading=Config.COLUMN_NAME_END_TIME)
|
||||||
columns["row_notes"] = Column(idx=8, heading=Config.COLUMN_NAME_NOTES),
|
columns["lastplayed"] = Column(idx=7, heading=Config.COLUMN_NAME_LAST_PLAYED)
|
||||||
#
|
columns["row_notes"] = Column(idx=8, heading=Config.COLUMN_NAME_NOTES)
|
||||||
|
|
||||||
|
|
||||||
# class NoSelectDelegate(QStyledItemDelegate):
|
# class NoSelectDelegate(QStyledItemDelegate):
|
||||||
# """https://stackoverflow.com/questions/72790705/dont-select-text-in-qtablewidget-cell-when-editing/72792962#72792962"""
|
# """https://stackoverflow.com/questions/72790705/dont-select-text-in-qtablewidget-cell-when-editing/72792962#72792962"""
|
||||||
#
|
#
|
||||||
@ -85,10 +87,10 @@ class PlaylistTab(QTableWidget):
|
|||||||
# cellEditingStarted = QtCore.pyqtSignal(int, int)
|
# cellEditingStarted = QtCore.pyqtSignal(int, int)
|
||||||
# cellEditingEnded = QtCore.pyqtSignal()
|
# cellEditingEnded = QtCore.pyqtSignal()
|
||||||
|
|
||||||
# # Qt.UserRoles
|
# Qt.UserRoles
|
||||||
# ROW_METADATA = Qt.UserRole
|
ROW_METADATA = Qt.UserRole
|
||||||
# CONTENT_OBJECT = Qt.UserRole + 1
|
# CONTENT_OBJECT = Qt.UserRole + 1
|
||||||
# ROW_DURATION = Qt.UserRole + 2
|
ROW_DURATION = Qt.UserRole + 2
|
||||||
|
|
||||||
def __init__(self, musicmuster: QMainWindow, session: Session,
|
def __init__(self, musicmuster: QMainWindow, session: Session,
|
||||||
playlist_id: int, *args, **kwargs):
|
playlist_id: int, *args, **kwargs):
|
||||||
@ -119,8 +121,8 @@ class PlaylistTab(QTableWidget):
|
|||||||
self._set_column_widths(session)
|
self._set_column_widths(session)
|
||||||
# Set column headings sorted by idx
|
# Set column headings sorted by idx
|
||||||
self.setHorizontalHeaderLabels(
|
self.setHorizontalHeaderLabels(
|
||||||
[a[0].heading for a in list(sorted(columns.values(),
|
[a.heading for a in list(sorted(columns.values(),
|
||||||
key=lambda item: item[0][0]))]
|
key=lambda item: item.idx))]
|
||||||
)
|
)
|
||||||
|
|
||||||
# self.setDragEnabled(True)
|
# self.setDragEnabled(True)
|
||||||
@ -153,8 +155,8 @@ class PlaylistTab(QTableWidget):
|
|||||||
# self.doubleClicked.connect(self._edit_cell)
|
# self.doubleClicked.connect(self._edit_cell)
|
||||||
self.horizontalHeader().sectionResized.connect(self._column_resize)
|
self.horizontalHeader().sectionResized.connect(self._column_resize)
|
||||||
#
|
#
|
||||||
# # Now load our tracks and notes
|
# Now load our tracks and notes
|
||||||
# self.populate(session, self.playlist_id)
|
self.populate(session, self.playlist_id)
|
||||||
|
|
||||||
def _column_resize(self, idx, old, new):
|
def _column_resize(self, idx, old, new):
|
||||||
"""
|
"""
|
||||||
@ -165,7 +167,7 @@ class PlaylistTab(QTableWidget):
|
|||||||
|
|
||||||
with Session() as session:
|
with Session() as session:
|
||||||
for column_name, data in columns.items():
|
for column_name, data in columns.items():
|
||||||
idx = data[0].idx
|
idx = data.idx
|
||||||
width = self.columnWidth(idx)
|
width = self.columnWidth(idx)
|
||||||
attribute_name = f"playlist_{column_name}_col_width"
|
attribute_name = f"playlist_{column_name}_col_width"
|
||||||
record = Settings.get_int_settings(session, attribute_name)
|
record = Settings.get_int_settings(session, attribute_name)
|
||||||
@ -350,6 +352,87 @@ class PlaylistTab(QTableWidget):
|
|||||||
# return self.item(row, self.COL_TITLE).text()
|
# return self.item(row, self.COL_TITLE).text()
|
||||||
# else:
|
# else:
|
||||||
# return None
|
# return None
|
||||||
|
|
||||||
|
def insert_row(self, session: Session, row_data: PlaylistRows,
|
||||||
|
repaint: bool = True) -> None:
|
||||||
|
"""
|
||||||
|
Insert a row into playlist tab.
|
||||||
|
|
||||||
|
If playlist has a row selected, add new row above. Otherwise,
|
||||||
|
add to end of playlist.
|
||||||
|
|
||||||
|
Note: we ignore the row number in the PlaylistRows record. That is
|
||||||
|
used only to order the query that generates the records.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if self.selectionModel().hasSelection():
|
||||||
|
row = self.currentRow()
|
||||||
|
else:
|
||||||
|
row = self.rowCount()
|
||||||
|
self.insertRow(row)
|
||||||
|
|
||||||
|
# Add row metadata to userdata column
|
||||||
|
item: QTableWidgetItem = QTableWidgetItem()
|
||||||
|
item.setData(self.ROW_METADATA, 0)
|
||||||
|
self.setItem(row, columns['userdata'].idx, item)
|
||||||
|
|
||||||
|
# Prepare notes, start and end items for later
|
||||||
|
notes_item = QTableWidgetItem(row_data.note)
|
||||||
|
start_item = QTableWidgetItem()
|
||||||
|
end_item = QTableWidgetItem()
|
||||||
|
|
||||||
|
if row_data.track_id:
|
||||||
|
# Add track details to items
|
||||||
|
start_gap = row_data.track.start_gap
|
||||||
|
start_gap_item = QTableWidgetItem(str(start_gap))
|
||||||
|
if start_gap and start_gap >= 500:
|
||||||
|
start_gap_item.setBackground(QColor(Config.COLOUR_LONG_START))
|
||||||
|
|
||||||
|
title_item = QTableWidgetItem(row_data.track.title)
|
||||||
|
|
||||||
|
artist_item = QTableWidgetItem(row_data.track.artist)
|
||||||
|
|
||||||
|
duration_item = QTableWidgetItem(
|
||||||
|
helpers.ms_to_mmss(row_data.track.duration))
|
||||||
|
self._set_row_duration(row, row_data.track.duration)
|
||||||
|
|
||||||
|
last_playtime = Playdates.last_played(session, row_data.track.id)
|
||||||
|
last_played_str = get_relative_date(last_playtime)
|
||||||
|
last_played_item = QTableWidgetItem(last_played_str)
|
||||||
|
self.setItem(row, columns['lastplayed'], last_played_item)
|
||||||
|
|
||||||
|
# Mark track if file is unreadable
|
||||||
|
if not self._file_is_readable(row_data.track.path):
|
||||||
|
self._set_unreadable_row(row)
|
||||||
|
|
||||||
|
else:
|
||||||
|
# This is a note row so make empty items (row background
|
||||||
|
# won't be coloured without items present)
|
||||||
|
start_gap_item = QTableWidgetItem()
|
||||||
|
title_item = QTableWidgetItem()
|
||||||
|
artist_item = QTableWidgetItem()
|
||||||
|
duration_item = QTableWidgetItem()
|
||||||
|
last_played_item = QTableWidgetItem()
|
||||||
|
|
||||||
|
# Add items to table
|
||||||
|
self.setItem(row, columns['start_gap'].idx, start_gap_item)
|
||||||
|
self.setItem(row, columns['title'].idx, title_item)
|
||||||
|
self.setItem(row, columns['artist'].idx, artist_item)
|
||||||
|
self.setItem(row, columns['duration'].idx, duration_item)
|
||||||
|
self.setItem(row, columns['start_time'].idx, start_item)
|
||||||
|
self.setItem(row, columns['end_time'].idx, end_item)
|
||||||
|
self.setItem(row, columns['row_notes'].idx, notes_item)
|
||||||
|
|
||||||
|
if not row_data.track_id:
|
||||||
|
# Span note across table
|
||||||
|
self.setSpan(row, 0, len(columns), 1)
|
||||||
|
|
||||||
|
# Scroll to new row
|
||||||
|
self.scrollToItem(title_item, QAbstractItemView.PositionAtCenter)
|
||||||
|
|
||||||
|
if repaint:
|
||||||
|
self.save_playlist(session)
|
||||||
|
self.update_display(session, clear_selection=False)
|
||||||
#
|
#
|
||||||
# def insert_track(self, session: Session, track: Tracks,
|
# def insert_track(self, session: Session, track: Tracks,
|
||||||
# repaint: bool = True) -> None:
|
# repaint: bool = True) -> None:
|
||||||
@ -525,50 +608,37 @@ class PlaylistTab(QTableWidget):
|
|||||||
#
|
#
|
||||||
# self._clear_current_track_row()
|
# self._clear_current_track_row()
|
||||||
# self.current_track_start_time = None
|
# self.current_track_start_time = None
|
||||||
#
|
|
||||||
# def populate(self, session: Session, playlist_id: int) -> None:
|
def populate(self, session: Session, playlist_id: int) -> None:
|
||||||
# """
|
"""
|
||||||
# Populate from the associated playlist ID
|
Populate from the associated playlist ID
|
||||||
#
|
"""
|
||||||
# We don't mandate that an item will be on its specified row, only
|
|
||||||
# that it will be above larger-numbered row items, and below
|
# data: List[Union[Tuple[List[int], Tracks], Tuple[List[int], Notes]]] \
|
||||||
# lower-numbered ones.
|
# = []
|
||||||
# """
|
# item: Union[Notes, Tracks]
|
||||||
#
|
# note: Notes
|
||||||
# data: List[Union[Tuple[List[int], Tracks], Tuple[List[int], Notes]]] \
|
# row: int
|
||||||
# = []
|
# track: Tracks
|
||||||
# item: Union[Notes, Tracks]
|
|
||||||
# note: Notes
|
playlist = session.get(Playlists, playlist_id)
|
||||||
# row: int
|
|
||||||
# track: Tracks
|
# Clear playlist
|
||||||
#
|
self.setRowCount(0)
|
||||||
# playlist = Playlists.get_by_id(session, playlist_id)
|
|
||||||
#
|
# Add the rows
|
||||||
# for row, track in playlist.tracks.items():
|
for row in playlist.rows:
|
||||||
# data.append(([row], track))
|
self.insert_row(session, row, repaint=False)
|
||||||
# for note in playlist.notes:
|
|
||||||
# data.append(([note.row], note))
|
# Scroll to top
|
||||||
#
|
scroll_to: QTableWidgetItem = self.item(0, 0)
|
||||||
# # Clear playlist
|
self.scrollToItem(scroll_to, QAbstractItemView.PositionAtTop)
|
||||||
# self.setRowCount(0)
|
|
||||||
#
|
# We possibly don't need to save the playlist here, but row
|
||||||
# # Now add data in row order
|
# numbers may have changed during population, and it's cheap to do
|
||||||
# for i in sorted(data, key=lambda x: x[0]):
|
# KAE self.save_playlist(session)
|
||||||
# item = i[1]
|
self.update_display(session)
|
||||||
# if isinstance(item, Tracks):
|
|
||||||
# self.insert_track(session, item, repaint=False)
|
|
||||||
# elif isinstance(item, Notes):
|
|
||||||
# self._insert_note(session, item, repaint=False)
|
|
||||||
#
|
|
||||||
# # Scroll to top
|
|
||||||
# scroll_to: QTableWidgetItem = self.item(0, 0)
|
|
||||||
# self.scrollToItem(scroll_to, QAbstractItemView.PositionAtTop)
|
|
||||||
#
|
|
||||||
# # We possibly don't need to save the playlist here, but row
|
|
||||||
# # numbers may have changed during population, and it's cheap to do
|
|
||||||
# self.save_playlist(session)
|
|
||||||
# self.update_display(session)
|
|
||||||
#
|
|
||||||
# def save_playlist(self, session) -> None:
|
# def save_playlist(self, session) -> None:
|
||||||
# """
|
# """
|
||||||
# Save playlist to database.
|
# Save playlist to database.
|
||||||
@ -1614,7 +1684,7 @@ class PlaylistTab(QTableWidget):
|
|||||||
"""Column widths from settings"""
|
"""Column widths from settings"""
|
||||||
|
|
||||||
for column_name, data in columns.items():
|
for column_name, data in columns.items():
|
||||||
idx = data[0].idx
|
idx = data.idx
|
||||||
attr_name = f"playlist_{column_name}_col_width"
|
attr_name = f"playlist_{column_name}_col_width"
|
||||||
record: Settings = Settings.get_int_settings(session, attr_name)
|
record: Settings = Settings.get_int_settings(session, attr_name)
|
||||||
if record and record.f_int is not None:
|
if record and record.f_int is not None:
|
||||||
|
|||||||
9
ipython_commands.txt
Normal file
9
ipython_commands.txt
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
from sqlalchemy.orm import (sessionmaker, scoped_session)
|
||||||
|
s = sessionmaker(bind=engine)
|
||||||
|
from dbconfig import engine
|
||||||
|
s = sessionmaker(bind=engine)
|
||||||
|
s
|
||||||
|
playlist in s
|
||||||
|
s = scoped_session(sessionmaker(bind=engine))
|
||||||
|
playlist_id = 3
|
||||||
|
playlist = Playlists.get_by_id(session, playlist_id)
|
||||||
@ -0,0 +1,54 @@
|
|||||||
|
"""schema changes for row notes
|
||||||
|
|
||||||
|
Revision ID: 3b063011ed67
|
||||||
|
Revises: 51f61433256f
|
||||||
|
Create Date: 2022-07-06 19:48:23.960471
|
||||||
|
|
||||||
|
"""
|
||||||
|
from alembic import op
|
||||||
|
import sqlalchemy as sa
|
||||||
|
from sqlalchemy.dialects import mysql
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision = '3b063011ed67'
|
||||||
|
down_revision = '51f61433256f'
|
||||||
|
branch_labels = None
|
||||||
|
depends_on = None
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade():
|
||||||
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
|
op.drop_table('notes')
|
||||||
|
op.add_column('playlist_rows', sa.Column('note', sa.String(length=2048), nullable=True))
|
||||||
|
op.alter_column('playlist_rows', 'track_id',
|
||||||
|
existing_type=mysql.INTEGER(display_width=11),
|
||||||
|
nullable=True)
|
||||||
|
op.drop_index('uniquerow', table_name='playlist_rows')
|
||||||
|
op.drop_column('playlist_rows', 'text')
|
||||||
|
op.alter_column('playlist_rows', 'row', new_column_name='row_number',
|
||||||
|
existing_type=mysql.INTEGER(display_width=11),
|
||||||
|
nullable=False)
|
||||||
|
op.create_index('uniquerow', 'playlist_rows', ['row_number', 'playlist_id'], unique=True)
|
||||||
|
# ### end Alembic commands ###
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade():
|
||||||
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
|
op.alter_column('playlist_rows', 'row_number', new_column_name='row',
|
||||||
|
existing_type=mysql.INTEGER(display_width=11),
|
||||||
|
nullable=False)
|
||||||
|
op.add_column('playlist_rows', sa.Column('text', mysql.VARCHAR(length=2048), nullable=True))
|
||||||
|
op.drop_index('uniquerow', table_name='playlist_rows')
|
||||||
|
op.create_index('uniquerow', 'playlist_rows', ['row', 'playlist_id'], unique=False)
|
||||||
|
op.drop_column('playlist_rows', 'note')
|
||||||
|
op.create_table('notes',
|
||||||
|
sa.Column('id', mysql.INTEGER(display_width=11), autoincrement=True, nullable=False),
|
||||||
|
sa.Column('playlist_id', mysql.INTEGER(display_width=11), autoincrement=False, nullable=True),
|
||||||
|
sa.Column('row', mysql.INTEGER(display_width=11), autoincrement=False, nullable=False),
|
||||||
|
sa.Column('note', mysql.VARCHAR(length=256), nullable=True),
|
||||||
|
sa.ForeignKeyConstraint(['playlist_id'], ['playlists.id'], name='notes_ibfk_1'),
|
||||||
|
sa.PrimaryKeyConstraint('id'),
|
||||||
|
mysql_default_charset='utf8mb4',
|
||||||
|
mysql_engine='InnoDB'
|
||||||
|
)
|
||||||
|
# ### end Alembic commands ###
|
||||||
94
play.py
Executable file
94
play.py
Executable file
@ -0,0 +1,94 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
from sqlalchemy import create_engine
|
||||||
|
from sqlalchemy import text
|
||||||
|
from sqlalchemy import Table, Column, Integer, String
|
||||||
|
from sqlalchemy import ForeignKey
|
||||||
|
from sqlalchemy import select
|
||||||
|
from sqlalchemy import insert
|
||||||
|
from sqlalchemy.orm import Session
|
||||||
|
from sqlalchemy.orm import declarative_base
|
||||||
|
from sqlalchemy.orm import relationship
|
||||||
|
|
||||||
|
|
||||||
|
Base = declarative_base()
|
||||||
|
|
||||||
|
engine = create_engine("sqlite+pysqlite:///:memory:", echo=True, future=True)
|
||||||
|
|
||||||
|
|
||||||
|
class User(Base):
|
||||||
|
__tablename__ = 'user_account'
|
||||||
|
id = Column(Integer, primary_key=True)
|
||||||
|
name = Column(String(30))
|
||||||
|
fullname = Column(String)
|
||||||
|
addresses = relationship("Address", back_populates="user")
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return (
|
||||||
|
f"User(id={self.id!r}, name={self.name!r}, "
|
||||||
|
f"fullname={self.fullname!r})"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class Address(Base):
|
||||||
|
__tablename__ = 'address'
|
||||||
|
id = Column(Integer, primary_key=True)
|
||||||
|
email_address = Column(String, nullable=False)
|
||||||
|
user_id = Column(Integer, ForeignKey('user_account.id'))
|
||||||
|
user = relationship("User", back_populates="addresses")
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return f"Address(id={self.id!r}, email_address={self.email_address!r})"
|
||||||
|
|
||||||
|
|
||||||
|
Base.metadata.create_all(engine)
|
||||||
|
|
||||||
|
squidward = User(name="squidward", fullname="Squidward Tentacles")
|
||||||
|
krabs = User(name="ehkrabs", fullname="Eugene H. Krabs")
|
||||||
|
|
||||||
|
session = Session(engine)
|
||||||
|
|
||||||
|
session.add(squidward)
|
||||||
|
session.add(krabs)
|
||||||
|
|
||||||
|
session.commit()
|
||||||
|
|
||||||
|
u1 = User(name='pkrabs', fullname='Pearl Krabs')
|
||||||
|
a1 = Address(email_address="pearl.krabs@gmail.com")
|
||||||
|
u1.addresses.append(a1)
|
||||||
|
a2 = Address(email_address="pearl@aol.com", user=u1)
|
||||||
|
|
||||||
|
session.add(u1)
|
||||||
|
session.add(a1)
|
||||||
|
session.add(a2)
|
||||||
|
|
||||||
|
session.commit()
|
||||||
|
|
||||||
|
|
||||||
|
# with engine.connect() as conn:
|
||||||
|
# conn.execute(text("CREATE TABLE some_table (x int, y int)"))
|
||||||
|
# conn.execute(
|
||||||
|
# text("INSERT INTO some_table (x, y) VALUES (:x, :y)"),
|
||||||
|
# [{"x": 1, "y": 1}, {"x": 2, "y": 4}]
|
||||||
|
# )
|
||||||
|
# conn.commit()
|
||||||
|
#
|
||||||
|
# with engine.begin() as conn:
|
||||||
|
# conn.execute(
|
||||||
|
# text("INSERT INTO some_table (x, y) VALUES (:x, :y)"),
|
||||||
|
# [{"x": 6, "y": 8}, {"x": 9, "y": 10}]
|
||||||
|
# )
|
||||||
|
#
|
||||||
|
# # with engine.connect() as conn:
|
||||||
|
# # result = conn.execute(text("SELECT x, y FROM some_table"))
|
||||||
|
# # for row in result:
|
||||||
|
# # print(f"x: {row.x} y: {row.y}")
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# stmt = text(
|
||||||
|
# "SELECT x, y FROM some_table WHERE y > :y ORDER BY x, y").bindparams(y=6)
|
||||||
|
#
|
||||||
|
# with Session(engine) as session:
|
||||||
|
# result = session.execute(stmt)
|
||||||
|
# for row in result:
|
||||||
|
# print(f"x: {row.x} y: {row.y}")
|
||||||
Loading…
Reference in New Issue
Block a user