8
votes

Comment gérer les dates «partielles» (2010-00-00) de MySQL à Django?

Dans l'un de mes projets Django qui utilisent MySQL comme base de données, j'ai besoin d'avoir une date les champs qui acceptent également des dates "partielles" comme une seule année (aaaa) et année et mois (aaaa Mm) plus la date normale (AAAA-MM-DD).

Le champ date dans MySQL peut traiter cela en acceptant 00 pour le mois et le jour. SO 2010-00-00 est valide dans MySQL et représente 2010. Même chose pour 2010-05-00 qui représente mai 2010.

Alors j'ai commencé à créer un partialdatefield pour prendre en charge cette fonctionnalité. Mais je frappe un mur car, par défaut, et que Django utilise la valeur par défaut, MySQLDB, le pilote Python à MySQL, renvoyer un objet datetime.date objet pour une date date et < Code> DateTime.Date () supporte uniquement la date réelle. Il est donc possible de modifier le convertisseur pour le champ date utilisé par MySQLDB et ne renvoyez qu'une chaîne dans ce format 'yyyy-mm-dd' . Malheureusement, le convertisseur utilise par MySQLDB est défini au niveau de la connexion afin qu'il soit utilisé pour tous les champs MySQL date . Mais Django Datefield s'appuie sur le fait que la base de données renvoie un objet dentetime.date , donc si je change le convertisseur pour renvoyer une chaîne, Django n'est pas du tout heureux. < / p>

Quelqu'un a une idée ou des conseils pour résoudre ce problème? Comment créer un partialdatefield dans django?

Edit

Aussi, je devrais ajouter que j'ai déjà pensé à 2 solutions, créer 3 champs entier pour l'année, le mois et la journée (comme mention par Alison r. ) ou utilisez un varchar Champ pour garder la date en tant que chaîne dans ce format aaaaa-mm-dd .

Mais dans les deux solutions, si je ne me trompe pas, je vais perdre les propriétés spéciales d'un champ date comme faire une requête de ce type sur eux: Obtenez toutes les entrées après cette date . Je peux probablement ré-implémenter cette fonctionnalité sur le côté du client, mais qui ne sera pas une solution valide dans mon cas car la base de données peut être requise à partir d'autres systèmes (client MySQL, accès MS, etc.)


0 commentaires

5 Réponses :


2
votes

Vous pouvez stocker la date partielle sous forme d'entier (de préférence dans un champ nommé pour la partie de la date à laquelle vous stockez, telle que année, mois ou jour ) et faire validation et conversion à un objet de date dans le modèle.

Modifier

Si vous avez besoin de la fonctionnalité de date réelle, vous avez probablement besoin de réelles, non partielles, dates. Par exemple, «TOUT TOUT APRÈS 2010-0-0» Les dates de retour de 2010-0-0 »incluent des dates de 2010 ou seulement en 2011 et au-delà? Il en va de même pour votre autre exemple de mai 2010. La manière dont les différentes langues / clients traitent des dates partielles (si elles les soutiennent du tout) sont susceptibles d'être très idiosyncratiques, et il est peu probable qu'il correspond à la mise en œuvre de MySQL.

D'autre part, si vous stockez un Année INTEGER, tel que 2010, il est facile de poser la base de données pour "tous les enregistrements d'année> 2010" et de comprendre exactement ce que le résultat devrait être, de n'importe quel client, sur n'importe quelle plate-forme. Vous pouvez même combiner cette approche pour des dates / des requêtes plus compliquées, telles que "Tous les enregistrements avec l'année> 2010 et mois> 5".

deuxième modification

Votre seule autre option (et peut-être la meilleure) consiste à stocker des dates vraiment valides et à proposer une convention dans votre application pour ce qu'ils signifient. Un champ DateTime nommé comme date_month pourrait avoir une valeur de 2010-05-01, mais vous traiterez cela comme représentant toutes les dates en mai 2010. Vous auriez besoin de l'adapter à cette programmation. Si vous aviez date_montath dans Python sous forme d'objet DateTime, vous devez appeler une fonction comme date_month.end_of_month () Pour les dates de requête suivant ce mois-ci. (C'est un pseudocode, mais pourrait être facilement mis en œuvre avec quelque chose comme le Calendrier Module.)


1 commentaires

J'ai déjà pensé à cette solution mais je pense que cela ne fonctionnera pas dans mon cas. Voir mon édition.



1
votes

Pouvez-vous stocker la date avec un drapeau qui indique à quel point la date est valide?

Quelque chose comme ceci: xxx

alors, si vous avez un Date comme 2010-00-00, convertissez-la en 2010-01-01 et définissez le drapeau sur Y_VALID. Si vous avez une date comme 2010-06-00, convertissez-la en 2010-06-01 et définissez le drapeau sur ym_valid.

donc, alors, partialDatefield serait une classe qui regroupe une date et le drapeau valide de la date décrit ci-dessus.

ps Vous n'avez pas besoin d'utiliser les drapeaux comme je l'ai montré; C'est l'ancien programmeur C en moi qui vient à la surface. Vous pouvez utiliser y_valid, ym_valid, ymd_valid = gamme (3) et cela fonctionnerait également. La clé est d'avoir une sorte de drapeau qui vous indique à quel point la date de confiance.


3 commentaires

Cette réponse ne traite pas du fait que Python ne considère pas quelque chose comme 2010-00-00 une date valide (même si MySQL le fait). Comment suggérez-vous qu'il stocke et récupérez cela?


Cette réponse suggère de convertir une telle date au 2010-01-01. Regardez, c'est là-bas, je n'ai pas simplement modifié ça. MySQL possède une fonctionnalité où vous pouvez stocker des dates partielles étranges, mais Python ne les comprend pas, alors je recommande spécifiquement de ne pas utiliser les dates partielles étranges, mais plutôt l'utilisation des dates et un drapeau qui dit à quel point la date de la date à laquelle vous pouvez réellement faire confiance. . Vous n'avez pas besoin d'utiliser les drapeaux comme je l'ai montré; C'est l'ancien programmeur C en moi qui vient à la surface. Vous pouvez utiliser y_valid, ym_valid, ymd_valid = gamme (3) et cela fonctionnerait également.


Assez juste. Je ne peux pas révoquer mon bowvote à moins que la réponse soit modifiée, malheureusement. (P.S. Cela semble en effet une solution plutôt C-ISH;)



2
votes

On dirait que vous souhaitez stocker un intervalle de date. En Python, cela (à ma compréhension encore quelque peu négligente) sera le plus facilement mis en œuvre en stockant deux objets Datetime.DateTime, une spécification du début de la plage de date et de l'autre spécifiant la fin. D'une manière similaire à celle utilisée pour spécifier des tranches de liste, le point final ne serait pas lui-même inclus dans la plage de dates.

Par exemple, ce code implémenterait une plage de dates comme tuple nommé: P>

>>> datetime(2012, 12, 21) >= the_year_2010.start
True


1 commentaires

Je ne stocke certainement pas les intervalles de temps. Ce partialDate sera utilisé pour stocker principalement des dates d'anniversaire pour les peuples, les morts et vivants. Donc, souvent, en particulier pour les peuples morts, mon client n'a pas la date totale. Mais cela m'aidait dans mon développement de "penser à une date partielle" comme une gamme ".



7
votes

Tout d'abord, merci pour toutes vos réponses. Aucun d'entre eux, comme c'est une bonne solution pour mon problème, mais pour votre défense, je devrais ajouter que je n'ai pas donné toutes les conditions requises. Mais chacun aidait à moi penser à mon problème et certaines de vos idées font partie de ma solution finale.

Donc, ma solution finale, sur le côté DB, consiste à utiliser un champ Varchar (limité à 10 caractères) et stocker la date en elle, comme une chaîne, dans le format ISO (aaaaa Mm-dd) avec 00 pour le mois et le jour où il n'y a pas de mois et / ou de jour (comme un champ date dans MySQL). De cette façon, ce champ peut fonctionner avec toutes les bases de données, les données peuvent être lues, comprendre et éditer directement et facilement par un humain à l'aide d'un client simple (comme le client MySQL, phpmyadmin, etc.). C'était une exigence. Il peut également être exporté vers Excel / CSV sans aucune conversion, etc. L'inconvénient est que le format n'est pas applicable (sauf à Django). Quelqu'un pourrait écrire 'pas une date' ou faire une erreur dans le format et la DB l'acceptera (si vous avez une idée de ce problème ...).

De cette façon, il est également possible de faire toutes les questions spéciales d'une date date facilement facilement. Pour les requêtes avec où: <,>, <=,> = et = fonctionner directement. Les requêtes et entre les requêtes fonctionnent directement aussi. Pour interrogé par jour ou mois, vous devez juste le faire avec extrait (jour | mois ...). Commander des travaux aussi directement. Je pense donc qu'il couvre toutes les besoins de la requête et avec la plupart des complications.

sur le côté Django, j'ai fait 2 choses. Tout d'abord, j'ai créé un objet partieldate qui ressemble principalement à datetime.date mais la date de prise en charge sans mois et / ou jour. Dans cet objet, j'utilise un objet DateTime.DateTime pour garder la date. J'utilise les heures et les minutes en tant que drapeau qui indiquent si le mois et le jour sont valides quand ils sont définis sur 1. C'est la même idée que steveha propose mais avec une implémentation différente (et seulement sur le côté client). Utilisation d'un objet DateTime.DateTime me donne beaucoup de fonctionnalités agréables pour travailler avec des dates (validation, comparaison, etc.).

Deuxièmement, j'ai créé un partialdatefield qui traite principalement de la conversion entre l'objet partialdate et la base de données.

Jusqu'à présent, cela fonctionne assez bien (j'ai surtout terminé mes tests d'unité étendus).


4 commentaires

Cela semble être une très bonne solution. Je n'aime pas le truc d'utiliser les heures et les minutes comme un drapeau, car je suis inquiet que ce soit un jour que vous deviez les utiliser pour stocker des heures et des minutes, puis vous aurez un problème. Mais si ce jour ne vient jamais, alors je suis inquiet pour rien!


Si vous avez besoin d'étendre ceci pour gérer les heures, les minutes, les secondes, etc. Je vous suggère d'utiliser la norme RFC 3339, car elle partage les mêmes vertus que votre solution (fonctionne avec n'importe quelle base de données, lisible humain, etc.) ietf.org/rfc/rfc3339.txt


Merci pour les informations sur la RFC! Je conviens que l'utilisation des heures et des minutes en tant que drapeau est la seule partie pirate de ma solution, mais sa seule faire des choses si simples pour le tri, etc. Si j'ai besoin de temps dans le futur, je vais créer un nouvel objet partielDateTetime (pas un partialDate) avec une autre mise en œuvre. Mais honnêtement, j'ai du mal à voir le cas d'utilisation pour cela?


Ici semble être une mise en œuvre de cette approche: Gitorious.org/wmbr- Lecture / WMBR-DJ3000 / Source / ...



1
votes

Bien que pas dans Python - voici un exemple de la manière dont le même problème a été résolu dans Ruby - en utilisant une seule valeur entier - et des opérateurs bitwises à stocker une année, un mois et une journée - avec mois et jour en option.

https://github.com/58bits/parttiale-date

Regardez la source en lib pour date.rb et bits.rb.

Je suis sûr qu'une solution similaire pourrait être écrite en python.

Pour persister la date (triable), vous simplement enregistrer l'entier à la base de données.


0 commentaires