अस्थायी रूप से auto_now / auto_now_add अक्षम करें


104

मेरे पास एक मॉडल है:

class FooBar(models.Model):
    createtime = models.DateTimeField(auto_now_add=True)
    lastupdatetime = models.DateTimeField(auto_now=True)

मैं कुछ मॉडल उदाहरणों के लिए दो दिनांक फ़ील्ड को अधिलेखित करना चाहता हूं (डेटा का माइग्रेशन करते समय उपयोग किया जाता है)। वर्तमान समाधान इस तरह दिखता है:

for field in new_entry._meta.local_fields:
    if field.name == "lastupdatetime":
        field.auto_now = False
    elif field.name == "createtime":
        field.auto_now_add = False

new_entry.createtime = date
new_entry.lastupdatetime = date
new_entry.save()

for field in new_entry._meta.local_fields:
    if field.name == "lastupdatetime":
        field.auto_now = True
    elif field.name == "createtime":
        field.auto_now_add = True

क्या कोई बेहतर समाधान है?


new_entry.createtime.auto_now = गलत?
एकोनसु

3
+1 - यह वास्तव में परीक्षण के लिए अच्छा होगा
फ्रैंक हेनार्ड

@akonsu नहीं: 'datetime.datetime' ऑब्जेक्ट में कोई विशेषता 'auto_now' नहीं है
mlissner

2
यह इंगित करने योग्य है कि कुछ से अधिक कोर auto_now(_add)
देवता

new_entry._meta.get_field ('date_update') अधिक प्रत्यक्ष है
Sérgio

जवाबों:


99

मैंने हाल ही में अपने आवेदन का परीक्षण करते समय इस स्थिति का सामना किया है। मुझे समय-सीमा समाप्त होने के लिए "बल" की आवश्यकता थी। मेरे मामले में मैंने क्वेरी अपडेट का उपयोग करके चाल चली। ऐशे ही:

# my model
class FooBar(models.Model):
    title = models.CharField(max_length=255)
    updated_at = models.DateTimeField(auto_now=True, auto_now_add=True)



# my tests
foo = FooBar.objects.get(pk=1)

# force a timestamp
lastweek = datetime.datetime.now() - datetime.timedelta(days=7)
FooBar.objects.filter(pk=foo.pk).update(updated_at=lastweek)

# do the testing.

इस उत्तर के लिए धन्यवाद। यहाँ अपडेट के लिए डॉक्स हैं (): docs.djangoproject.com/en/dev/ref/models/querysets/…
guettli

1
वास्तव में, यह विधि बहुत अच्छी तरह से काम करती है अगर आपको डेटाबेस से टकराने का मन नहीं है। मैंने परीक्षणों के लिए भी इसका उपयोग किया।
अक्टूबर

3
Django प्रलेखन से: docs.djangoproject.com/en/1.9/topics/db/queries/… इस बात से अवगत रहें कि अद्यतन () विधि सीधे SQL कथन में परिवर्तित हो जाती है। यह प्रत्यक्ष अपडेट के लिए एक बल्क ऑपरेशन है। यह आपके मॉडल पर कोई भी सेव () तरीके नहीं चलाता है, या प्री_सैव या पोस्ट_सेवे सिग्नल्स (जो कॉल सेविंग ()) का परिणाम है, या ऑटो_वॉल्ड ऑप्शन का सम्मान करता है
NoamG

@NoamG मुझे लगता है कि यह एक दुर्लभ मामला है जहां यह update()व्यवहार ठीक वैसा ही है जैसा हमें चाहिए।
डेविड डी।

54

आप वास्तव में दूसरे तरीके से auto_now / auto_now_add को अक्षम नहीं कर सकते हैं। यदि आपको उन मूल्यों को बदलने के लिए लचीलेपन की आवश्यकता है, auto_now/ auto_now_addसबसे अच्छा विकल्प नहीं है। ऑब्जेक्ट को सहेजे जाने से ठीक पहले हेरफेर करने के लिए उपयोग करने defaultऔर / या ओवरराइड करने के लिए यह अधिक लचीला होता save()है।

defaultएक ओवरराइड save()विधि का उपयोग करना , अपनी समस्या को हल करने का एक तरीका यह होगा कि आप अपने मॉडल को इस तरह परिभाषित करें:

class FooBar(models.Model):
    createtime = models.DateTimeField(default=datetime.datetime.now)
    lastupdatetime = models.DateTimeField()

    def save(self, *args, **kwargs):
        if not kwargs.pop('skip_lastupdatetime', False):
            self.lastupdatetime = datetime.datetime.now()

        super(FooBar, self).save(*args, **kwargs)

अपने कोड में, जहाँ आप स्वचालित अंतिम अद्यतन परिवर्तन को छोड़ना चाहते हैं, बस उपयोग करें

new_entry.save(skip_lastupdatetime=True)

यदि आपकी वस्तु को व्यवस्थापक इंटरफ़ेस या अन्य स्थानों में सहेजा गया है, तो स्किप () को Skip_lastupdatetime तर्क के बिना बुलाया जाएगा, और यह वैसा ही व्यवहार करेगा जैसा उसने पहले किया था auto_now


14
TL; DR बजाय उपयोग का auto_now_addउपयोग न करें default
ठाणे ब्रिम्हल

9
इस उदाहरण के लिए एक चेतावनी यह है कि datetime.datetime.nowएक भोली डेटाइम लौटाता है। एक समयक्षेत्र-जागरूक डेटाटाइम का उपयोग करने के लिए, docs.djangoproject.com/en/1.9/topics/i18n/timezones/… का उपयोग करें from django.utils import timezoneऔर models.DateTimeField(default=timezone.now)देखें
बेंजामिनबोर्ड

तो, बस स्पष्ट होना चाहिए: यदि आप केवल createtimeक्षेत्र को संशोधित करने में सक्षम होना चाहते हैं , तो आपको ओवरराइड करने की आवश्यकता नहीं है save()। यह auto_now_add=Trueसमकक्ष default=timezone.now, editable=False, blank=True( डॉक्स के अनुसार ) द्वारा प्रतिस्थापित करने के लिए पर्याप्त है । बाद के दो विकल्प व्यवस्थापक में समान व्यवहार सुनिश्चित करते हैं।
djvg

28

मैंने प्रश्नकर्ता द्वारा किए गए सुझाव का उपयोग किया, और कुछ फ़ंक्शन बनाए। यहाँ उपयोग मामला है:

turn_off_auto_now(FooBar, "lastupdatetime")
turn_off_auto_now_add(FooBar, "createtime")

new_entry.createtime = date
new_entry.lastupdatetime = date
new_entry.save()

यहाँ कार्यान्वयन है:

def turn_off_auto_now(ModelClass, field_name):
    def auto_now_off(field):
        field.auto_now = False
    do_to_model(ModelClass, field_name, auto_now_off)

def turn_off_auto_now_add(ModelClass, field_name):
    def auto_now_add_off(field):
        field.auto_now_add = False
    do_to_model(ModelClass, field_name, auto_now_add_off)

def do_to_model(ModelClass, field_name, func):
    field = ModelClass._meta.get_field_by_name(field_name)[0]
    func(field)

उन्हें वापस चालू करने के लिए समान कार्य बनाए जा सकते हैं।


9
पुनरावृति के बजाय ज्यादातर मामलों में आप शायद ऐसा कर सकते हैं Clazz._meta.get_field_by_name(field_name)[0]
नक्तिनियां

साभार @naktinis बदला हुआ।
फ्रैंक हेनार्ड

4
nb (1.10) ModelClass._meta.get_field_by_name (field_name) [0] do_to_model में मेरे लिए काम नहीं कर रहा है - परिवर्तित: ModelClass._meta .get_field (field_name)
Georgina S

27

आप अपने क्षेत्र के update_fieldsपैरामीटर का भी उपयोग कर सकते हैं । यहाँ एक उदाहरण है:save()auto_now

# Date you want to force
new_created_date = date(year=2019, month=1, day=1)
# The `created` field is `auto_now` in your model
instance.created = new_created_date
instance.save(update_fields=['created'])

यहाँ Django के दस्तावेज से स्पष्टीकरण दिया गया है: https://docs.djangoproject.com/en/stable/ref/models/instances/#specifying-which-fields-to-save


1
काश मैं इस उच्च वोट सकता है! यह वास्तव में वही है जो मैं चाहता था ('ऑटो' फ़ील्ड्स को छूने के बिना मॉडल के कुछ हिस्सों को बदलने के लिए), लेकिन दुर्भाग्य से दिए गए प्रश्न का उत्तर नहीं देता है (जो कि उपयोग करने वाले फ़ील्ड में स्पष्ट मानों को सहेजना है ) auto_nowऔर auto_now_add
टिम टिस्डल

1
सबसे अच्छा जवाब, मेरी इच्छा है कि मैं इसे 10 वोट दे सकता हूं, बहुत ही सरल और सुरुचिपूर्ण
बेड्रोस

18

मैं पुन: प्रयोज्यता के लिए संदर्भ प्रबंधक तरीका गया।

@contextlib.contextmanager
def suppress_autotime(model, fields):
    _original_values = {}
    for field in model._meta.local_fields:
        if field.name in fields:
            _original_values[field.name] = {
                'auto_now': field.auto_now,
                'auto_now_add': field.auto_now_add,
            }
            field.auto_now = False
            field.auto_now_add = False
    try:
        yield
    finally:
        for field in model._meta.local_fields:
            if field.name in fields:
                field.auto_now = _original_values[field.name]['auto_now']
                field.auto_now_add = _original_values[field.name]['auto_now_add']

ऐसे उपयोग करें:

with suppress_autotime(my_object, ['updated']):
    my_object.some_field = some_value
    my_object.save()

बूम।


8

इसे देखने के लिए जब वे परीक्षण लिख रहे हैं, तो फ्रीजगंज नामक एक अजगर पुस्तकालय है जो आपको समय नकली करने की अनुमति देता है - इसलिए जब auto_now_addकोड चलता है, तो यह वह समय प्राप्त करता है जिसे आप वास्तव में चाहते हैं। इसलिए:

from datetime import datetime, timedelta
from freezegun import freeze_time

with freeze_time('2016-10-10'):
    new_entry = FooBar.objects.create(...)
with freeze_time('2016-10-17'):
    # use new_entry as you wish, as though it was created 7 days ago

इसे डेकोरेटर के रूप में भी इस्तेमाल किया जा सकता है - मूल डॉक्स के लिए ऊपर दिए गए लिंक को देखें।


4

आप auto_now_addविशेष कोड के बिना ओवरराइड कर सकते हैं ।

मैं इस सवाल पर आया जब मैंने किसी विशेष तिथि के साथ एक वस्तु बनाने की कोशिश की:

Post.objects.create(publication_date=date, ...)

जहां publication_date = models.DateField(auto_now_add=True)

तो यह वही है जो मैंने किया है:

post = Post.objects.create(...)
post.publication_date = date
post.save()

यह सफलतापूर्वक ओवरराइड हो गया है auto_now_add

अधिक दीर्घकालिक समाधान के रूप में, ओवरराइडिंग saveविधि जाने का रास्ता है: https://code.djangoproject.com/ticket/1st83


2

मुझे माइग्रेशन के दौरान एक डेटटाइम फ़ील्ड के लिए auto_now को अक्षम करने की आवश्यकता थी और ऐसा करने में सक्षम था।

events = Events.objects.all()
for event in events:
    for field in event._meta.fields:
        if field.name == 'created_date':
            field.auto_now = False
    event.save()

1

मुझे पार्टी के लिए देर हो रही है, लेकिन कई अन्य उत्तरों के समान है, यह एक समाधान है जो मैंने डेटाबेस प्रवास के दौरान उपयोग किया था। अन्य उत्तरों से अंतर यह है कि यह मॉडल के लिए सभी auto_now फ़ील्ड को इस धारणा के तहत निष्क्रिय कर देता है कि वास्तव में इस तरह के एक से अधिक फ़ील्ड होने का कोई कारण नहीं है।

def disable_auto_now_fields(*models):
    """Turns off the auto_now and auto_now_add attributes on a Model's fields,
    so that an instance of the Model can be saved with a custom value.
    """
    for model in models:
        for field in model._meta.local_fields:
            if hasattr(field, 'auto_now'):
                field.auto_now = False
            if hasattr(field, 'auto_now_add'):
                field.auto_now_add = False

फिर इसका उपयोग करने के लिए, आप बस कर सकते हैं:

disable_auto_now_fields(Document, Event, ...)

और यह आपके द्वारा auto_nowऔर auto_now_addआपके द्वारा पास किए गए सभी मॉडल वर्गों के लिए खेतों के माध्यम से जाएगा ।


1

Https://stackoverflow.com/a/35943149/1731460 से संदर्भ प्रबंधक का थोड़ा और साफ संस्करण

from contextlib import contextmanager

@contextmanager
def suppress_auto_now(model, field_names):
    """
    idea taken here https://stackoverflow.com/a/35943149/1731460
    """
    fields_state = {}
    for field_name in field_names:
        field = model._meta.get_field(field_name)
        fields_state[field] = {'auto_now': field.auto_now, 'auto_now_add': field.auto_now_add}

    for field in fields_state:
        field.auto_now = False
        field.auto_now_add = False
    try:
        yield
    finally:
        for field, state in fields_state.items():
            field.auto_now = state['auto_now']
            field.auto_now_add = state['auto_now_add']

आप इसे कारखानों (कारखाने-लड़के) के साथ भी उपयोग कर सकते हैं

        with suppress_autotime(Click, ['created']):
            ClickFactory.bulk_create(post=obj.post, link=obj.link, created__iter=created)

0

Django की कॉपी - Model.DateTimeField - गतिशील रूप से auto_now_add मान बदलना

खैर, मैंने इस दोपहर को यह पता लगाने में बिताया कि पहली समस्या यह है कि कैसे मॉडल ऑब्जेक्ट लाया जाता है और कोड में कहां है। मैं serializer.py में restframework हूं, उदाहरण के लिए __init__serializer में यह अभी तक मॉडल नहीं हो सकता है। अब in_internal_value में आप फ़ील्ड प्राप्त कर सकते हैं, फ़ील्ड प्राप्त कर सकते हैं और इस उदाहरण में फ़ील्ड गुण संशोधित करने के बाद:

class ProblemSerializer(serializers.ModelSerializer):

    def to_internal_value(self, data): 
        ModelClass = self.Meta.model
        dfil = ModelClass._meta.get_field('date_update')
        dfil.auto_now = False
        dfil.editable = True

0

मुझे ऐसे समाधान की आवश्यकता थी जो update_or_createमेरे साथ काम करे , मैं इस समाधान पर @andreaspelme कोड के आधार पर आया हूं।

केवल परिवर्तन यह है कि आप संशोधित फ़ील्ड को सेट करके स्किपिंग सेट कर सकते हैं, skipन कि वास्तव में skip_modified_updateसहेजने के लिए () विधि द्वारा क्वार्ग पास करके ।

बस yourmodelobject.modified='skip'और अद्यतन छोड़ दिया जाएगा!

from django.db import models
from django.utils import timezone


class TimeTrackableAbstractModel(models.Model):
    created = models.DateTimeField(default=timezone.now, db_index=True)
    modified = models.DateTimeField(default=timezone.now, db_index=True)

    class Meta:
        abstract = True

    def save(self, *args, **kwargs):
        skip_modified_update = kwargs.pop('skip_modified_update', False)
        if skip_modified_update or self.modified == 'skip':
            self.modified = models.F('modified')
        else:
            self.modified = timezone.now()
        super(TimeTrackableAbstractModel, self).save(*args, **kwargs)
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.