Ja, das war ursprünglich auch der Plan. Da ich aber keine Ahnung hab, wie ich mir das mit den Flags vorstellen soll, habe ich das so neu interpretiert. Bin da ehrlich gesagt verwirrt...In Deinem vorletzen Beitrag klang das noch so als wenn Du den Job-Datensatz tatsächlich löschen wolltest und bei den Zeiten zu dem Job ein Flag setzen wolltest
Projekt Zeitmanagement
-
- User
- Beiträge: 52
- Registriert: Dienstag 2. Februar 2016, 10:56
Demnach müsste sowohl beim Anlegen eines Jobs als auch eines Zeiteintrags jeweils die ID zurückgegeben, an die GUI weitergereicht und dort vom System mit einer "Rücküberetzung" in Klartext verbunden werden, damit der Nutzer weiß, was er tut und das System trotzdem (unabhängig vom Nutzer) mit den IDs arbeiten kann.
@T.T. Kreischwurst: Wenn man nur die ID zurück gibt, dann ist ja in aller Regel zwangsläufig der nächste Schritt, dass über die ID der Jobtitel abgefragt wird, damit man den anzeigen kann. Und dazu dann den Status falls man inaktive Jobs gar nicht oder anders darstellen möchte. Also könnte man beim Einfügen gleich alles zurückgeben. Beziehungsweise *zum* einfügen alles ausser der ID, die dann von der Einfügemethode gesetzt wird. Ich sehe das aber auch alles aus einer Sicht das man das gar nicht selber schreiben möchte. Ich würde ein `Job`-Objekt erstellen und das dann speichern (lassen).
Ich denke da in solchem Code (unvollständig und ungetestet):
Ohne ORM würde ich mich trotzdem grob an dieser Struktur orientieren.
Ich denke da in solchem Code (unvollständig und ungetestet):
Code: Alles auswählen
# ...
class Job(Base):
__tablename__ = 'job'
id = Column(INT, primary_key=True)
title = Column(TEXT, nullable=False, unique=True)
is_active = Column(BOOLEAN, nullable=False, default=True)
class TimeSlot(Base):
__tablename__ = 'time_slot'
id = Column(INT, primary_key=True)
job_id = Column(ForeignKey(Job.id), nullable=False)
start_time = Column(DATETIME, nullable=False)
job = relationship(Job)
@property
def is_active(self):
return self.job.is_active
# ...
job = Job(title='Nase bohren')
session.save(job)
slot = TimeSlot(job=job, start_time=datetime.datetime(2016, 12, 14, 12, 15))
session.save(job)
session.commit()
all_jobs = session.Query(Job).all()
active_jobs = session.Query(Job).filter_by(is_active=True).all()
today = datetime.date.today()
end_of_today = today + relativedelta(days=1, seconds=-1)
todays_slots = session.Query(TimeSlot).filter(
TimeSlot.start_time.between(today, end_of_today)
).all()
-
- User
- Beiträge: 52
- Registriert: Dienstag 2. Februar 2016, 10:56
Servus!
Nach einer dreimonatigen Pause, bedingt durch einen spontanen und sehr erfreulichen Jobwechsel (habe es als Quereinsteiger in die IT-Brachne, genauer gesagt in ein Ausbildungsprogramm im Bereich Mainframe geschafft) habe ich nun mein altes Pythonprojekt wieder ausgegraben.
Letzter Stand war ein Neuentwurf des Datenbankdesign bzw. der entsprechenden Klasse, und hier sind beim Schreiben Fragen aufgetaucht, schon bevor alle Methoden formuliert waren. Hier der aktuelle Stand:
- Das SQL lehnt sich etwas an DB2 an, da ich das in der Ausbildung gelernt habe. Funktioniert das so oder sind da Konstrukte drin, die SQLite nicht kennt? Habe das unterwegs ohne SQLite Referenz geschrieben... bin daher nicht sicher, ob so Sachen wie das nachträgliche Einsetzen der Foreign und Primary Keys so klappt. Uns wurde jedenfalls nahegelegt, erstmal die Tabellenstruktur zu erstellen und dann erst Schlüssel und Constraints anzulegen.
- Beim Lesen der DB gibt es mit diesem Design (das Sirius' Vorschlag folgt) ein Problem: Im Programm wird der Nutzer hauptsächlich über Datumsangaben zugreifen. Sprich: er wird sagen: "gib mir alle Jobs, die ich am 20.03.2017 erledigt habe". Das geht hier - aber nur, wenn man einen Vergleich auf ein nicht-Schlüsselfeld macht. Das soll man nicht tun, aber Datums-/Zeitangaben finde ich als Primärschlüssel schwierig. Man müsste dann z.B. das Feld "von" nehmen, im WHERE Feld des SQL Statements das Datum aus dem Schlüssel ziehen und mit dem übergebenen Datum vergleichen. Datetime Objekte kann ich nicht vom Programm an die Datenbank übergeben, weil sie zu kleinteilig sind: den Nutzer interessieren nur ganze Tage, nicht ein einzelner Zeitabschnitt davon.
Irgendwie ist das aber ungeil...
Nach einer dreimonatigen Pause, bedingt durch einen spontanen und sehr erfreulichen Jobwechsel (habe es als Quereinsteiger in die IT-Brachne, genauer gesagt in ein Ausbildungsprogramm im Bereich Mainframe geschafft) habe ich nun mein altes Pythonprojekt wieder ausgegraben.
Letzter Stand war ein Neuentwurf des Datenbankdesign bzw. der entsprechenden Klasse, und hier sind beim Schreiben Fragen aufgetaucht, schon bevor alle Methoden formuliert waren. Hier der aktuelle Stand:
Code: Alles auswählen
import sqlite3
class Database():
def __init__(self, filepath)
self.connection = sqlite3.connect(filepath)
self.create_tables()
def create_tables(self):
cursor = self.connection.cursor()
cursor.execute('''
create table if not exists
job
(
id integer not null
,bezeichnung text
)
;
create table if not exists
time_slot
(
ref_job integer not null
,von timestamp
,bis timestamp
)
;''')
cursor.execute('''
alter table job
add primary key (id)
;
alter table time_slot
add foreign key zeit_job (ref_job)
references (job)
on delete set null
alter table zeit
add primary key (ref_job)
;
''')
self.connection.commit()
def insert_job(self, title):
cursor = self.connection.cursor()
cursor.execute('''
insert into
job
values
(?)
''', (title, )
)
self.connection.commit()
def insert_time(self, begin, end, ref_job)
cursor = self.connection.cursor()
cursor.execute('''
insert into
time_slot
values
(?,?,?)
''', (ref_job, begin, end)
)
self.connection.commit()
def read_data(self, XXXXXX)
- Beim Lesen der DB gibt es mit diesem Design (das Sirius' Vorschlag folgt) ein Problem: Im Programm wird der Nutzer hauptsächlich über Datumsangaben zugreifen. Sprich: er wird sagen: "gib mir alle Jobs, die ich am 20.03.2017 erledigt habe". Das geht hier - aber nur, wenn man einen Vergleich auf ein nicht-Schlüsselfeld macht. Das soll man nicht tun, aber Datums-/Zeitangaben finde ich als Primärschlüssel schwierig. Man müsste dann z.B. das Feld "von" nehmen, im WHERE Feld des SQL Statements das Datum aus dem Schlüssel ziehen und mit dem übergebenen Datum vergleichen. Datetime Objekte kann ich nicht vom Programm an die Datenbank übergeben, weil sie zu kleinteilig sind: den Nutzer interessieren nur ganze Tage, nicht ein einzelner Zeitabschnitt davon.
Irgendwie ist das aber ungeil...
@T.T. Kreischwurst: Was ist denn die Begründung dafür Primär- und Fremdschlüssel erst in einem zweiten Gang bekannt zu machen? Da hat man dann ja nicht *eine* Beschreibung davon wie die Tabellen aussehen, sondern muss die sich aus den beiden Anweisungen erst zusammen suchen. Bei zwei Tabellen geht das vielleicht noch, aber bei einer DB mit vielen Tabellen fänd ich das sehr unübersichtlich. Wo ist die Deklaration der Tabelle `zeit`?
Bei SQLite kann das tatsächlich ein Problem werden, weil man bei vorhandenen Tabellen nicht alle möglichen Änderungen machen kann. (Ich weiss jetzt leider aus dem Kopf nicht welche Änderungen möglich sind und welche nicht.)
Warum soll man keine Abfragen auf Nicht-Schlüssel-Felder machen? Wenn das doch aber die Kriterien sind die den Benutzer interessieren? Komische Regel(‽)
Das Datum/die Zeitstempel würde ich nicht als Schlüssel verwenden, das macht speziell beim Datum auch gar keinen Sinn, denn es gibt ja mehr als einen Eintrag an einem Datum, also kann das Datum gar kein Primärschlüssel sein. Bei den zu erwartenden Datenmengen würde ich mir erst einmal gar keinen Kopf um so etwas machen und einfach nach dem `DATE()`-Teil vom Zeitstempel filtern. Sollte das dann irgendwann tatsächlich zu einem *messbaren* Problem werden, kann man einen Index auf die `von`-Spalte setzen und mit BETWEEN den ganzen Tag filtern. Aber das würde ich wirklich erst machen wenn es tatsächlich problematisch wird, was wohl ein paar Jahre an Datensammeln dauern kann.
Bei SQLite kann das tatsächlich ein Problem werden, weil man bei vorhandenen Tabellen nicht alle möglichen Änderungen machen kann. (Ich weiss jetzt leider aus dem Kopf nicht welche Änderungen möglich sind und welche nicht.)
Warum soll man keine Abfragen auf Nicht-Schlüssel-Felder machen? Wenn das doch aber die Kriterien sind die den Benutzer interessieren? Komische Regel(‽)
Das Datum/die Zeitstempel würde ich nicht als Schlüssel verwenden, das macht speziell beim Datum auch gar keinen Sinn, denn es gibt ja mehr als einen Eintrag an einem Datum, also kann das Datum gar kein Primärschlüssel sein. Bei den zu erwartenden Datenmengen würde ich mir erst einmal gar keinen Kopf um so etwas machen und einfach nach dem `DATE()`-Teil vom Zeitstempel filtern. Sollte das dann irgendwann tatsächlich zu einem *messbaren* Problem werden, kann man einen Index auf die `von`-Spalte setzen und mit BETWEEN den ganzen Tag filtern. Aber das würde ich wirklich erst machen wenn es tatsächlich problematisch wird, was wohl ein paar Jahre an Datensammeln dauern kann.
-
- User
- Beiträge: 52
- Registriert: Dienstag 2. Februar 2016, 10:56
Weiß ich nicht genau. Ich nehme an, es war ein Tipp für uns Anfänger: erstmal die Tabellen sauber erstellen, dann Contraints anlegen, dann nächste Tabelle. Bei sehr großen creates nehme ich an, dass auch der Ausbilder auf die Kurzschreibweise zurückgegriffen hätte. Er hat sie nämlich explizit erwähnt, allerdings stehen bei den besprochenen Änderungen an DB2 Systemen allenfalls einzelne Tabellen-Creates an, höchst selten mal mehr als zwei gleichzeitig. Die müssen aber passen.Was ist denn die Begründung dafür Primär- und Fremdschlüssel erst in einem zweiten Gang bekannt zu machen?
Ups, das ist der alte Name. Habe sie in time_slots umbenannt, muss das hier noch ändern...Wo ist die Deklaration der Tabelle `zeit`?
Dann werde ich die Schreibweise wieder auf die kürzere aus meiner vorherigen Version umstellen.
Das ist keine Regel aus der Ausbildung, sondern eine Überlegung von mir. Es gab ja den Hinweis, am besten über IDs abzufragen. Ich sehe das wie du, obwohl das Datum prinzipiell schon keyfähig wäre. Es sind ja Datetime Objekte, und da nur ein Job zu einer Zeit gespeichert werden kann, sollte es gehen. Trotzdem lieber über Fremdschlüssel zu Job, das finde ich sauberer.Warum soll man keine Abfragen auf Nicht-Schlüssel-Felder machen? Wenn das doch aber die Kriterien sind die den Benutzer interessieren? Komische Regel(‽)
Ansonsten scheint es ja soweit zu passen - dann würde ich nämlich weiterschreiben.
-
- User
- Beiträge: 52
- Registriert: Dienstag 2. Februar 2016, 10:56
So, neue Version der Datenbank mit ein paar Fragen. Ich habe mit einer separaten Testklasse die Funktionen jeweils getestet und sie tun soweit, was sie sollen. Ich kann natürlich nicht garantieren, dass ich an alle möglichen Testfälle gedacht habe...
Aber zu den Fragen:
1) Ist es besser oder schlechter, erst den Primärschlüssel für den Job über read_job in einer eigenen Funktion abzufragen und dann mithilfe des jew. Rückgabewertes Aktionen wie ein delete (in remove_job) durchzuführen? Oder sollte ich mir die Funktion sparen und direkt in den Funktionen einen Stringvergleich machen? Also: in remove_job statt where id =? schreiben: where bezeichnung= ? und statt einer ID den vom Nutzer eingegebenen String prüfen? So wie ich es jetzt habe, ist es halt einheitliher, weil es genau eine Schnittstelle gibt, an der geprüft wird, ob es den vom Nutzer ausgewählten Job gibt. Allerdings ist es etwas uständlich und ich bin vom Nutzen noch nicht übrezeugt.
2) on delete Befehle funktionieren nicht beim Test. Das delete wird zwar durchgeführt, aber die Zeile in der Tabelle time_slot bleibt bei cascade einfach stehen und wird auch bei set null nicht null gesetzt.
Wo ist mein Denkfehler?
Aber zu den Fragen:
1) Ist es besser oder schlechter, erst den Primärschlüssel für den Job über read_job in einer eigenen Funktion abzufragen und dann mithilfe des jew. Rückgabewertes Aktionen wie ein delete (in remove_job) durchzuführen? Oder sollte ich mir die Funktion sparen und direkt in den Funktionen einen Stringvergleich machen? Also: in remove_job statt where id =? schreiben: where bezeichnung= ? und statt einer ID den vom Nutzer eingegebenen String prüfen? So wie ich es jetzt habe, ist es halt einheitliher, weil es genau eine Schnittstelle gibt, an der geprüft wird, ob es den vom Nutzer ausgewählten Job gibt. Allerdings ist es etwas uständlich und ich bin vom Nutzen noch nicht übrezeugt.
2) on delete Befehle funktionieren nicht beim Test. Das delete wird zwar durchgeführt, aber die Zeile in der Tabelle time_slot bleibt bei cascade einfach stehen und wird auch bei set null nicht null gesetzt.
Wo ist mein Denkfehler?
Code: Alles auswählen
import sqlite3
class Database():
def __init__(self, filepath):
self.connection = sqlite3.connect(filepath)
self.create_tables()
def create_tables(self):
cursor = self.connection.cursor()
cursor.execute('''
create table if not exists
job
(
id integer primary key not null
,bezeichnung text
)
;''')
cursor.execute('''
create table if not exists
time_slot
(
ref_job integer primary key not null
,von timestamp
,bis timestamp
,foreign key(ref_job)
references job(id)
on delete cascade
)
;''')
self.connection.commit()
def insert_job(self, title):
cursor = self.connection.cursor()
cursor.execute('''
insert into
job(bezeichnung)
values
(?)
''', (title, )
)
self.connection.commit()
def update_job(self, term, job_id):
cursor = self.connection.cursor()
cursor.execute('''
update
job
set
bezeichnung = ?
where
id = ?
''', (term, job_id))
self.connection.commit()
def insert_time(self, begin, end, ref_job):
cursor = self.connection.cursor()
cursor.execute('''
insert into
time_slot
values
(?,?,?)
''', (ref_job, begin, end)
)
self.connection.commit()
def read_job(self, term):
cursor = self.connection.cursor()
job_id = cursor.execute('''
select
id
from
job
where
bezeichnung = ?
''', (term,))
job_id = job_id.fetchone()[0]
cursor.close()
return job_id
def read_data(self, begin, end):
cursor = self.connection.cursor()
jobs_done = cursor.execute('''
select
job.bezeichnung
from
job
,time_slot
where
time_slot.ref_job = job.id
and
time_slot.von = ?
and
time_slot.bis = ?
''', (begin, end))
jobs_done = jobs_done.fetchall()
cursor.close()
return jobs_done
def remove_job(self, job_id):
cursor = self.connection.cursor()
cursor.execute('''
delete from
job
where
id = ?
''', (job_id,))
self.connection.commit()
@T.T. Kreischwurst: ich weiß nicht, wie Du Dir Deine Oberfläche gestaltest, aber normalerweise fragt man ID und Bezeichnung ab, nimmt die Bezeichnung zur Darstellung aber die ID zum Arbeiten. Deshalb sollte insert_job auch die neu erzeugte ID zurückgeben. In insert_time würde ich die Spaltennamen noch explizit angeben. read_job hieße besser find_job_id und sollte noch prüfen, ob wirklich nur ein Job mit dieser Bezeichnung existiert. Soweit ich sehe ist die Spalte bezeichnung nicht unique. read_data ist ein zu generischer Name. Warum heißt das Ergebnis jobs_done? Auch hier solltest Du mit job_ids arbeiten, so dass der Join gar nicht nötig ist. Willst Du nicht nach Jobs suchen, für die gilt von<=datum<=ende? Normalerweise weiß man ja nicht, wann ein Job angefangen hat und wann er beendet wurde?
Ich designe Datenbanken normalerweise so, dass es kein remove gibt.
Ich designe Datenbanken normalerweise so, dass es kein remove gibt.
-
- User
- Beiträge: 52
- Registriert: Dienstag 2. Februar 2016, 10:56
Erledigt. Stimmt, dann kann man die ID gleich benutzen, z.B. wenn man den Namen des neuen Jobs in der GUI anzeigen will.Deshalb sollte insert_job auch die neu erzeugte ID zurückgeben.
Danke für den Hinweis:
Einen Job soll es nur einmal geben, daher bietet sich der unique constraint an. Ist eingebaut.read_job hieße besser find_job_id und sollte noch prüfen, ob wirklich nur ein Job mit dieser Bezeichnung existiert. Soweit ich sehe ist die Spalte bezeichnung nicht unique
Korrekt - so, wie sie jetzt da steht, soll die Funktion zu viel leisten, was sie gar nicht kann. Ich brauche in Bezug auf die Tabelle Job drei Funktionen/selects, die irgendwie Daten auslesen:Warum heißt das Ergebnis jobs_done? Auch hier solltest Du mit job_ids arbeiten, so dass der Join gar nicht nötig ist. Willst Du nicht nach Jobs suchen, für die gilt von<=datum<=ende? Normalerweise weiß man ja nicht, wann ein Job angefangen hat und wann er beendet wurde?
1) get_job_id: holt einen Datensatz für genau eine vom Nutzer eingegebene Bezeichnung, sofern vorhanden. Liefert sonst none zurück und kann daher als Prüfung dienen, ob es einen Job mit einer bestimmten Bezeichnung gibt oder nicht.
2) Eine Funktion, die alle Job-Bezeichnungen ausliest und zurückgibt: das ist für eine Auswahlliste in der GUI nötig, in der alle Jobs stehen.
3) EIne Funktion, die Jobs sucht, für die gilt von<=datum<=ende, wie du schreibst. Das ist das, woran ich bei read_data gedacht hatte.
-
- User
- Beiträge: 52
- Registriert: Dienstag 2. Februar 2016, 10:56
So. Ich habe die angemerkten Änderungen eingebaut und den Code in einem neuen Branch in Github eingestellt (Link im ersten Post).
Das ganze würde ich als eigenes Modul so stehen lassen; Frage ist, ob sich eine weitere Unterteilung in Klassen anbietet. Derzeit würde ich das nicht so sehen; einzig die Erzeugung der Tabellen könnte ich mir noch als eigene Klasse vorstellen, wobei man die beiden executes, welche die Tabellen erzeugen, dann zu eigenen Methoden machen würde. Wie gesagt, ich finde es aktuell nicht sinnvoll, außer die Database-Klasse ist zu lang.
Ansonsten ist mein Plan, jetzt die ganze Config-Dateien Logik und die Passwort Logik in eigenen Klassen zu schreiben und sauber von der GUI zu trennen. Die weiteren Schritte sind sehr eng mit der GUI verzahnt oder sind die GUI im engeren Sinn; das kommt also später.
Das ganze würde ich als eigenes Modul so stehen lassen; Frage ist, ob sich eine weitere Unterteilung in Klassen anbietet. Derzeit würde ich das nicht so sehen; einzig die Erzeugung der Tabellen könnte ich mir noch als eigene Klasse vorstellen, wobei man die beiden executes, welche die Tabellen erzeugen, dann zu eigenen Methoden machen würde. Wie gesagt, ich finde es aktuell nicht sinnvoll, außer die Database-Klasse ist zu lang.
Ansonsten ist mein Plan, jetzt die ganze Config-Dateien Logik und die Passwort Logik in eigenen Klassen zu schreiben und sauber von der GUI zu trennen. Die weiteren Schritte sind sehr eng mit der GUI verzahnt oder sind die GUI im engeren Sinn; das kommt also später.
Sieht schon ganz gut aus.
Wie immer milde Meckereien, bzw. Verbesserungsvorschlaege:
- deine Kommasetzung bei den Create-Statements ist ungewoehnlich und erschwert mir das Verstaendnis.
- ich bin ein Freund datengetriebener Programmierung. Statt in der Methode ewig lange SQL-statements zu haben, wuerde ich eher eine Liste auf Modulebene anlegen, "CREATE_STATEMENTS", und ueber die rueberlaufen, und die dann abfeuern.
- im Sinne von DRY faellt mir das oft am Ende einer Methode stehende commit auf. Das waere fuer mich ein klarer Kandidat fuer einen Dekorator-basierten Ansatz, bei dem ich damit klar kenntlich mache ob in einer Methode Daten veraendert werden, ohne das ich die Details kennen muss. Und umgekehrt: schreibe ich eine Methode, die Daten veraendern soll, ist es einfach, sie auszuzeichnen. Ungetestet so:
Wie immer milde Meckereien, bzw. Verbesserungsvorschlaege:
- deine Kommasetzung bei den Create-Statements ist ungewoehnlich und erschwert mir das Verstaendnis.
- ich bin ein Freund datengetriebener Programmierung. Statt in der Methode ewig lange SQL-statements zu haben, wuerde ich eher eine Liste auf Modulebene anlegen, "CREATE_STATEMENTS", und ueber die rueberlaufen, und die dann abfeuern.
- im Sinne von DRY faellt mir das oft am Ende einer Methode stehende commit auf. Das waere fuer mich ein klarer Kandidat fuer einen Dekorator-basierten Ansatz, bei dem ich damit klar kenntlich mache ob in einer Methode Daten veraendert werden, ohne das ich die Details kennen muss. Und umgekehrt: schreibe ich eine Methode, die Daten veraendern soll, ist es einfach, sie auszuzeichnen. Ungetestet so:
Code: Alles auswählen
from functools import wraps
def commits(func)
@wraps(func)
def _d(self, *a, **k):
res = func(self, *a, **k)
self.connection.commit()
return res
return _d
...
class Foo(...):
@commits
def neuer_job(...)
-
- User
- Beiträge: 52
- Registriert: Dienstag 2. Februar 2016, 10:56
Ich steh auf milde Meckereien. Deswegen bin ich hier Danke fürs Feedback!__deets__ hat geschrieben: Wie immer milde Meckereien, bzw. Verbesserungsvorschlaege:
Die Kommasetzung ist zugegebenermaßen ungewöhnlich, hilft mir persönlich aber bei der Wartung des Codes. Setze ich die Kommata immer an die erste Position, stehen sie immer untereinander und immer am gleichen Fleck. So sehe ich sofort, wenn ein Komma fehlt oder überschüssig ist, wenn sie am SQL was geändert hat. Ich kann es künftig aber gerne auch klassisch schreiben, sofern ich dran denke
Du meinst also ein Modul bestehend lediglich aus einer Liste mit allen nötigen SQL Statements, die dann in den Python Funktionen nur mit Liste[Index] angesprochen werden? Klingt nice, aber wie ist das dann bei Parametern, die in das SQL reinmüssen? Deren Verwendung wird durch die Auslagerung in ein Modul ja eher erschwert, oder?Statt in der Methode ewig lange SQL-statements zu haben, wuerde ich eher eine Liste auf Modulebene anlegen, "CREATE_STATEMENTS", und ueber die rueberlaufen, und die dann abfeuern
Deinen letzten Punkt blick ich ehrlich gesagt nicht :K
@T.T. Kreischwurst: Eingerückt wird mit 4 Leerzeichen pro Ebene, nicht mit Tabs. Jedesmal, wenn eine Instanz der Datenbankklasse erstellt wird, alles Tabellen zu erzeugen, halte ich für einen Fehler. Das sollte man bewußt, einmal beim erstellen einer neuen Datenbank machen. Zeile 51: wußte gar nicht, dass man nach einem INSERT ein fetch machen kann. Gerade ausprobiert, geht auch gar nicht. Das nimmt solangsam Ausmaße an, dass es ratsam wäre auf SQLAlchemy umzusteigen.
-
- User
- Beiträge: 52
- Registriert: Dienstag 2. Februar 2016, 10:56
Ah ja da war etwas, das Blackjack mal erwähnt hatte.
Ich kann jetzt alles nochmal auf SQLAlchemy umschreiben, aber ist das wirklich sinnvoll? Erstens entstehen damit neue Fehlerquellen (weil unbekannt), zweitens dachte ich, ich lerne das ganze erstmal auf die rudimentäre, harte Tour, bevor ich Hilfsmittel benutze, mit denen ich bestimmte Fehler nicht mache. Ich bekomme daher eher immer stärker das Gefühl, dass die ganze Sache keinen Sinn hat. Ich komme hier zunehmend vom 100. ins 1000. und stelle fest, dass ich weder einen Plan von meinem Programm habe, noch einen erstellen kann weil ich keine Ahnung von objektorientiertem Entwurf habe und in letzter Konsequenz nicht begreife, was ORM eigtl. ist und was das für einen Vorteil bringen soll.
Vor diesem Hintergrund dann Prinzipien und Systeme anwenden und ausprobieren zu wollen, die man eigtl. nicht begreift, erscheint mir immer weniger sinnvoll. Wahrscheinlich muss ich erstmal viel mehr lernen, wobei ich nicht weiß, wo ich anfangen soll :K
Ich kann jetzt alles nochmal auf SQLAlchemy umschreiben, aber ist das wirklich sinnvoll? Erstens entstehen damit neue Fehlerquellen (weil unbekannt), zweitens dachte ich, ich lerne das ganze erstmal auf die rudimentäre, harte Tour, bevor ich Hilfsmittel benutze, mit denen ich bestimmte Fehler nicht mache. Ich bekomme daher eher immer stärker das Gefühl, dass die ganze Sache keinen Sinn hat. Ich komme hier zunehmend vom 100. ins 1000. und stelle fest, dass ich weder einen Plan von meinem Programm habe, noch einen erstellen kann weil ich keine Ahnung von objektorientiertem Entwurf habe und in letzter Konsequenz nicht begreife, was ORM eigtl. ist und was das für einen Vorteil bringen soll.
Vor diesem Hintergrund dann Prinzipien und Systeme anwenden und ausprobieren zu wollen, die man eigtl. nicht begreift, erscheint mir immer weniger sinnvoll. Wahrscheinlich muss ich erstmal viel mehr lernen, wobei ich nicht weiß, wo ich anfangen soll :K
Zuletzt geändert von T.T. Kreischwurst am Dienstag 18. April 2017, 17:09, insgesamt 1-mal geändert.
Bezueglich der statements: die sollen nicht (notwendigerweise) ausgelagert, aber getrennt vom Code werden;
Damit wird in meinen Augen der Code klarer - ich kann den losgeloest von deinen aufwaendig (was gut ist) formatierten SQL statements verstehen.
Das lohnt natuerlich nur bei statements die keine oder immer die gleichen Parameter haben, und wenn man eben wirklich mehrere hat - ich wuerde das nicht aus Prinzip empfehlen, sondern wirklich nur fuer die Anlage der DB.
AFAIK kann man sogar multi-statement absetzen, und statt einer Liste mehrere durch Semikola getrennte Statements in einen String schreiben.
Dann hat man einen Block, den man sogar per C&P von Hand in sqlite eingeben kann, wenn man das mal will.
Code: Alles auswählen
CREATE_TABLE_STATEMENTS = [
"""create table foo (
....
)""",
"""create table foo (
....
)""",
]
...
for statement in CREATE_TABLE_STATEMENTS:
cursor.execute(statement)
Damit wird in meinen Augen der Code klarer - ich kann den losgeloest von deinen aufwaendig (was gut ist) formatierten SQL statements verstehen.
Das lohnt natuerlich nur bei statements die keine oder immer die gleichen Parameter haben, und wenn man eben wirklich mehrere hat - ich wuerde das nicht aus Prinzip empfehlen, sondern wirklich nur fuer die Anlage der DB.
AFAIK kann man sogar multi-statement absetzen, und statt einer Liste mehrere durch Semikola getrennte Statements in einen String schreiben.
Dann hat man einen Block, den man sogar per C&P von Hand in sqlite eingeben kann, wenn man das mal will.