datetime und date

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
mechanicalStore
User
Beiträge: 124
Registriert: Dienstag 29. Dezember 2009, 00:09

Hallo,

Folgender Code (stark gekürzt):

Code: Alles auswählen

from datetime import datetime

class Base(DeclarativeBase):
    type_annotation_map = {
        datetime: TIMESTAMP(timezone=True
        }
        
class Part(Base):
    testing_date: Mapped[datetime]
    ....

def main():
	example = Part(testing_date = datetime.now()
	....
	result = session.scalars(select(Part.testing_date).first()
	print(result)
Ausgabe:

Code: Alles auswählen

2024-04-12 12:31:53.516356
Ich will nach Datum filtern. Speichere ich das ganze so ab:

Code: Alles auswählen

format(Part.testing_date, "%Y-%m-%d")
habe ich nur noch einen String, was ich auch nicht will. Wie kann ich das reine Datum (als Datumsobjekt) ablegen und danach suchen?

Danke und Gruß
Benutzeravatar
__blackjack__
User
Beiträge: 13123
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@mechanicalStore: Auf Python-Seite verwendest Du ein `datetime.date`-Objekt und bei der Abfrage musst Du das Datum vom TIMESTAMP abfragen. Also so etwas ``select().where(func.date(Part.testing_date) == some_date)``
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
mechanicalStore
User
Beiträge: 124
Registriert: Dienstag 29. Dezember 2009, 00:09

__blackjack__ hat geschrieben: Freitag 12. April 2024, 13:39 @mechanicalStore: Auf Python-Seite verwendest Du ein `datetime.date`-Objekt und bei der Abfrage musst Du das Datum vom TIMESTAMP abfragen. Also so etwas ``select().where(func.date(Part.testing_date) == some_date)``
Hallo __blackjack__,

Code: Alles auswählen

class Base(DeclarativeBase):
    type_annotation_map = {
        datetime.date: TIMESTAMP(timezone=True),
        float: Float,
        bool: Boolean
    }

Code: Alles auswählen

class Part(Base):
	...
	testing_date: Mapped[datetime.date]
Hier scheitert es schon am Mapping. Ich finde auch keinen Hinweis auf eine func.date Funktion in Verbindung mit TIMESTAMP, oder meintest Du, dass die selbst definiert werden muss?
Benutzeravatar
__blackjack__
User
Beiträge: 13123
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@mechanicalStore: `func` ist aus `sqlalchemy` und das generiert beliebige SQL-Funktionen.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
mechanicalStore
User
Beiträge: 124
Registriert: Dienstag 29. Dezember 2009, 00:09

__blackjack__ hat geschrieben: Dienstag 16. April 2024, 08:46 @mechanicalStore: `func` ist aus `sqlalchemy` und das generiert beliebige SQL-Funktionen.
Hallo __blackjack__

Ich dachte, sqlalchemy ist genau dazu da, sich nicht mit den native SQL Themen auseinandersetzen zu müssen. Und wie gesagt, endet schon die o.g. kleine Anpassung in Fehlermeldungen. Offenbar bin ich hier auf dem Holzweg. Hast Du ggf. ein minimal lauffähiges Beispiel?
Benutzeravatar
__blackjack__
User
Beiträge: 13123
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@mechanicalStore: SQLAlchemy befreit einen nicht davon SQL zu kennen/können. Man muss es halt nur nicht selber schreiben oder schlimmer, Code schreiben der SQL-Anfragen als Zeichenketten zusammenbastelt, und man bekommt eine Abbildung der SQL-Datentypen auf Python-Datentypen und Datensätze auf Objekte in beide Richtungen, die man sich nicht von Hand basteln muss.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
mechanicalStore
User
Beiträge: 124
Registriert: Dienstag 29. Dezember 2009, 00:09

__blackjack__ hat geschrieben: Dienstag 16. April 2024, 10:13 @mechanicalStore: SQLAlchemy befreit einen nicht davon SQL zu kennen/können. Man muss es halt nur nicht selber schreiben oder schlimmer, Code schreiben der SQL-Anfragen als Zeichenketten zusammenbastelt, und man bekommt eine Abbildung der SQL-Datentypen auf Python-Datentypen und Datensätze auf Objekte in beide Richtungen, die man sich nicht von Hand basteln muss.
Hallo __blackjack__,

danke für Deine Antwort. Das ist mir schon klar. Hilft halt aktuell nicht weiter. Ein Beispiel wäre gut gewesen, da ich total auf dem Schlauch stehe. Weiß aber, dass es hier keine Anrechte darauf gibt.

Danke Dir trotzdem.

Gruß
Benutzeravatar
__blackjack__
User
Beiträge: 13123
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Ich weiss nicht so recht was ein Minimalbeispiel jetzt mehr bringen soll als das was ich schon geschrieben hatte, denn das funktioniert bei mir, und hat nicht wirklich etwas mit dem Mapping zu tun, solange die Spalte nur auf SQL-Seite TIMESTAMP ist. Noch mit SQLAlchemy 1.4, aber das sollte für die Abfrage keinen Unterschied machen:

Code: Alles auswählen

#!/usr/bin/env python3
from datetime import date as Date

import sqlalchemy as sa
from sqlalchemy.orm import Session, declarative_base

Base = declarative_base()


class Record(Base):
    __tablename__ = "records"
    id = sa.Column(sa.Integer, primary_key=True)
    timestamp = sa.Column(sa.TIMESTAMP, nullable=False)
    value = sa.Column(sa.REAL, nullable=False)


def main():
    engine = sa.create_engine("sqlite:///test.db", future=True, echo=True)
    session = Session(engine, future=True)
    print(
        session.execute(
            sa.select(Record).where(
                sa.func.date(Record.timestamp) == Date(2024, 1, 4)
            )
        ).all()
    )


if __name__ == "__main__":
    main()
Beispieldaten hatte ich interaktiv mit Pandas erstellt:

Code: Alles auswählen

In [550]: df=pd.DataFrame({"timestamp": pd.date_range("2024-01-01", periods=100, freq="1h"), "value": np.random.random(100)*10})

In [551]: df.index.name = "id"

In [552]: con = sqlite3.connect("test.db")

In [553]: df.to_sql("records", con)
Out[553]: 100
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
mechanicalStore
User
Beiträge: 124
Registriert: Dienstag 29. Dezember 2009, 00:09

Hallo __blackjack__

vielen Dank für das Beispiel! Tatsächlich hatte ich es mittlerweile selbst lösen können. Ich hatte das falsch verstanden, nämlich, dass ich func.dateselbst definieren muss. Lag daran, dass ich func nicht aus dem sqlalchemy Namensraum importiert hatte (ich importiere immer nur das, was ich brauche, nicht den ganzen Namensraum). Dennoch verstehe ich folgendes noch nicht.

Folgendes funktioniert nicht:

Code: Alles auswählen

from datetime import datetime

class Base(DeclarativeBase):
    type_annotation_map = {
        datetime.date: TIMESTAMP(timezone=True),

class Part(Base):
	testing_date: Mapped[datetime.date]
	....

example = Part(testing_date = datetime.now().date(),
....
stmt = select(func.date(Part.testing_date))
.....
und führt am Ende zu:

Code: Alles auswählen

.../.venv/lib/python3.11/site-packages/sqlalchemy/orm/decl_api.py", line 1259, in _resolve_type
    search = ((pt, pt) for pt in python_type_type.__mro__)
                                 ^^^^^^^^^^^^^^^^^^^^^^^^
AttributeError: 'method_descriptor' object has no attribute '__mro__'
Folgendes aber schon:

Code: Alles auswählen

from datetime import datetime, date

class Base(DeclarativeBase):
    type_annotation_map = {
        date: TIMESTAMP(timezone=True),

class Part(Base):
	testing_date: Mapped[date]
	....

example = Part(testing_date = datetime.now().date(),
....
stmt = select(func.date(Part.testing_date))
.....
Einziger Unterschied ist, dass ich sowohl in der type_annotation, als auch in der Klassendefinition den datetime Namensraum mit angegeben hatte, was offenbar nicht geht. Warum, ist mir nicht klar.

Und warum gibt es nur

Code: Alles auswählen

datetime.now().date()
und nicht

Code: Alles auswählen

date.now()
Benutzeravatar
snafu
User
Beiträge: 6744
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Einmal hast du eine Klasse:

Code: Alles auswählen

>>> from datetime import date
>>> print(date)
<class 'datetime.date'>
Und einmal hast du eine Methode:

Code: Alles auswählen

>>> from datetime import datetime
>>> print(datetime.date)
<method 'date' of 'datetime.datetime' objects>
Das macht natürlich schon einen Unterschied. Das erste ist ``datetime.date`` und das zweite ist ``datetime.datetime.date()``.

Aufgrund der Namensgebung kann man damit schon mal durcheinander kommen. Für die Typ-Annotation brauchst du auf jeden Fall die Klasse.

Beachte auch, dass du durch den zweiten Import das ursprüngliche ``datetime``-Modul überschreibst. Unter anderem deshalb importiert man das besser mit:

Code: Alles auswählen

from datetime import datetime as dt
oder:

Code: Alles auswählen

from datetime import datetime as DateTime
Letzteres entspricht den üblichen Konventionen für die Benennung von Klassen.
Manul
User
Beiträge: 53
Registriert: Samstag 13. Februar 2021, 16:00

mechanicalStore hat geschrieben: Dienstag 16. April 2024, 15:58 Und warum gibt es nur

Code: Alles auswählen

datetime.now().date()
und nicht

Code: Alles auswählen

date.now()
Weil letzteres

Code: Alles auswählen

date.today()
heißt.
mechanicalStore
User
Beiträge: 124
Registriert: Dienstag 29. Dezember 2009, 00:09

@snafu: Danke für die Tipps und für die Aufklärung, was nun völlig einleuchtend ist.

@Manul: Danke auch Dir für den Hinweis. Manchmal kann es so einfach seiin...
Antworten