Для Django 1.1.
У меня это в моих models.py:
class User(models.Model):
created = models.DateTimeField(auto_now_add=True)
modified = models.DateTimeField(auto_now=True)
При обновлении строки я получаю:
[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)
Соответствующая часть моей базы данных:
`created` datetime NOT NULL,
`modified` datetime NOT NULL,
Является ли это поводом для беспокойства?
Боковой вопрос: в моем админ-инструменте эти два поля не отображаются. Ожидается ли это?
Любое поле с auto_now
атрибутов auto_now
также наследует editable=False
и поэтому не будет отображаться в панели администратора. В прошлом были разговоры о том, что аргументы auto_now
и auto_now_add
, и, хотя они все еще существуют, я чувствую, что вам лучше использовать собственный метод save()
.
Поэтому, чтобы сделать эту работу должным образом, я бы рекомендовал не использовать auto_now
или auto_now_add
и вместо этого определить свой собственный метод save()
чтобы убедиться, что created
обновляется только в том случае, если id
не установлен (например, когда элемент сначала создан) и его обновление modified
каждый раз, когда элемент будет сохранен.
Я сделал то же самое с другими проектами, которые я написал с помощью Django, и поэтому ваш save()
будет выглядеть так:
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)
Надеюсь это поможет!
Редактировать в ответ на комментарии:
Причина, по которой я просто придерживаюсь перегрузки save()
и полагаюсь на эти аргументы поля, в два раза:
django.utils.timezone.now()
vs. datetime.datetime.now()
, потому что он вернет объект с поддержкой TZ или наивный datetime.datetime
зависимости от settings.USE_TZ
. Чтобы решить, почему OP увидел ошибку, я точно не знаю, но похоже, что created
даже не был заполнен, несмотря на то, что auto_now_add=True
. Для меня это выделяется как ошибка, и подчеркивает пункт №1 в моем маленьком списке выше: auto_now
и auto_now_add
в лучшем случае auto_now_add
.
Ба... Недостаточно репутации для комментариев... Но я хотел указать, что мнение, выраженное в принятом ответе, несколько устарело. Согласно более поздним обсуждениям (django bugs # 7634 и # 12785), auto_now и auto_now_add никуда не денутся, и даже если вы перейдете к оригинальному обсуждению, вы найдете сильные аргументы против RY ( как в DRY) в пользовательских методах сохранения.
Было предложено лучшее решение (настраиваемые типы полей), но не набрало достаточного количества импульса, чтобы превратить его в django. Вы можете написать свои собственные в трех строках (это предложение Якова Каплана-Мосса).
class AutoDateTimeField(models.DateTimeField):
def pre_save(self, model_instance, add):
return datetime.datetime.now()
#usage
created_at = models.DateField(default=timezone.now)
updated_at = models.AutoDateTimeField(default=timezone.now)
Говоря о боковом вопросе: если вы хотите видеть эти поля в admin (хотя вы не сможете его редактировать), вы можете добавить readonly_fields
в свой класс администратора.
class SomeAdmin(ModelAdmin):
readonly_fields = ("created","modified",)
Ну, это относится только к последним версиям Django (я считаю, 1.3 и выше)
XxAdmin
. Я прочитал его слишком быстро и попытался добавить его в мои AdminForm
или ModelForm
и понятия не имел, почему они не рендерили «поля только для чтения». Кстати, есть ли возможность иметь истинные "поля только для чтения в форме?
Я думаю, что самое легкое (и, может быть, самое элегантное) решение состоит в том, чтобы использовать тот факт, что вы можете установить default
на вызываемый. Таким образом, чтобы обойти администрацию специальной обработкой auto_now, вы можете просто объявить поле следующим образом:
from django.utils import timezone
date_filed = models.DateField(default=timezone.now)
Важно, чтобы вы не использовали timezone.now()
, поскольку значение по умолчанию не обновлялось (т.е. значение по умолчанию устанавливается только при загрузке кода). Если вы обнаружите, что делаете это много, вы можете создать настраиваемое поле. Тем не менее, это довольно СУХОЙ, я думаю.
Если вы измените свой класс модели следующим образом:
class MyModel(models.Model):
time = models.DateTimeField(auto_now_add=True)
time.editable = True
Тогда это поле появится на моей странице смены администратора.
На основе того, что я прочитал, и моего опыта работы с Django до сих пор, auto_now_add не работает. Я соглашаюсь с jthanism --- переопределить нормальный метод сохранения, который он чист, и вы знаете, что такое hapenning. Теперь, чтобы сделать его сухим, создайте абстрактную модель под названием 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
И тогда, когда вам нужна модель, которая имеет это вредоносное поведение, просто подкласс:
MyNewTimeStampyModel(TimeStamped):
field1 = ...
Если вы хотите, чтобы поля отображались в admin, просто удалите опцию editable=False
timezone.now()
вы используете здесь? Я предполагаю, django.utils.timezone.now()
, но я не уверен. Кроме того, зачем использовать timezone.now()
а не datetime.datetime.now()
?
timezone.now()
заключается в том, что он поддерживает часовой пояс, а datetime.datetime.now()
- наивный. Вы можете прочитать об этом здесь: docs.djangoproject.com/en/dev/topics/i18n/timezones
Вы можете использовать timezone.now()
для созданного и auto_now
для измененного:
from django.utils import timezone
class User(models.Model):
created = models.DateTimeField(default=timezone.now())
modified = models.DateTimeField(auto_now=True)
Если вместо стандартного auto- increment int
используется пользовательский первичный ключ, auto_now_add
приведет к ошибке.
Вот код Django по умолчанию DateTimeField.pre_save с auto_now
и 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)
Я не уверен, что параметр add
. Я надеюсь, что это будет что-то вроде:
add = True if getattr(model_instance, 'id') else False
Новая запись не будет иметь атр
id
, такgetattr(model_instance, 'id')
вернет значение False приведет к не устанавливать какое - либо значение в поле.
Является ли это поводом для беспокойства?
Нет, Django автоматически добавляет его для вас при сохранении моделей, поэтому ожидается.
Боковой вопрос: в моем админ-инструменте эти 2 поля не отображаются. Ожидается ли это?
Поскольку эти поля добавляются автоматически, они не отображаются.
Чтобы добавить к сказанному выше, как сказал сикэк, в списке рассылки django произошли дебаты, чтобы удалить это, потому что он "не разработан хорошо" и "взломан"
Написание пользовательских save() на каждой из моих моделей намного больнее, чем использование auto_now
Очевидно, вам не нужно записывать его в каждую модель. Вы можете записать его в одну модель и наследовать другие.
Но, поскольку auto_add
и auto_now_add
есть, я использовал бы их вместо того, чтобы пытаться написать метод самостоятельно.
Мне понадобилось что-то подобное сегодня на работе. Значение по умолчанию - timezone.now(), но редактируемое как в представлении администратора, так и в классе, наследующем FormMixin, поэтому для созданных в моих моделях .py следующий код выполнил эти требования:
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)()
)
Для DateTimeField, я думаю, удалить функцию .date() из функции и изменить datetime.date на datetime.datetime или лучше timezone.datetime. Я не пробовал это с DateTime, только с Date.
Что касается вашего экрана администратора, см. этот ответ.
Примечание: auto_now и auto_now_add установлены на editable = False по умолчанию, поэтому это применимо.
auto_now=True
не работал у меня в Django 1.4.1, но приведенный ниже код спас меня. Он предназначен для определения времени и времени в часовом поясе.
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)
Здесь ответ, если вы используете юг и хотите по умолчанию указать дату добавления поля в базу данных:
Выберите опцию 2 затем: datetime.datetime.now()
Похож:
$ ./manage.py schemamigration myapp --auto
? The field 'User.created_date' does not have a default specified, yet is NOT NULL.
? Since you are adding this field, you MUST specify a default
? value to use for existing rows. Would you like to:
? 1. Quit now, and add a default to the field in models.py
? 2. Specify a one-off value to use for existing columns now
? Please select a choice: 2
? Please enter Python code for your one-off default value.
? The datetime module is available, so you can do e.g. datetime.date.today()
>>> datetime.datetime.now()
+ Added field created_date on myapp.User
update()
не будет вызыватьsave()
что означает, что он не может автоматически обновитьmodified
поле