Für Django 1.1.
Ich habe dies in meiner models.py:
class User(models.Model):
created = models.DateTimeField(auto_now_add=True)
modified = models.DateTimeField(auto_now=True)
Beim Aktualisieren einer Zeile erhalte ich:
[Sun Nov 15 02:18:12 2009] [error] /home/ptarjan/projects/Twitter-meme/Django/db/backends/mysql/base.py:84: Warning: Column 'created' cannot be null
[Sun Nov 15 02:18:12 2009] [error] return self.cursor.execute(query, args)
Der relevante Teil meiner Datenbank ist:
`created` datetime NOT NULL,
`modified` datetime NOT NULL,
Ist das Anlass zur Sorge?
Nebenfrage: In meinem Admin-Tool werden diese beiden Felder nicht angezeigt. Wird das erwartet
Jedes Feld mit dem Attribut auto_now
erbt auch editable=False
Und wird daher nicht im Admin-Bereich angezeigt. In der Vergangenheit wurde darüber geredet, dass die Argumente auto_now
Und auto_now_add
wegfallen, und obwohl sie immer noch existieren, ist es meiner Meinung nach besser, nur a zu verwenden benutzerdefinierte save()
Methode .
Damit dies richtig funktioniert, würde ich empfehlen, nicht auto_now
Oder auto_now_add
Zu verwenden und stattdessen Ihre eigene save()
-Methode zu definieren, um sicherzustellen, dass created
ist Wird nur aktualisiert, wenn id
nicht festgelegt ist (z. B. beim erstmaligen Erstellen des Elements), und muss modified
jedes Mal aktualisiert werden, wenn das Element gespeichert wird.
Ich habe genau dasselbe mit anderen Projekten gemacht, die ich mit Django geschrieben habe, und so würde Ihr save()
so aussehen:
from Django.utils import timezone
class User(models.Model):
created = models.DateTimeField(editable=False)
modified = models.DateTimeField()
def save(self, *args, **kwargs):
''' On save, update timestamps '''
if not self.id:
self.created = timezone.now()
self.modified = timezone.now()
return super(User, self).save(*args, **kwargs)
Hoffe das hilft!
Bearbeiten als Antwort auf Kommentare:
Der Grund, warum ich mich nur auf das Überladen von save()
beschränke, anstatt mich auf diese Feldargumente zu verlassen, ist zweierlei:
Django.utils.timezone.now()
vs. datetime.datetime.now()
, da abhängig von datetime.datetime
Ein TZ-fähiges oder naives settings.USE_TZ
- Objekt zurückgegeben wird.Um zu klären, warum das OP den Fehler gesehen hat, weiß ich nicht genau, aber es sieht so aus, als ob created
trotz auto_now_add=True
Überhaupt nicht besetzt ist. Für mich ist es ein Fehler und unterstreicht Punkt 1 in meiner kleinen Liste oben: auto_now
Und auto_now_add
Sind bestenfalls schuppig.
Ich möchte jedoch darauf hinweisen, dass die in akzeptierte Antwort ausgedrückte Meinung etwas veraltet ist. Neueren Diskussionen zufolge (Django-Bugs # 7634 und # 12785 ) führen auto_now und auto_now_add nirgendwo hin, und selbst wenn Sie zur Originaldiskussion) gehen , Sie werden starke Argumente gegen das RY (wie in DRY) in benutzerdefinierten Speichermethoden finden.
Es wurde eine bessere Lösung angeboten (benutzerdefinierte Feldtypen), die jedoch nicht genug Schwung gewann, um in Django Fuß zu fassen. Sie können Ihre eigenen in drei Zeilen schreiben (es ist Vorschlag von Jacob Kaplan-Moss ).
from Django.db import models
from Django.utils import timezone
class AutoDateTimeField(models.DateTimeField):
def pre_save(self, model_instance, add):
return timezone.now()
#usage
created_at = models.DateField(default=timezone.now)
updated_at = models.AutoDateTimeField(default=timezone.now)
Apropos Nebenfrage: Wenn Sie diese Felder in admin sehen möchten (Sie können sie jedoch nicht bearbeiten), können Sie Ihrer Admin-Klasse readonly_fields
Hinzufügen.
class SomeAdmin(ModelAdmin):
readonly_fields = ("created","modified",)
Nun, dies gilt nur für die neuesten Django Versionen (ich glaube, 1.3 und höher)
Ich denke, die einfachste (und vielleicht eleganteste) Lösung besteht darin, die Tatsache zu nutzen, dass Sie default
auf einen aufrufbaren Wert setzen können. Um die spezielle Behandlung von auto_now durch admin zu umgehen, können Sie das Feld einfach wie folgt deklarieren:
from Django.utils import timezone
date_filed = models.DateField(default=timezone.now)
Es ist wichtig, dass Sie timezone.now()
nicht verwenden, da der Standardwert nicht aktualisiert wird (d. H. Der Standardwert wird nur festgelegt, wenn der Code geladen wird). Wenn Sie dies häufig tun, können Sie ein benutzerdefiniertes Feld erstellen. Das ist aber schon hübsch DRY finde ich.
Wenn Sie Ihre Modellklasse folgendermaßen ändern:
class MyModel(models.Model):
time = models.DateTimeField(auto_now_add=True)
time.editable = True
Dann erscheint dieses Feld auf meiner Admin-Änderungsseite
Basierend auf dem, was ich gelesen habe und meiner Erfahrung mit Django=) ist auto_now_add fehlerhaft. Ich stimme mit jthanism überein --- überschreibe die normale Speichermethode, sie ist sauber und du weißt, was passiert. Jetzt, Um es trocken zu machen, erstellen Sie ein abstraktes Modell mit dem Namen TimeStamped:
from Django.utils import timezone
class TimeStamped(models.Model):
creation_date = models.DateTimeField(editable=False)
last_modified = models.DateTimeField(editable=False)
def save(self, *args, **kwargs):
if not self.creation_date:
self.creation_date = timezone.now()
self.last_modified = timezone.now()
return super(TimeStamped, self).save(*args, **kwargs)
class Meta:
abstract = True
Und wenn Sie ein Modell mit diesem Zeitstempelverhalten möchten, geben Sie einfach folgende Unterklasse ein:
MyNewTimeStampyModel(TimeStamped):
field1 = ...
Wenn die Felder in admin angezeigt werden sollen, entfernen Sie einfach das editable=False
Möglichkeit
Ist das Anlass zur Sorge?
Nein, Django fügt es automatisch hinzu, während die Modelle gespeichert werden. Es wird also erwartet.
Nebenfrage: In meinem Admin-Tool werden diese beiden Felder nicht angezeigt. Wird das erwartet
Da diese Felder automatisch hinzugefügt werden, werden sie nicht angezeigt.
Um das oben Gesagte zu ergänzen, hat es eine Debatte auf der Django= Mailingliste gegeben, um dies zu entfernen, weil es "nicht gut entworfen" und "ein Hack" ist.
Das Schreiben eines benutzerdefinierten save () für jedes meiner Modelle ist weitaus schmerzhafter als die Verwendung von auto_now
Offensichtlich müssen Sie es nicht auf jedes Modell schreiben. Sie können es in ein Modell schreiben und andere davon erben.
Aber auto_add
und auto_now_add
sind da, ich würde sie eher benutzen, als selbst eine Methode zu schreiben.
Sie können timezone.now()
für erstellt und auto_now
Für geändert verwenden:
from Django.utils import timezone
class User(models.Model):
created = models.DateTimeField(default=timezone.now())
modified = models.DateTimeField(auto_now=True)
Wenn Sie einen benutzerdefinierten Primärschlüssel anstelle des Standardschlüssels auto- increment int
Verwenden, führt auto_now_add
Zu einem Fehler.
Hier ist der Code von Djangos Standard DateTimeField.pre_save mit auto_now
Und auto_now_add
:
def pre_save(self, model_instance, add):
if self.auto_now or (self.auto_now_add and add):
value = timezone.now()
setattr(model_instance, self.attname, value)
return value
else:
return super(DateTimeField, self).pre_save(model_instance, add)
Ich bin nicht sicher, was der Parameter add
ist. Ich hoffe es wird eine Sache wie:
add = True if getattr(model_instance, 'id') else False
Der neue Datensatz hat kein attr
id
, daher gibtgetattr(model_instance, 'id')
False zurück, was dazu führt, dass im Feld kein Wert festgelegt wird.
Informationen zu Ihrer Admin-Anzeige finden Sie unter diese Antwort .
Hinweis: auto_now
Und auto_now_add
Sind standardmäßig auf editable=False
Eingestellt, weshalb dies gilt.
Ich brauchte heute bei der Arbeit etwas Ähnliches. Der Standardwert ist timezone.now()
, kann jedoch sowohl in Administrator- als auch in Klassenansichten bearbeitet werden, die von FormMixin
erben. Daher erfüllte der folgende Code für die Erstellung in meinem models.py
Diese Anforderungen:
from __future__ import unicode_literals
import datetime
from Django.db import models
from Django.utils.functional import lazy
from Django.utils.timezone import localtime, now
def get_timezone_aware_now_date():
return localtime(now()).date()
class TestDate(models.Model):
created = models.DateField(default=lazy(
get_timezone_aware_now_date, datetime.date)()
)
Für DateTimeField
entferne ich wohl die .date()
aus der Funktion und ändere datetime.date
In datetime.datetime
Oder besser timezone.datetime
. Ich habe es nicht mit DateTime
versucht, nur mit Date
.
auto_now=True
hat in Django 1.4.1) nicht funktioniert, aber der folgende Code hat mich gerettet.
from Django.utils.timezone import get_current_timezone
from datetime import datetime
class EntryVote(models.Model):
voted_on = models.DateTimeField(auto_now=True)
def save(self, *args, **kwargs):
self.voted_on = datetime.now().replace(tzinfo=get_current_timezone())
super(EntryVote, self).save(*args, **kwargs)