@ -1,9 +1,13 @@
from typing import List
from sqlalchemy import Column , Boolean , Float , String , Integer , ForeignKey , Table , func , ForeignKeyConstraint
import sqlalchemy
from sqlalchemy . orm import declarative_base , relationship , load_only
from database . models import Art , ArtnoID , Artist
# TODO read sensitive data from environment file or similar
SQLALCHEMY_DATABASE_URL = " postgresql://artnet_editor:G606Rm9sFEXe6wfTLxVu@127.0.0.1/artnet "
SQLALCHEMY_DATABASE_URL = " postgresql://artnet_editor:G606Rm9sFEXe6wfTLxVu@127.0.0.1/ test_ artnet"
engine = sqlalchemy . create_engine ( SQLALCHEMY_DATABASE_URL , echo = True )
SessionLocal = sqlalchemy . orm . sessionmaker ( autocommit = False , autoflush = False , bind = engine )
@ -12,8 +16,8 @@ Base = sqlalchemy.orm.declarative_base()
art_tag_table = Table ( ' art_tag ' , Base . metadata ,
Column ( ' art_id ' , Integer , ForeignKey ( ' art.id ' ) ) ,
Column ( ' tag_id ' , Integer , ForeignKey ( ' tag.tag_id ' ) ) )
Column ( ' art_id ' , Integer , ForeignKey ( ' art.id ' ) ) ,
Column ( ' tag_id ' , Integer , ForeignKey ( ' tag.tag_id ' ) ) )
artist_topic_table = Table ( ' artist_topic ' , Base . metadata ,
Column ( ' artist_id ' , Integer , ForeignKey ( ' artist.id ' ) ) ,
Column ( ' topic_id ' , Integer , ForeignKey ( ' topic.id ' ) ) )
@ -25,11 +29,11 @@ class DBPresence(Base):
name = Column ( String ( 20 ) , primary_key = True )
domain = Column ( String ( 20 ) , primary_key = True )
link = Column ( String , nullable = True )
artist_id = Column ( Integer , ForeignKey ( ' artist.id ' ), nullable = False , index = Tru e)
artist_id = Column ( Integer , ForeignKey ( ' artist.id ' , ondelete = " CASCADE " ) , nullable = Fals e)
artist = relationship ( " DBArtist " , back ref= ' presences ' , foreign_keys = [ artist_id ] )
#artists = relationship('DBArtist', foreign_keys='DBArtist.id', back_populates="presences")
arts = relationship ( " DBArt " , secondary = " art_author " , viewonly = True )
artist = relationship ( " DBArtist " , back _populates= " presences " )
arts = relationship ( " DBArt " , secondary = " art_author " , back_populates = " presences " ,
cascade = " delete, all " )
class DBArt ( Base ) :
@ -41,8 +45,9 @@ class DBArt(Base):
title = Column ( String , nullable = True )
link = Column ( String , nullable = True )
tags = relationship ( " DBTag " , secondary = art_tag_table , back_populates = " art " )
presences = relationship ( " DBPresence " , secondary = " art_author " , viewonly = True )
tags = relationship ( " DBTag " , secondary = art_tag_table , back_populates = " art " , viewonly = True )
presences = relationship ( " DBPresence " , secondary = " art_author " , back_populates = " arts " ,
cascade = " all, delete " )
class DBArtPresence ( Base ) :
@ -50,11 +55,11 @@ class DBArtPresence(Base):
presence_name = Column ( String ( 20 ) , primary_key = True )
presence_domain = Column ( String ( 20 ) , primary_key = True )
art_id = Column ( Integer , ForeignKey ( DBArt . id ), primary_key = True )
art_id = Column ( Integer , ForeignKey ( DBArt . id , ondelete = " CASCADE " ), primary_key = True )
__table_args__ = (
ForeignKeyConstraint (
[ " presence_name " , " presence_domain " ] ,
( " presence_name " , " presence_domain " ) ,
[ " presence.name " , " presence.domain " ]
) ,
)
@ -64,9 +69,10 @@ class DBArtist(Base):
__tablename__ = " artist "
id = Column ( Integer , primary_key = True )
description = Column ( String , nullable = Tru e)
description = Column ( String , nullable = Fals e)
topics = relationship ( " DBTopic " , secondary = " artist_topic " , back_populates = " artists " )
topics = relationship ( " DBTopic " , secondary = " artist_topic " , back_populates = " artists " , viewonly = True )
presences = relationship ( " DBPresence " , back_populates = " artist " , cascade = " all, delete " )
class DBTopic ( Base ) : # as of now unused
@ -76,7 +82,7 @@ class DBTopic(Base): # as of now unused
name = Column ( String ( 20 ) , unique = True , nullable = False )
description = Column ( String )
artists = relationship ( " DBArtist " , secondary = " artist_topic " , back_populates = " topics " )
artists = relationship ( " DBArtist " , secondary = " artist_topic " , back_populates = " topics " , viewonly = True )
class DBTagCategory ( Base ) :
@ -95,7 +101,9 @@ class DBTag(Base):
category_id = Column ( Integer , ForeignKey ( ' tag_category.category_id ' ) )
category = relationship ( " DBTagCategory " , backref = ' tags ' , foreign_keys = [ category_id ] )
# TODO check if cascade is required
art = relationship ( " DBArt " , secondary = art_tag_table , back_populates = " tags " )
# TODO check if cascade is required
Base . metadata . create_all ( bind = engine )
@ -103,31 +111,79 @@ Base.metadata.create_all(bind=engine)
class Database :
__db : sqlalchemy . orm . Session = None
def __get_db ( self ) - > sqlalchemy . orm . Session :
db = SessionLocal ( )
Database. __ db = SessionLocal ( ) if Database . __db is None else Database . __db
try :
return db
return Database. __ db
finally :
db . close ( )
##Database.__db.close()
pass
def __del__ ( self ) :
Database . __db . close ( )
Database . __db = None
# Art
def get_art_list ( self ) :
return self . __get_db ( ) . query ( DBArt ) . all ( ) # TODO FIX StackOverflow
def get_art_by_id ( self , art_id : int ) :
return self . __get_db ( ) . query ( DBArt ) . where ( DBArt . id == art_id ) . first ( )
def get_art_by_hash ( self , md5_hash : str ) :
return self . __get_db ( ) . query ( DBArt ) . filter ( func . lower ( DBArt . md5_hash ) == md5_hash . lower ( ) ) . first ( )
def update_art_by_id ( self , art_id : int , title : str , authors : list , path : str , tags : list , link : str ,
md5_hash : str ) :
def create_art_by_model ( self , art : ArtnoID ) :
if not ( isinstance ( art . path , str ) and len ( art . path ) > 0 ) :
raise ValueError ( " New Art must contain a path! " )
db_art = DBArt ( title = art . title , md5_hash = art . hash , path = art . path , link = art . link )
if self . get_art_by_hash ( art . hash ) is not None :
raise ValueError ( " New Art must not contain already known hashes! Is this really new? " )
db = self . __get_db ( )
db . add ( db_art )
db . commit ( )
# TODO implement saving art <-> presence relationship as given by art.presences
if art . presences is not None :
for presence in art . presences :
db_art_presence = DBArtPresence ( presence_name = presence . name , presence_domain = presence . domain ,
art_id = db_art . id )
db . add ( db_art_presence )
db . commit ( )
return db_art . id
@DeprecationWarning
def update_art_by_id ( self , art_id : int , title : str , path : str , tags : list , link : str ,
md5_hash : str , authors : list = None ) :
raise NotImplementedError
def update_art_by_hash ( self , md5_hash : str , title : str , authors : list , path : str , tags : list , link : str ) :
@DeprecationWarning
def update_art_by_hash ( self , md5_hash : str , title : str , path : str , tags : list , link : str , authors : list = None ) :
raise NotImplementedError
def update_art_by_model ( self , art : Art ) :
db_art : DBArt = self . get_art_by_id ( art . id )
db_art . title = art . title if art . title is not None else db_art . title
db_art . link = art . link if art . link is not None else db_art . link
db_art . md5_hash = art . hash if art . hash is not None else db_art . md5_hash
db_art . path = art . path if art . path is not None else db_art . path
self . __get_db ( ) . commit ( )
def delete_art_by_id ( self , art_id : int ) :
raise NotImplementedError
db_art : DBArt = self . get_art_by_id ( art_id )
self . __get_db ( ) . delete ( db_art )
self . __get_db ( ) . commit ( )
@DeprecationWarning
def delete_art_by_hash ( self , md5_hash : str ) :
raise NotImplementedError
@ -156,17 +212,40 @@ class Database:
# Presence
def get_presence_list ( self ) :
return self . __get_db ( ) . query ( DBPresence ) . all ( ) # TODO fix StackOverflow
def get_presence ( self , name : str , domain : str ) - > DBPresence :
result = self . __get_db ( ) . query ( DBPresence ) \
. filter ( func . lower ( DBPresence . name ) == name . lower ( ) and
func . lower ( DBPresence . domain ) == domain . lower ( ) ) . first ( )
return result
def create_presence ( self , name : str , domain : str , artist_id : int , link : str ) :
if not ( len ( name ) > 0 and len ( domain ) > 0 ) :
print ( f " Name: \" { name } \" Domain: \" { domain } \" " )
raise ValueError ( " New Presence must have some name and domain! " )
db_presence = DBPresence ( name = name , domain = domain , artist_id = artist_id , link = link )
db = self . __get_db ( )
db . add ( db_presence )
db . commit ( )
return db_presence . name , db_presence . domain
def update_presence ( self , name : str , domain : str , artist_id : int , link : str ) :
raise NotImplementedError
db_presence = self . get_presence ( name = name , domain = domain )
db_presence . link = link
db_presence . artist_id = artist_id
self . __get_db ( ) . commit ( )
def delete_presence ( self , name : str , domain : str ) :
raise NotImplementedError
db_presence = self . get_presence ( name = name , domain = domain )
self . __get_db ( ) . delete ( db_presence )
self . __get_db ( ) . commit ( )
# Artist -> Presence
@ -176,15 +255,43 @@ class Database:
# Artist
def get_artist ( self , artist_id : int ) :
def get_artist_list ( self ) :
return self . __get_db ( ) . query ( DBArtist ) . all ( ) # TODO fix StackOverflow
def get_artist ( self , artist_id : int ) - > DBArtist :
result = self . __get_db ( ) . query ( DBArtist ) . where ( DBArtist . id == artist_id ) . first ( )
return result
def update_artist ( self , artist_id : int , name : str ) :
raise NotImplementedError
def create_artist ( self , name : str , topics : List [ int ] ) :
db_artist = DBArtist ( description = name , topics = topics )
db = self . __get_db ( )
db . add ( db_artist )
db . commit ( )
return db_artist
def search_artist ( self , name : str ) - > Artist :
result = self . __get_db ( ) . query ( DBArtist ) . where ( DBArtist . description == name ) . all ( )
return result
def update_artist ( self , artist_id : int , name : str = None , topics : List [ int ] = None ) :
db_artist = self . get_artist ( artist_id )
if name is not None :
db_artist . description = name
if topics is not None :
db_artist . topics = topics
self . __get_db ( ) . commit ( )
def delete_artist ( self , artist_id : int ) :
raise NotImplementedError
db_artist = self . get_artist ( artist_id )
self . __get_db ( ) . delete ( db_artist )
self . __get_db ( ) . commit ( ) # TODO FIX sqlalchemy.exc.IntegrityError: (psycopg2.errors.ForeignKeyViolation)
# TODO update or delete on table "artist" violates foreign key constraint "presence_artist_id_fkey" on table "presence"
# TODO DETAIL: Key (id)=(83) is still referenced from table "presence".
# Topic