অস্থায়ীভাবে অটো_নো / অটো_নো_এডডি অক্ষম করুন


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_ હવે = মিথ্যা?
আকোনসু

3
+1 - এটি পরীক্ষার জন্য সত্যিই দুর্দান্ত লাগবে
ফ্রাঙ্ক হেনার্ড

@ অ্যাকোনসু নোপ: 'ডেটটাইম.ডেটটাইম' অবজেক্টটির কোনও 'অটো_নো' নেই
mlissner

2
এটি উল্লেখ করার মতো বিষয় যে কয়েকটি কোর auto_now(_add)
ডিভ

new_entry._meta.get_field ('তারিখ_আপডেট') আরও সরাসরি
সেরজিও

উত্তর:


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
আসলে, আপনি যদি ডাটাবেসটিকে আঘাত করতে আপত্তি না করেন তবে এই পদ্ধতিটি বেশ ভালভাবে কাজ করে। আমি পরীক্ষার জন্যও এটি ব্যবহার করে শেষ করেছি।
imjustmatthew

3
জাজানো ডকুমেন্টেশন থেকে: docs.djangoproject.com/en/1.9/topics/db/queries/… সচেতন হন আপডেট () পদ্ধতিটি সরাসরি এসকিউএল স্টেটমেন্টে রূপান্তরিত হয়। এটি সরাসরি আপডেটের জন্য একটি বাল্ক অপারেশন। এটি আপনার মডেলগুলিতে কোনও সেভ () পদ্ধতি চালায় না, বা প্রি-সেভ বা পোস্ট_সেভ সিগন্যালগুলি প্রেরণ করে (যা সেভ () সংরক্ষণের কলিংয়ের ফলাফল) বা অটো_নো ক্ষেত্র বিকল্পটিকে সম্মান দেয়
NoamG

@ নোমজি আমি মনে করি এটি একটি বিরল ঘটনা যেখানে আমাদের update()আচরণের প্রয়োজনটি ঠিক এমনই।
ডেভিড ডি

54

আপনি ইতিমধ্যে নিজের চেয়ে অন্য কোনও উপায়ে স্বয়ংক্রিয়ভাবে অটো_নো / অটো_নো_আডি অক্ষম করতে পারবেন না। আপনার যদি সেই মানগুলি পরিবর্তন করতে নমনীয়তা প্রয়োজন তবে 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
টি এল; ডিআর কি ব্যবহার করবেন auto_now_addব্যবহারের defaultপরিবর্তে।
থানা ব্রিমহল

9
এই উদাহরণের মধ্যে datetime.datetime.nowএকটি সতর্কতা হ'ল একটি নিষ্পাপ ডেটটাইম ফেরত। একটি সময় অঞ্চল-সচেতন DATETIME, ব্যবহার ব্যবহার করার জন্য from django.utils import timezoneএবং models.DateTimeField(default=timezone.now)দেখতে docs.djangoproject.com/en/1.9/topics/i18n/timezones/...
BenjaminGolder

সুতরাং, কেবল পরিষ্কার করার জন্য: আপনি যদি কেবলমাত্র 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]
নাকটিনিস

ধন্যবাদ @ নাকটিনিস পরিবর্তন হয়েছে।
ফ্র্যাঙ্ক হেনার্ড

4
nb (1.10) ModelClass._meta.get_field_by_name (মাঠের নাম) [0] মনে হয় না do_to_model আমার জন্য কাজ করছে - পরিবর্তিত হয়েছে: ModelClass._meta.get_field (ক্ষেত্রের নাম)
জর্জিনা এস

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'])

জ্যাঙ্গোর ডকুমেন্টেশন থেকে ব্যাখ্যা এখানে দেওয়া হয়েছে: https://docs.djangoproject.com/en/stable/ref/models/inferences/#specifying-Wich-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/16583


2

স্থানান্তরের সময় আমার একটি ডেটটাইম ক্ষেত্রের জন্য অটো_নো অক্ষম করা দরকার ছিল এবং এটি করতে সক্ষম হয়েছি was

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

আমি পার্টিতে দেরি করেছি, তবে অন্যান্য উত্তরগুলির মতো বেশ কয়েকটি, এটি একটি সমাধান যা আমি একটি ডাটাবেস স্থানান্তরের সময় ব্যবহার করেছি। অন্যান্য উত্তরের থেকে পার্থক্য হ'ল এই ধারণাটি অধীনে মডেলটির জন্য সমস্ত অটো_নো ক্ষেত্র অক্ষম করে যে এই জাতীয় একের বেশি ক্ষেত্র থাকার কোনও কারণ নেই।

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

জ্যাঙ্গোর অনুলিপি - মডেলস.ডেটটাইমফিল্ড - গতিবেগের সাথে অটো_আপনি_এডিড মান পরিবর্তন করা হচ্ছে

ঠিক আছে, আমি আজ বিকেলে এটি বের করে কাটিয়েছি এবং প্রথম সমস্যাটি হল মডেলটি কীভাবে আনতে হবে এবং কোডটি কোথায়। আমি সিরিয়ালাইজার.পি-তে পুনরায় ফ্রেমওয়ার্কে আছি, উদাহরণস্বরূপ __init__সিরিয়ালে এটির মডেলটি এখনও থাকতে পারে না। এখন to_intern_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)
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.