Datumsbereiche aus 2 Datepicker in eine Tabelle schreiben

Installation und Anwendung von Datenbankschnittstellen wie SQLite, PostgreSQL, MySQL, der DB-API 2.0 und sonstigen Datenbanksystemen.
jake012
User
Beiträge: 16
Registriert: Donnerstag 8. März 2018, 12:20

Donnerstag 12. Juli 2018, 18:34

Hi,

ich habe 2 Datepicker. Einer gibt das Start und der andere das Enddatum an. Diesen Datumsbereich von bis möchte ich in eine Tabelle schreiben. Jedes Datum in eine neue Zeile.

Beispiel:
Von: 01.07.2018
Bis: 10.07.2018

Zunächst hatte ich das Problem, dass ich das Datumsformat nicht in ein für sqlite ansprechendes Format hatte. Das habe ich gelöst und sieht nun auch im Format aus wie '2018-07-01', was mir auch bei print(date_tuple) bestätigt wird.

Nun schreibt der Code mir aber nur einen Teil in die DB und dann auch jede Zahl in eine neue Reihe, was dann so aussieht:

Code: Alles auswählen

2
0
1
8
-
0
7
-
0
1
Suche schon Tage verzweifelt nach einer Lösung, aber werde nicht fündig. Bin für jeden Ansatz dankbar :)

Mein Codesnip sieht so aus:

Code: Alles auswählen

    def OnDateChanged(self,event):
        from_date_a = self.m_date_from.GetValue()
        till_date_a = self.m_date_till.GetValue()
        from_date_b = from_date_a.Format("%Y-%m-%d")
        till_date_b = till_date_a.Format("%Y-%m-%d")
        from_date_c = datetime.datetime.strptime(from_date_b,"%Y-%m-%d")
        till_date_c = datetime.datetime.strptime(till_date_b,"%Y-%m-%d")


        day_count = (till_date_c - from_date_c).days +1
        for single_date in (from_date_c + datetime.timedelta(n) for n in range(day_count)):
            #sd1 = datetime.datetime(single_date.isoformat())
            date_tuple = str(single_date.date())
            print(date_tuple)

        sql_cmd = """
        create table if not exists daterange (range TEXT);
        """
        cur.execute(sql_cmd)

        sql_cmd = """
        INSERT INTO daterange
        VALUES(?);
        """
        cur.executemany(sql_cmd, (date_tuple))
        con.commit()
Sirius3
User
Beiträge: 8791
Registriert: Sonntag 21. Oktober 2012, 17:20

Donnerstag 12. Juli 2018, 19:22

Was ist denn m_date_from für ein Objekt, was liefert GetValue dann für ein Objekt?
Was soll die for-Schleife, wenn doch nur das Enddatum in die Datenbank geschrieben wird?
date_tuple ist kein Tuple, generell sollten Datentypen nicht in Variablennamen auftauchen.
Tabellen erzeugt man nicht im Vorhinein und nicht erst, wenn man Daten einfügen will.
execute_many erwartet eine Liste von Tupeln und nicht einen String als zweites Argument.
SQLite kann auch direkt DateTime-Objekte verarbeiten.
jake012
User
Beiträge: 16
Registriert: Donnerstag 8. März 2018, 12:20

Donnerstag 12. Juli 2018, 22:24

Hi,

was führt dich zu der Annahme, dass ich nur das Enddatum schreiben will? - Nein, ich will jedes Datum schreiben.

m_date_from ist folgendes Element:

Code: Alles auswählen

self.m_date_from = wx.adv.DatePickerCtrl(self, wx.ID_ANY, wx.DefaultDateTime, wx.DefaultPosition, wx.DefaultSize, wx.adv.DP_DEFAULT | wx.adv.DP_DROPDOWN)
Wenn ich from_date_a ausgebe, dann erhalte ich das:

Code: Alles auswählen

Sun Jul  1 00:00:00 2018
Es soll aber jedes Datum in die Tabelle geschrieben werden und ich habe nichts gefunden, wie ich dieses Datum-Format inkl. delta in die Datenbank schreiben könnte. Deshalb habe ich es in ein für mich bekanntes Format umgewandelt, was dann from_date_a entspricht '2018-07-01'.

Wenn ich print(date_tuple) ausführe, dann bekomme ich auch meine Liste mit jedem Datum:

Code: Alles auswählen

2018-07-01
2018-07-02
2018-07-03
2018-07-04
2018-07-05
2018-07-06
2018-07-07
2018-07-08
2018-07-09
2018-07-10
Wenn du mir jetzt natürlich sagst, ich kann auch mit dem ursprünglichen Datumsformat das delta herleiten, sodass jedes einzelne Datum in einer neuen Zeile geschrieben wird, ist mir das auch recht.

Hoffe, das hilft weiter und es gibt noch Ideen :)
Benutzeravatar
__blackjack__
User
Beiträge: 1566
Registriert: Samstag 2. Juni 2018, 10:21

Donnerstag 12. Juli 2018, 23:19

@jake012: Wir nehmen nicht an das Du nur das Enddatum schreiben willst, aber wir sehen am Code das Du offensichtlich nur das Enddatum *schreibst*. In der Schleife wird jedes Datum an den irreführenden Namen `date_tuple` gebunden. Irreführend weil es sich nicht um ein Tupel handelt, sondern um eine Zeichenkette. Und nach der Schleife ist an diesen Namen das letzte Datum aus der Schleife gebunden, also das Enddatum, und nur das wird dann Buchstabenweise auf die Datensätze verteilt.

Verteilt wird es, weil `executemany()` als zweites Argument ein iterierbares Objekt erwartet, welches die einzelnen Datensätze enthält. Und `date_tuple` ist eine Zeichenkette. Die einzelnen Elemente einer Zeichenkette sind nun mal die Buchstaben. Darum bekommt jeder Buchstabe einen eigenen Datensatz.

Ich würde ja von der direkten Verwendung der DB-API V2 abraten und SQLAlchemy verwenden. Dann kann man direkt mit den Datentypen aus dem `datetime`-Modul arbeiten und ist auch unabhängiger von der Datenbank die man verwendet, was beispielsweise die Platzhalter angeht.

Ich frage mich übrigens gerade von `cur` und `con` definiert sind, denn die Methode dürfte da ja eigentlich so gar nicht drauf zugreifen können, es sei denn Du hast da Variablen global definiert. Was man nicht tun sollte.

Code: Alles auswählen

    **** COMMODORE 64 BASIC V2 ****
 64K RAM SYSTEM  38911 BASIC BYTES FREE
   CYBERPUNX RETRO REPLAY 64KB - 3.8P
READY.
█
jake012
User
Beiträge: 16
Registriert: Donnerstag 8. März 2018, 12:20

Freitag 13. Juli 2018, 11:05

Okay, ich habe jetzt verstanden, dass meine Schleife auch falsch war und habe nun den Insert in der Schleife gelegt. Jetzt habe ich den win, dass alle Daten geschrieben werden, aufgrund der Tatsache, dass es aber weiterhin strings sind, immer noch jedes Zeichen untereinander. Dafür finde ich einfach keine Lösung...
  • tuple habe ich jetzt umbenannt
  • cur und con sind allgemein definiert. (con = sqlite3.connect("database.db"); (cur = con.cursor())
  • sqlalchemy hatte ich vorher noch nie gehört... Werde mich damit aber später beschäftigen und für dieses Projekt außen vor lassen und klassisch direkt die sqlite db ansprechen
  • dt = datetime.datetime (falls sich wer wundert)

Code: Alles auswählen

    def OnDateChanged(self,event):
        from_date_a = self.m_date_from.GetValue()
        till_date_a = self.m_date_till.GetValue()
        from_date_b = from_date_a.Format("%Y-%m-%d")
        till_date_b = till_date_a.Format("%Y-%m-%d")
        from_date_c = dt.strptime(from_date_b,"%Y-%m-%d")
        till_date_c = dt.strptime(till_date_b,"%Y-%m-%d")

        sql_cmd = """
        create table if not exists daterange (range TEXT);
        """
        cur.execute(sql_cmd)

        day_count = (till_date_c - from_date_c).days + 1
        for single_date in (from_date_c + datetime.timedelta(n) for n in range(day_count)):
            date_from_till = str(single_date.date())

            sql_cmd = """
            INSERT INTO daterange
            VALUES(?);
            """
            cur.executemany(sql_cmd, (date_from_till))
            print(date_from_till)

        con.commit()
Benutzeravatar
__blackjack__
User
Beiträge: 1566
Registriert: Samstag 2. Juni 2018, 10:21

Freitag 13. Juli 2018, 11:51

@jake012: Nun ja ein erster Schritt wäre es das nicht mehr als Zeichenkette in die Datenbank zu schreiben, sondern das `Date`-Objekt zu verwenden. Und die Tabellenspalte auch als `DATE` zu deklarieren. Dann schreibt der Code erst einmal gar nichts mehr, denn es wird Ausnahmen geben, dadurch das Du nicht mehr die etwas besondere Eingenschaft von Zeichenketten hast, dass die iterierbar sind und dabei wieder Zeichenketten liefern die iterierbar sind und dabei wieder Zeichenketten liefern die iterierbar und…

Nur deswegen hat die ursprünglich ja schon falsche Verwendung von `executemany()` überhaupt ”funktioniert”. Das war ja schon eine Ebene von Falsch, aber man konnte erahnen was eigentlich gemeint war. Dadurch das Du das jetzt in der Schleife für jedes Datum einzeln machst, ist das sozusagen zwei Ebenen von Falsch. Hier fragt man sich zusätzlich warum überhaupt `executemany()`, das macht doch gar keinen Sinn‽

Zusätzlich zu dem falschen Typ in der Datenbank macht auch der Name `range` für ein *einzelnes* Datum keinen Sinn. Ebenso wie der Tabellenname IMHO nicht stimmt. Üblicherweise beschreibt der *einen* Datensatz, aber *ein* Datensatz ist ja ein Datum und kein Datumsbereich in diesem Beispiel.

`cur` und `con` sollten nicht allgemeint definiert sein. Was eine Funktion oder Methode ausser an Konstanten benötigt, sollte als Argument(e) in die Funktion/Methode kommen. Sonst hat man Abhängigkeiten die es schwer bis unmöglich machen Code isoliert zu betrachten, zu testen, Fehler zu suchen in dem man das mal ”live” in einer Python-Shell oder Testcode aufruft, oder an anderer Stelle wiederzuverwenden. Und einige Werkzeuge zur Dokumentation oder statischen Analyse, sowie Bibliotheken (beispielsweise `multiprocessing` aus der Standardbibliothek) erwarten das man Module ohne Seiteneffekte importieren kann, wie Datenbankverbindungen oder Dateien öffnen oder erstellen.

Damit man `dt` nicht erklären muss importiere ich `datetime.datetime` in meinen Programmen immer als `DateTime`. `cur` und `con` würde ich ausschreiben als `cursor` und `connection`.

Diese `*_date_[a-c]`-Namen sind unschön. Wenn Du da noch einen Verarbeitungsschritt einbaust, musst Du am Ende überall `*_date_c` in `*_date_d` ändern. Und da wo der letzte durchbuchstabierte Name verwendet wird, fragt man sich als Leser was der Buchstabe da zu Bedeuten haben mag. Eigentlich hat er ja gar keine Bedeutung. Man muss die ganzen Zwischenschritte ja auch nicht an Namen binden:

Code: Alles auswählen

        date_format = '%Y-%m-%d'
        from_date = dt.strptime(
            self.m_date_from.GetValue().Format(date_format), date_format
        )
        till_date = dt.strptime(
            self.m_date_till.GetValue().Format(date_format), date_format
        )
Oder man schreibt sich eine Funktion für die Umwandlung, denn die kommt ja ziemlich wahrscheinlich öfter vor:

Code: Alles auswählen

        from_date = wx_date_to_python(self.m_date_from.GetValue())
        till_date = wx_date_to_python(self.m_date_till.GetValue())
Was soll eigentlich das `m_` bei Namen bedeuten? Ich kenne das beispielsweise aus C++ um lokale Namen von Membervariablen zu unterscheiden ohne das man `this.` davor schreiben muss. Da man in Python aber zwingend explizit `self.` verwenden *muss*, macht diese Namenskonvention in Python keinen Sinn und sind einfach nur zusätzliche Zeichen die Python-Programmierer verwirren.

Code: Alles auswählen

    **** COMMODORE 64 BASIC V2 ****
 64K RAM SYSTEM  38911 BASIC BYTES FREE
   CYBERPUNX RETRO REPLAY 64KB - 3.8P
READY.
█
jake012
User
Beiträge: 16
Registriert: Donnerstag 8. März 2018, 12:20

Freitag 13. Juli 2018, 15:03

Hi,

sqlite hat keine Deklaration 'date' oder ähnliches. Das fällt also weg, bzw. ist auch für sqlite nicht relevant.

executemany habe ich deshalb gemacht, weil ich im code die Variablen, also die Daten schreiben möchte. So soll ja jedes Datum vom 01.07.2018 bis 10.07.2018 als Beispiel geschrieben werden (was mir beim Tippen jetzt aber wieder die Überlegung bringt, dass das dann doch nicht in die Schleife, sondern danach geschrieben werden muss. Die Schleife sollte ja eigtl. nur das delta herleiten, zwischenspeichern und dann als Variablen in die Tabelle der db geschrieben werden).

Habe nun auch festgestellt, wenn ich ein print in der Schleife schreibe, dass nur dann alle Daten ausgegeben werden. Bin ich auch der Schleife raus, wird mir nur der letzte Wert übergeben.

Das verstehe ich nicht:
Ebenso wie der Tabellenname IMHO nicht stimmt. Üblicherweise beschreibt der *einen* Datensatz, aber *ein* Datensatz ist ja ein Datum und kein Datumsbereich in diesem Beispiel
Wieso soll der Tabellenname "daterange" nicht stimmen? Kann doch die Tabelle nennen wie ich möchte. Alle Datensätze sind zusammen der Datumsbereich.

Das mit 'date [a-c]' habe ich nur übergangsweise gewählt, bis der Code funktioniert. Bezeichnungen entsprangen dem Ausprobieren, ob es funktioniert oder nicht. Habe ich nun aber auch geändert. Deine Idee mit datetime.datetime as DateTime fand ich gut. habe ich direkt übernommen. Gefällt mir.

m_ im Namen kommt aus dem Programm wxFormBuilder und ich habe den Aufbau der Namen einfach übernommen. Habe mir dabei nichts gedacht :D

Jetzt bekomme ich für from_date und till_date folgenden output:

Code: Alles auswählen

2018-07-01 00:00:00
2018-07-10 00:00:00
und meine date_from_till weiterhin:

Code: Alles auswählen

2018-07-01
2018-07-02
2018-07-03
2018-07-04
2018-07-05
2018-07-06
2018-07-07
2018-07-08
2018-07-09
2018-07-10
Jetzt habe ich noch probiert, eine Liste zu erstellen und die Ausgaben dort 'abzulegen' aber auch das klappt nicht,; die Ausgabe sieht für mich sehr verrückt aus.
Exemplarisch für das erste Datum:

Code: Alles auswählen

[0]
[0, 1]
[0, 1, 2]
[0, 1, 2, 3]
[0, 1, 2, 3, 4]
[0, 1, 2, 3, 4, 5]
[0, 1, 2, 3, 4, 5, 6]
[0, 1, 2, 3, 4, 5, 6, 7]
[0, 1, 2, 3, 4, 5, 6, 7, 8]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
Was ich suche, ist also diese Zeilen so umzuwandeln, dass ich diese Daten einer nach den anderen in eine neue Zeile schreibe. Ich suche schon selbst mit Schlagwörtern wie "string to tuple" oder "string to list" oder "convert string to tuple/list", etc... aber ich finde nichts passendes.

Der aktuelle Codesnip:

Code: Alles auswählen

    def OnDateChanged(self,event):
        date_format = "%Y-%m-%d"
        from_date = DateTime.strptime(self.m_date_from.GetValue().Format(date_format),date_format)
        till_date = DateTime.strptime(self.m_date_till.GetValue().Format(date_format),date_format)

        print(from_date)
        print(till_date)

        sql_cmd = """
                create table if not exists daterange (range TEXT);
                """
        cur.execute(sql_cmd)

        # data_range_list = []

        day_count = (till_date - from_date).days + 1
        # num_of_elements =int(day_count)
        
        print(day_count)
        for single_date in (from_date + datetime.timedelta(n) for n in range(day_count)):
            date_from_till = str(single_date.date())
            print(date_from_till)


            # for i in range(num_of_elements):
            #     data_range_list.append(i)
            #     print(data_range_list)

        sql_cmd = """
                INSERT INTO daterange
                VALUES(?);
                """
        cur.executemany(sql_cmd, (data_range_list))
        print(data_range_list)

        con.commit()
Benutzeravatar
__blackjack__
User
Beiträge: 1566
Registriert: Samstag 2. Juni 2018, 10:21

Freitag 13. Juli 2018, 18:15

@jake012: Doch SQLite kennt auch DATE, und insbesondere kennt halt der Leser und Bibliotheken DATE. Das heisst es dient sowohl als Dokumentation, als auch als Hinweis was Bibliotheken/Programme mit dem Wert machen sollen. An der Stelle ist dann SQLAlchemy wieder besser als die DB-API V2 weil das bei als DATE deklarierten Spalten automatisch das richtige macht: nämlich zwischen dem Wert aus der Datenbank und Python `datetime.date`-Objekten automatisch konvertieren. Das `sqlite3`-Modul macht das nur bei Spalten die als TIMESTAMP deklariert sind. Den Typ hat SQLite streng genommen auch nicht, aber es kennt ihn zumindest, und das `sqlite3`-Modul eben auch und reagiert entsprechend.

Wie gesagt war klar was Du beim `executemany()` nach der Schleife eigentlich machen wolltest, nur hast Du das halt nicht getan, also Du hast in der Schleife nicht die Daten erzeugt *und gespeichert* sondern die Daten erzeugt und alle bis auf das letzte Datum verworfen.

Du kannst die Tabelle auch Käsekuchen nennen und sagen das stimmt. Wie schon gesagt ist es üblicher eine Tabelle nach *einem* Datensatz zu benennen. Und selbst wenn nicht dann ist die Tabelle die nur *einen* Datumsbereich speichert, das aber mit einem Datensatz pro Tag, aber sonst nix, nicht wirklich sinnvoll. Wenn sie mehr als einen Datumsbereich enthalten soll, müsste sie `dateranges` heissen. Dann fangen aber so Überlegungen an wie: Was passiert bei überlappenden oder direkt anschliessenden Datumsbereichen — wie erkennt man die und hält die auseinander. Bei dem Tabellenamen `daterange` hätte ich zumindest so etwas in dieser Art erwartet:

Code: Alles auswählen

CREATE TABLE daterange (
    id INTEGER RPIMARY KEY
    start DATE
    end DATE
)
Also Datensätze die tatsächlich Datumsbereiche beschreiben und eine ID als Fremdschlüssel haben, damit man sich in anderen Tabelln auf einzelne Bereiche beziehen kann.

wxFormBuilder habe ich nie benutzt als ich noch wxWidgets verwendet habe, weil die Python-Anbindung an wxWidgets mehr Widgets bietet als wxWidgets selbst und die aber dann alle nicht im wxFormBuilder zur Verfügung stehen.

Warum sieht die Ausgabe für Dich verrückt aus? In jedem Schleifendurchlauf wird die Liste um ein Element erweitert und dann ausgegeben. Das führt logischerweise genau zu so einer Ausgabe. Und das machst Du wnn die Einrückung von dem auskommentierten Code stimmt, für *jedes* Datum. Also wird die Liste halt auch bei jedem Datum um die Anzahl der Daten länger. `num_of_elements` hat den gleichen Wert wie `day_count` — warum hast Du dafür noch einen Namen eingeführt?

`executemany()` erwartet als zweites Argument eine Sequenz die als Elemente Sequenzen enthält mit einem Wert pro Platzhalter in der SQLAbfrage. Auch wenn da nur ein Platzhalter steht, wird erwartet das es eine Sequenz mit einem Element pro ”Aufruf” des SQL gibt.

Sequenz kann hier beispielsweise jeweils Liste oder Tupel sein. Übrigens ein Grund warum Grunddatentypen wie `list` nicht in Namen vorkommen sollten. `date_range_list` ist entweder `date_range` oder `dates`. Und sollte dann auch existieren und Datumsobjekte enthalten. Beziehungsweise wenn es hier an `executemany()` verfüttert werden soll, dann müssen diese Einzelobjekte jeweils in einem Sequenztyp verpackt sein, weil ja pro Datensatz *ein* Platzhalter ersetzt werden muss.

Code: Alles auswählen

    **** COMMODORE 64 BASIC V2 ****
 64K RAM SYSTEM  38911 BASIC BYTES FREE
   CYBERPUNX RETRO REPLAY 64KB - 3.8P
READY.
█
jake012
User
Beiträge: 16
Registriert: Donnerstag 8. März 2018, 12:20

Freitag 13. Juli 2018, 23:51

Hi,

wusste echt nicht, dass sqlite auch DATE kann. Danke! Mit sqlalchemy werde ich mich gewiss noch auseinandersetzen. Ich glaube, das hat viel mehr potential, als ich derzeit einschätzen kann. Wie dem auch sei, ich habe nach etlichen Stunden nun mein wx.DateTime Problem gelöst bekommen. Habe dafür einen converting-code gefunden:

Code: Alles auswählen

        def wxdate2pydate(date):
            assert isinstance(date, wx.DateTime)
            if date.IsValid():
                ymd = map(int, date.FormatISODate().split('-'))
                return datetime.date(*ymd)
            else:
                print('no')
                return None
Den habe ich nun als Sub-function eingebaut, um diese ansprechen zu können. Nun sieht meine Ausgabe auch so aus: '[datetime.date(2018, 7, 1)]'. Und jedes Datum wird in eine eigene Zeile geschrieben; jetzt auch wieder nur mit execute() in der Schleife.

Die Tabelle wird später als Hilfstabelle fungieren, deshalb auch nur so minimalistisch...

wxFormBuilder: Habe ich auch schon das eine oder andere mal festgestellt, aber für einen Anfänger finde ich das schon eine gute Hilfe, um in den Konstrukten einer class reinzufinden; Simple und schnell.

Die Ausgabe sah für mich 'verrückt' aus, weil ich noch nicht ganz verstand, warum der output eine Aufzählung ist (ie: [0, 1, 2, 3, 4, 5]). day_count war ein Test mit der Schleife da drunter.

Vielen Dank jedenfalls für die ganzen Erklärungen. Nur so lernt man dazu! :)

Hier also mein Snip:

Code: Alles auswählen

    def OnDateChanged(self,event):
        def wxdate2pydate(date):
            assert isinstance(date, wx.DateTime)
            if date.IsValid():
                ymd = map(int, date.FormatISODate().split('-'))
                return datetime.date(*ymd)
            else:
                print('no')
                return None

        from_date = wxdate2pydate(self.m_date_from.GetValue())
        till_date = wxdate2pydate(self.m_date_till.GetValue())
        print(from_date)
        print(till_date)

        sql_cmd = """
                create table if not exists daterange (range DATE);
                """
        cur.execute(sql_cmd)

        day_count = (till_date - from_date).days + 1

        print(day_count)
        for single_date in (from_date + datetime.timedelta(n) for n in range(day_count)):
            date_from_till = [single_date]
            print(date_from_till)

            sql_cmd = """
                    INSERT INTO daterange
                    VALUES(?);
                    """
            cur.execute(sql_cmd, (date_from_till))

        con.commit()
Sirius3
User
Beiträge: 8791
Registriert: Sonntag 21. Oktober 2012, 17:20

Samstag 14. Juli 2018, 10:03

›wxdate2pydata‹ sollte keine interne Funktion sein, denn so kann man sie nicht einzeln testen. Funktionen zu verschachteln macht wirklich nur sehr selten Sinn. Der Rückgabewert None macht keinen Sinn, denn der führt nur dazu, dass sieben Zeilen später eine ganz andere Fehlermeldung entsteht, an der man nicht mehr sieht, was das ursprüngliche Problem war. Richtig wäre es, einen ValueError zu werfen. Das Erzeugen von Tabellen innerhalb von »OnDateChange« ist, wie schon ganz am Anfang geschrieben der falsche Ort. Eine Tabelle, die nur ein Datumsobjekt enthält, ist wie __blackjack__ schon geschrieben hat, nicht sehr sinnvoll.
Bei INSERT sollte man immer die Feldnamen explizit angeben, das macht Umstrukturierungen von Tabellen sonst unmöglich. Was sollen die Klammern um date_from_till bei execute?
jake012
User
Beiträge: 16
Registriert: Donnerstag 8. März 2018, 12:20

Samstag 14. Juli 2018, 13:25

Hi,

eigtl. wollte ich das auch nicht als verschachtelte function machen. Weiß halt nicht wie ich das sonst machen soll. die Funktion aus einer anderen Funktion anzusprechen, hat irgendwie nicht geklappt, oder ich habe was falsch gemacht. Hatte vorher halt die wxdate2pydata auf der selben Ebene wie meine anderen Funktionen in der class und das klappte nicht (obwohl ich davon soo viele Beispiele gefunden habe, aber irgendwas muss ich falsch machen). Na ja, eigtl. sollte none sowieso nie vorkommen bei einem datepicker. Man kann da keine ungültigen Werte eingeben, sondern nur die jeweiligen Tage, Monate, Jahre.

die [ ] um date_from_till machen es erst zu einem Datum-Format, was ich in die Tabelle schreiben kann.
Ausgabe als: date_from_till = single_date = 2018-07-14
Ausgabe als: date_from_till = [single_date] = [datetime.date(2018, 7, 14)]

Ohne die [ ] bekomme ich auch einen ValueError (parameters are of unsupported type).
Benutzeravatar
__blackjack__
User
Beiträge: 1566
Registriert: Samstag 2. Juni 2018, 10:21

Samstag 14. Juli 2018, 13:36

@jake012: Funktionen in Klassen sind Methoden und die müssen das formale Argument für das Objekt auf dem sie aufgerufen werden als erstes Argument erwarten (oder als `@staticmethod` erstellt worden sein), und man muss sie dann natürlich auch über das Objekt aufrufen aus anderen Methoden des selben Objekts. Nur ist das ja aber semantisch eigentlich tatsächlich eine *Funktion*, gehört also gar nicht in die Klasse.

Man kann bei dem Datepicker beispielsweise den Text komplett löschen. Dann liefert der ein `wx.DateTime` das nicht gültig ist und bei Deinem Code zu `None` führt.

Die Frage war nicht nach den eckigen Klammern die das Element in eine Liste verpacken, sondern nach den runden Klammern beim Argument beim Aufruf von `execute()`: ``cur.execute(sql_cmd, (date_from_till))``.

Code: Alles auswählen

    **** COMMODORE 64 BASIC V2 ****
 64K RAM SYSTEM  38911 BASIC BYTES FREE
   CYBERPUNX RETRO REPLAY 64KB - 3.8P
READY.
█
jake012
User
Beiträge: 16
Registriert: Donnerstag 8. März 2018, 12:20

Samstag 14. Juli 2018, 16:00

Danke, wusste ich nicht. Ich habe nun die Funktion wxdate2pydate über alle Klassen gesetzt; kann die Funktion weiterhin ansprechen. Echt genial!

Also der 'wx.adv.DatePickerCtrl' kann bei mir nicht mit untypischen Inhalten überschrieben oder gar ganz geleert werden von einem Anwender. Es ist eine Zelle mit einem vorgegebenen Datum-Format, so ähnlich wie hier: Bild

Ach so, diese Klammern meinst du. Ja gute Frage. Hat scheinbar keinen Effekt. Hab's entfernt :)
Benutzeravatar
__blackjack__
User
Beiträge: 1566
Registriert: Samstag 2. Juni 2018, 10:21

Samstag 14. Juli 2018, 16:22

@jake012: Also ich kann da mit der Maus vorne in den Textbereich reinklicken, und dann den Text da drin löschen und dann das Feld wieder verlassen. Das ist dann leer und das Datumsobjekt das man von dem Widget abfragen kann ist dann ungültig. Ich kann da auch das Tagesdatum auf den 31 im November im Textfeld ändern. Ist auch ein ungültiges Datumsobjekt.

Dabei fällt mir auch auf, das Du die Methode so benannt hast wie in den wx-Beispielen das was an das Ereignis `wx.EVT_DATE_CHANGED` gebunden ist: Das wird jedes mal aufgerufen wenn sich das Datum ändert, also auch wenn man in das Textfeld geht und beispielsweise aus der 2008 eine 2018 macht, dann wird dieses Ereignis *zweimal* ausgelöst. Einmal für das löschen der 0 und einmal für das hinzufügen der 1. Die Methode `OnDateChanged()` würde dann also erst mit dem 11.10.208 (wirklich zweihundertundacht!) und dann noch mal mit dem 11.10.2018 aufgerufen.

Code: Alles auswählen

    **** COMMODORE 64 BASIC V2 ****
 64K RAM SYSTEM  38911 BASIC BYTES FREE
   CYBERPUNX RETRO REPLAY 64KB - 3.8P
READY.
█
jake012
User
Beiträge: 16
Registriert: Donnerstag 8. März 2018, 12:20

Samstag 14. Juli 2018, 17:14

Also bei mir geht das nicht. Es ist tatsächlich so statisch, dass es unmöglich ist. Und wx.EVT_DATE_CHANGED habe ich gar nicht... Bei mir werden alle Funktionen erst beim Klick auf einem Button ausgelöst.

Edit:
Jetzt habe ich mir das nochmal im FormBuilder angesehen... Du kommst darauf, weil meine Funktion OnDateChanged heißt... Hat leider nichts mit dem event DATE_CHANGED zu tun.
Wollte die noch umbenennen; nicht mehr dran gedacht. Hole ich direkt mal nach und benne sie StartCreating
Antworten