it-swarm.com.de

Django auto_now und auto_now_add

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

245
Paul Tarjan

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:

  1. Die oben genannten Höhen und Tiefen mit ihrer Zuverlässigkeit. Diese Argumente hängen stark davon ab, wie jeder Datenbanktyp, der Django= mit einem Datums-/Zeitstempelfeld umgehen kann, zwischen den einzelnen Releases zu wechseln scheint Ich glaube, das ist der Anstoß für den Aufruf, sie insgesamt zu entfernen.
  2. Die Tatsache, dass sie nur für DateField, DateTimeField und TimeField funktionieren, und mit dieser Technik können Sie jeden Feldtyp bei jedem Speichern eines Elements automatisch auffüllen.
  3. Verwenden Sie 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.

333
jathanism

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)
156
Shai Berger

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)

30
DataGreed

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.

23
Josh

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

17
Eric Zheng

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

12
Edward Newell

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.

5
Lakshman Prasad

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 gibt getattr(model_instance, 'id') False zurück, was dazu führt, dass im Feld kein Wert festgelegt wird.

2
suhailvs

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.

2
jlovison

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.

2
Tiphareth

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)
1
Ryu_hayabusa