Wie behandelt man "partielle" Daten (2010-00-00) von MySQL in Django?

8

In einem meiner Django-Projekte, die MySQL als Datenbank verwenden, muss ich ein Datum Felder haben, die auch "teilweise" Daten wie nur Jahr (YYYY) und Jahr und Monat (YYYY-) akzeptieren MM) plus normales Datum (JJJJ-MM-TT).

Das Datum Feld in MySQL kann damit umgehen, indem es 00 für den Monat und den Tag akzeptiert. So ist 2010-00-00 in MySQL gültig und repräsentiert 2010. Dasselbe gilt für 2010-05-00 , das Mai 2010 repräsentiert.

Also habe ich angefangen, ein PartialDateField zu erstellen, um diese Funktion zu unterstützen. Aber ich stoße gegen eine Wand, weil Django standardmäßig den Standardwert MySQLdb, den Python-Treiber für MySQL verwendet, ein datetime.date -Objekt für ein Datum -Feld zurückgibt und datetime.date() nur echtes Datum unterstützt. Es ist also möglich, den Konverter für das von MySQLdb verwendete Datum Feld zu ändern und nur eine Zeichenkette in diesem Format 'YYYY-MM-DD' zurückzugeben. Leider ist die Verwendung des Konverters durch MySQLdb auf der Verbindungsebene festgelegt, so dass es für alle MySQL date Felder verwendet wird. Aber Django DateField verlässt sich auf die Tatsache, dass die Datenbank ein datetime.date -Objekt zurückgibt. Wenn ich also den Konverter ändere, um eine Zeichenkette zurückzugeben, ist Django überhaupt nicht glücklich.

Jemand hat eine Idee oder einen Rat, um dieses Problem zu lösen? Wie erstelle ich ein PartialDateField in Django?

BEARBEITEN

Ich sollte auch hinzufügen, dass ich bereits an 2 Lösungen gedacht habe, 3 ganzzahlige Felder für Jahr, Monat und Tag (wie von Alison R. erwähnt) oder ein varchar Feld, um das Datum als String in diesem Format zu halten YYYY-MM-DD .

Aber in beiden Lösungen, wenn ich nicht falsch liege, werde ich die speziellen Eigenschaften eines Datums Feldes verlieren, als würde man eine solche Abfrage auf sie anwenden: Erhalten Sie alle Einträge nach diesem Datum . Ich kann diese Funktionalität wahrscheinlich auf der Client-Seite neu implementieren, aber das wird in meinem Fall keine gültige Lösung sein, da die Datenbank von anderen Systemen (mysql-Client, MS Access, etc.) abgefragt werden kann.

    
Etienne 04.06.2010, 02:49
quelle

5 Antworten

6

Zuerst, vielen Dank für all Ihre Antworten. Keiner von ihnen, wie es ist, war eine gute Lösung für mein Problem, aber zu Ihrer Verteidigung, ich sollte hinzufügen, dass ich nicht alle Anforderungen gegeben habe. Aber jeder hilft mir, über mein Problem nachzudenken und einige Ihrer Ideen sind Teil meiner endgültigen Lösung.

Also ist meine letzte Lösung auf der DB-Seite, ein varchar -Feld zu verwenden (begrenzt auf 10 Zeichen) und das Datum darin als String im ISO-Format zu speichern (YYYY- MM-DD) mit 00 für Monat und Tag, an dem es keinen Monat und / oder Tag gibt (wie ein Datum Feld in MySQL). Auf diese Weise kann dieses Feld mit beliebigen Datenbanken arbeiten, die Daten können direkt und einfach von einem Menschen gelesen, verstanden und bearbeitet werden, indem ein einfacher Client verwendet wird (wie zB mysql client, phpmyadmin, etc.). Das war eine Voraussetzung. Es kann auch ohne Konvertierung in Excel / CSV exportiert werden. Der Nachteil ist, dass das Format nicht erzwungen wird (außer in Django). Jemand könnte 'kein Datum' schreiben oder einen Fehler im Format machen und die DB wird es akzeptieren (wenn Sie eine Idee zu diesem Problem haben ...).

Auf diese Weise ist es auch möglich, alle speziellen Abfragen eines Datums -Feldes relativ einfach durchzuführen. Für Abfragen mit WHERE: & lt ;, & gt ;, & lt; =, & gt; = und = direkt arbeiten. Die IN- und BETWEEN-Queries funktionieren auch direkt. Für die Abfrage nach Tag oder Monat müssen Sie es nur mit EXTRACT (DAY | MONTH ...) machen. Bestellung funktioniert auch direkt. Also ich denke, es deckt alle Abfrage Bedürfnisse und mit meist keine Komplikationen.

Auf der Django-Seite habe ich zwei Dinge gemacht. Zuerst habe ich ein PartialDate -Objekt erstellt, das größtenteils wie datetime.date , aber unterstützendes Datum ohne Monat und / oder Tag aussieht. In diesem Objekt verwende ich ein datetime.datetime Objekt, um das Datum zu behalten. Ich verwende die Stunden und Minuten als Flag, die angeben, ob der Monat und der Tag gültig sind, wenn sie auf 1 gesetzt sind. Es ist die gleiche Idee, die steveha vorschlägt, aber mit einer anderen Implementierung (und nur auf der Client-Seite). Die Verwendung eines datetime.datetime -Objekts bietet mir viele nützliche Funktionen für die Arbeit mit Daten (Validierung, Vergleich, etc.).

Zweitens habe ich eine PartialDateField erstellt, die sich hauptsächlich mit der Konvertierung zwischen dem PartialDate -Objekt und der Datenbank befasst.

Bisher funktioniert es ziemlich gut (ich habe meistens meine umfangreichen Komponententests beendet).

    
Etienne 12.06.2010, 03:50
quelle
2

Sie könnten das Teildatum als ganze Zahl speichern (vorzugsweise in einem Feld, das nach dem Teil des zu speichernden Datums benannt ist, z. B. year, month oder day ) und Validierung und Konvertierung in ein Datumsobjekt durchführen im Modell.

BEARBEITEN

Wenn Sie eine echte Datumsfunktion benötigen, benötigen Sie wahrscheinlich echte, nicht teilweise Daten. Zum Beispiel, gibt es "alles nach 2010-0-0" Rückkehr Daten einschließlich 2010 oder nur Daten in 2011 und darüber hinaus? Dasselbe gilt für Ihr anderes Beispiel von Mai 2010. Die Art und Weise, in der verschiedene Sprachen / Clients mit Teildaten umgehen (wenn sie diese überhaupt unterstützen), ist wahrscheinlich sehr eigenwillig und sie werden wahrscheinlich nicht mit der Implementierung von MySQL übereinstimmen.

>

Wenn Sie andererseits eine year ganze Zahl wie 2010 speichern, ist es einfach, die Datenbank nach "alle Datensätze mit Jahr & gt; 2010" zu fragen und genau zu verstehen, was das Ergebnis von jedem Client sein soll. auf jeder Plattform. Sie können diesen Ansatz sogar für komplexere Daten / Abfragen kombinieren, z. B. "Alle Datensätze mit Jahr & gt; 2010 UND Monat & gt; 5".

ZWEITE BEARBEITUNG

Ihre einzige (und vielleicht beste) Option ist es, wirklich gültige Daten zu speichern und eine Konvention in Ihrer Bewerbung zu erstellen. Ein DATETIME-Feld mit dem Namen date_month könnte den Wert 2010-05-01 haben, aber Sie würden dies als Darstellung aller Daten im Mai 2010 behandeln. Sie müssten dies beim Programmieren berücksichtigen. Wenn Sie date_month in Python als Datetime-Objekt hätten, müssten Sie eine Funktion wie date_month.end_of_month() aufrufen, um das Datum nach diesem Monat abzufragen. (Das ist Pseudocode, könnte aber leicht mit dem Kalender -Modul implementiert werden.)

    
Alison R. 04.06.2010 02:54
quelle
2

Es klingt, als ob Sie ein Datumsintervall speichern möchten. In Python würde dies (zu meinem noch etwas noblen Verständnis) am einfachsten implementiert werden, indem zwei datetime.datetime-Objekte gespeichert werden, von denen eines den Beginn des Datumsbereichs und das andere das Ende angibt. Ähnlich wie bei der Festlegung von Listenabschnitten wurde der Endpunkt nicht selbst in den Datumsbereich aufgenommen.

Dieser Code würde beispielsweise einen Datumsbereich als benanntes Tupel implementieren:

%Vor%

Oder fügen Sie sogar etwas Magie hinzu:

%Vor%

Das ist so ein nützliches Konzept, dass ich mir ziemlich sicher bin, dass jemand bereits eine Implementierung zur Verfügung gestellt hat. Ein kurzer Blick deutet beispielsweise darauf hin, dass die Klasse relativedate aus dem Paket dateutil dies tut und Ausdrücklicher, indem ein Schlüsselwort-Argument 'years' an den Konstruktor übergeben wird.

Das Zuordnen eines solchen Objekts zu Datenbankfeldern ist jedoch etwas komplizierter. Daher sollten Sie es besser implementieren, indem Sie einfach beide Felder einzeln extrahieren und dann kombinieren. Ich denke, das hängt vom DB-Framework ab; Ich kenne diesen Aspekt von Python noch nicht sehr gut.

In jedem Fall denke ich, dass der Schlüssel darin besteht, ein "Teildatum" als einen Bereich und nicht als einen einfachen Wert zu betrachten.

bearbeite

Es ist verlockend, aber ich halte es für unangebracht, weitere magische Methoden hinzuzufügen, die die Verwendung der Operatoren > und < behandeln. Es gibt ein bisschen Zweideutigkeit: Gibt es ein Datum, das größer ist als ein bestimmter Bereich nach dem Ende des Bereichs oder nach seinem Beginn? Es erscheint zunächst angebracht, <= zu verwenden, um anzuzeigen, dass das Datum auf der rechten Seite der Gleichung nach dem Anfang des Bereichs liegt, und < , um anzuzeigen, dass es nach dem Ende ist.

Dies bedeutet jedoch eine Gleichheit zwischen dem Bereich und einem Datum innerhalb des Bereichs, was inkorrekt ist, da es impliziert, dass der Monat Mai 2010 dem Jahr 2010 entspricht, da der 4. Mai 2010 den beiden entspricht Sie. IE würden Sie mit Falsismen enden, wie 2010-04-20 == 2010 == 2010-05-04 wahr ist.

Wahrscheinlich wäre es also besser, eine Methode wie isafterstart zu implementieren, um explizit zu prüfen, ob ein Datum nach dem Beginn des Bereichs liegt. Aber wieder ist es wahrscheinlich schon jemand getan, also ist es wahrscheinlich einen Blick auf pypi wert, um zu sehen, was produktionsbereit ist. Dies wird durch das Vorhandensein von "Entwicklungsstatus :: 5 - Produktion / Stabil" im Abschnitt "Kategorien" der Pypi-Seite eines bestimmten Moduls angezeigt. Beachten Sie, dass nicht alle Module einen Entwicklungsstatus erhalten haben.

Oder Sie können es einfach halten und die grundlegende Implementierung von namedtuple explizit überprüfen

%Vor%     
intuited 04.06.2010 23:00
quelle
1

Können Sie das Datum zusammen mit einem Flag speichern, das angibt, wie viel des Datums gültig ist?

In etwa so:

%Vor%

Wenn Sie ein Datum wie 2010-00-00 haben, dann konvertieren Sie das zu 2010-01-01 und setzen Sie das Flag auf Y_VALID. Wenn Sie ein Datum wie 2010-06-00 haben, konvertieren Sie das in 2010-06-01 und setzen Sie das Flag auf YM_VALID.

Also wäre PartialDateField eine Klasse, die ein Datum und das oben beschriebene Datum-gültig-Flag zusammenfasst.

P.S. Sie brauchen die Flaggen nicht so zu benutzen, wie ich sie gezeigt habe; Das ist der alte C-Programmierer in mir, der an die Oberfläche kommt. Sie könnten Y_VALID, YM_VALID, YMD_VALID = range (3) verwenden und es würde auch funktionieren. Der Schlüssel ist, eine Art Flagge zu haben, die dir sagt, wie viel des Datums vertrauenswürdig ist.

    
steveha 04.06.2010 05:22
quelle
1

Obwohl nicht in Python - hier ist ein Beispiel, wie das gleiche Problem in Ruby gelöst wurde - mit einem einzigen Integer-Wert - und bitweise Operatoren zum Speichern von Jahr, Monat und Tag - mit optionalem Monat und Tag.

Ссылка

Sehen Sie sich die Quelle in lib für date.rb und bits.rb an.

Ich bin sicher, dass eine ähnliche Lösung in Python geschrieben werden könnte.

Um das Datum beizubehalten (sortierbar) speichern Sie einfach die Ganzzahl in der Datenbank.

    
Blue Waters 28.05.2012 18:00
quelle

Tags und Links