Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
355 views
in Technique[技术] by (71.8m points)

python - 如何避免`obj.save()`不更新`updated_at`字段?(How do I avoid `obj.save()` not to update `updated_at` field?)

The title alone seems to be vague.

(仅标题似乎是模糊的。)

Here is the detail.

(这是细节。)

I have the following Model:

(我有以下模型:)

class Post(models.Model):
    title = models.CharField(max_length=100)
    # deleted for brevity
    created_at = models.DateTimeField(blank=True, null=True, auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)
    count_visits = models.PositiveIntegerField(default=1)  # counts visit time of posts

and added the following code in blogs.html :

(并在blogs.html添加了以下代码:)

{% if post.created_at == post.updated_at %} 
    Posted {{ post.created_at|naturaltime }} 
{% else %} 
    Updated {{ post.updated_at|naturaltime }} 
{% endif %}

then I made a simple if statement in DetailView to keep track of number of visitors of a certain post:

(然后我在DetailView做了一个简单的if statement来跟踪某个帖子的访问者数量:)

def post_detail(request, title_slug):
    obj = get_object_or_404(Post, slug=title_slug)
    session_key = f'key_{title_slug}'

    if not request.session.get(session_key, False):
        obj.count_visits += 1
        obj.save()
        request.session[session_key] = True

    return render(request, 'blogApp/detail.html', {'post': obj})

Here is the problem, When I create a post, in list view it shows Posted now .

(这是问题所在,当我创建帖子时,在列表视图中显示“立即Posted now 。)

在此处输入图片说明

but as I click the post to view its detail.

(但是当我单击该帖子以查看其详细信息时。)

It shows Updated now

(它显示立即Updated now)

在此处输入图片说明

and I think I know what is the problem, when I go to detail view.

(当我进入详细视图时,我想我知道出了什么问题。)

it creates a session_key , increments obj.count_visits += 1 and save the object obj.save() .

(它创建一个session_key ,使obj.count_visits += 1递增并保存对象obj.save() 。)

When obj.save() is called it updates the database including updated_at field and that is why I get Updated now .

(调用obj.save() ,它将更新数据库,其中包括updated_at字段,这就是为什么我Updated nowUpdated now 。)

How do I solve this?

(我该如何解决?)

I want to increment number of count_views field, but when object is saved it shouldn't change updated_at field.

(我想增加count_views字段的数量,但是保存对象时不应更改updated_at字段。)

I hope you help me.

(我希望你能帮助我。)

Thank You

(谢谢)

  ask by Basir Payenda translate from so

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

You can do the update directly on the database, bypassing the Django layer:

(您可以绕过Django层直接在数据库上进行更新:)

from django.db.models import F

if not request.session.get(session_key, False):
    type(obj).objects.filter(pk=obj.pk).update(
        count_visits=F('count_visits') + 1
)

This will result in the following DB query:

(这将导致以下数据库查询:)

UPDATE "<db_table>" SET "count_visits" = ("<db_table>"."count_visits" + 1) 
    WHERE "<db_table>"."id" = <obj.id>

The F -object is used to resolve references to existing database objects, in this case, we're getting the current value of field count_visits .

(F对象用于解析对现有数据库对象的引用,在这种情况下,我们获取字段count_visits的当前值。)

The important property of this is that it can generate expression from the operation and operands (objects) by implementing eg __add__ , __sub__ , __mul__ , __div__ and other dunder methods (actually implemented by its superclass Combinable ).

(这样做的重要特性是,它可以产生通过实现例如从操作和操作数(对象)表达__add____sub____mul____div__等dunder方法(实际上是由它的父类实现Combinable )。)


Another approach would be to take an additional keyword argument to save that indicates whether the updated_at field should be updated as well.

(另一种方法是采用附加的关键字参数来save ,该参数指示是否也应更新updated_at字段。)

To implement this, at first we need to drop the auto_now argument from the field instantiation as well:

(为此,首先我们还需要从字段实例中删除auto_now参数:)

from django.utils import timezone

class Post(models.Model):    
    ...
    updated_at = models.DateTimeField(default=timezone.now)
    ...

    def save(self, *args, set_updated_at=True, **kwargs):
        if self.pk is not None:
            if set_updated_at:
                self.updated_at = timezone.now()

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

Now you only need to pass the argument when calling save eg in this case, it should be False :

(现在,您只需要在调用save时传递参数,例如,在这种情况下,它应该为False :)

if not request.session.get(session_key, False):
    obj.count_visits += 1
    obj.save(set_updated_at=False)

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...