livro - python with django pdf




Django auto_now e auto_now_add (8)

Isso é motivo de preocupação?

Não, o Django adiciona automaticamente para você enquanto salva os modelos, portanto, é esperado.

Pergunta secundária: na minha ferramenta de administração, esses dois campos não estão aparecendo. Isso é esperado?

Como esses campos são adicionados automaticamente, eles não são exibidos.

Para adicionar ao acima, como synack disse, tem havido um debate na lista de discussão django para remover isso, porque, ele não é bem projetado e é um hack

Escrever um save () personalizado em cada um dos meus modelos é muito mais doloroso do que usar o auto_now

Obviamente, você não precisa escrever para todos os modelos. Você pode escrevê-lo em um modelo e herdar outros dele.

Mas, como auto_add e auto_now_add estão lá, eu os usaria em vez de tentar escrever um método sozinho.

Para o Django 1.1.

Eu tenho isso no meu models.py:

class User(models.Model):
    created = models.DateTimeField(auto_now_add=True)
    modified = models.DateTimeField(auto_now=True)

Ao atualizar uma linha eu recebo:

[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)

A parte relevante do meu banco de dados é:

  `created` datetime NOT NULL,
  `modified` datetime NOT NULL,

Isso é motivo de preocupação?

Pergunta secundária: na minha ferramenta de administração, esses dois campos não estão aparecendo. Isso é esperado?


Acho que a solução mais fácil (e talvez mais elegante) aqui é aproveitar o fato de que você pode definir o default como callable. Então, para contornar o tratamento especial do admin de auto_now, você pode simplesmente declarar o campo da seguinte forma:

from django.utils import timezone
date_filed = models.DateField(default=timezone.now)

É importante que você não use timezone.now() pois o valor padrão não seria atualizado (isto é, o padrão é definido somente quando o código é carregado). Se você está fazendo muito isso, você pode criar um campo personalizado. No entanto, isso é muito seco já acho.


Bah ... Não há reputação suficiente para comentar ... Mas eu queria salientar que a opinião expressa na resposta aceita é um pouco desatualizada. De acordo com discussões mais recentes (django bugs #7634 e #12785 ), auto_now e auto_now_add não vão a lugar nenhum, e mesmo se você for à discussão original , você encontrará argumentos fortes contra o RY (como em DRY) em salvar personalizado métodos.

Uma melhor solução foi oferecida (tipos de campos personalizados), mas não ganhou impulso suficiente para chegar ao django. Você pode escrever seus próprios em três linhas (é a sugestão de 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)

Baseado no que eu li e na minha experiência com o Django até agora, o auto_now_add é um buggy. Eu concordo com o jthanism --- sobrepondo o método normal de salvar, está limpo e você sabe o que está acontecendo. Agora, para secar, crie um modelo abstrato chamado 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

E então, quando você quer um modelo que tenha esse comportamento de tempo, apenas subclasse:

MyNewTimeStampyModel(TimeStamped):
    field1 = ...

Se você quiser que os campos apareçam em admin, basta remover a opção editable=False


Falando sobre uma questão paralela: se você quiser ver esses campos no admin (você não poderá editá-la), você pode adicionar readonly_fields à sua classe admin.

class SomeAdmin(ModelAdmin):
    readonly_fields = ("created","modified",)

Bem, isso se aplica apenas às últimas versões do Django (eu acredito, 1.3 e acima)


Qualquer campo com o atributo auto_now também herdará editable=False e, portanto, não será exibido no painel de administração. Houve conversas no passado sobre como fazer com que os argumentos auto_now_add e auto_now_add fossem embora e, embora eles ainda existam, sinto que é melhor usar um método save() personalizado .

Portanto, para que isso funcione corretamente, recomendo não usar auto_now ou auto_now_add e, em vez disso, definir seu próprio método save() para garantir que o created seja atualizado apenas se o id não estiver definido (por exemplo, quando o item for criado) e atualizá-lo toda vez que o item for salvo.

Eu fiz exatamente a mesma coisa com outros projetos que escrevi usando o Django, e assim o seu save() ficaria assim:

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)

Espero que isto ajude!

Edite em resposta aos comentários:

A razão pela qual eu simplesmente mantenho a sobrecarga save() versus confiar nesses argumentos de campo é dupla:

  1. Os altos e baixos acima mencionados com sua confiabilidade. Esses argumentos são fortemente dependentes da maneira como cada tipo de banco de dados com o qual o Django sabe interagir trata um campo de data / hora, e parece quebrar e / ou mudar entre cada lançamento. (O que eu acredito é o ímpeto por trás do chamado para removê-los completamente).
  2. O fato de que eles só funcionam em DateField, DateTimeField e TimeField e, ao usar essa técnica, você pode preencher automaticamente qualquer tipo de campo sempre que um item é salvo.
  3. Use django.utils.timezone.now() vs. datetime.datetime.now() , porque ele retornará um objeto datetime.datetime com reconhecimento de TZ ou ingênuo, dependendo das settings.USE_TZ .

Para abordar o motivo pelo qual o OP viu o erro, não sei exatamente, mas parece que nem sequer está sendo preenchido, apesar de ter auto_now_add=True . Para mim, isso se destaca como um bug, e ressalta o item # 1 na minha pequena lista acima: auto_now e auto_now_add são escamosos na melhor das hipóteses.


Se você alterar sua classe de modelo assim:

class MyModel(models.Model):
    time = models.DateTimeField(auto_now_add=True)
    time.editable = True

Então este campo aparecerá na minha página de alteração de administrador


Você pode usar timezone.now() para criado e auto_now para modificado:

from django.utils import timezone
class User(models.Model):
    created = models.DateTimeField(default=timezone.now())
    modified = models.DateTimeField(auto_now=True)

Se você estiver usando uma chave primária personalizada em vez do auto- increment int padrão auto- increment int , auto_now_add resultará em um erro.

Aqui está o código do DateTimeField.pre_save padrão do Django com auto_now e 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)

Não tenho certeza de qual é o parâmetro add . Eu espero que seja alguma coisa como:

add = True if getattr(model_instance, 'id') else False

O novo registro não terá o getattr(model_instance, 'id') attr id , então getattr(model_instance, 'id') retornará False, o que levará a não definição de nenhum valor no campo.





django-admin