简体   繁体   English

如何判断django用户是否曾经登录过?

[英]how to tell if a django user has EVER logged in?

We have a Django project where a User account is created for a person whenever she calls us on the phone. 我们有一个Django项目,无论何时有人打电话给我们,都会为该用户创建一个用户帐户。 The person may or may not, at a later point, use her account to log in to our website. 该人以后可能会也可能不会使用她的帐户登录我们的网站。

Question: How can I tell if a User has ever logged in to our website? 问题:如何判断用户是否曾经登录过我们的网站?

Clarification: I want to answer the question above for hundreds of existing users, not just for new users that are created from this point on. 澄清:我想为数百名现有用户回答以上问题,而不仅仅是针对从那时起创建的新用户。

Thought 1: Check User.last_login . 思想1:检查User.last_login Unfortunately Django initializes last_login to datetime.datetime.now() when the User is created, regardless of whether she ever logged in. 不幸的是,无论用户是否登录,Django在创建用户时都会将last_login初始化为datetime.datetime.now()

Thought 2: Check if User.last_login matches User.date_joined . 思考2​​:检查User.last_loginUser.date_joined匹配。 Unfortunately Django initializes both these fields to datetime.now() , and they can be a few microseconds apart: 不幸的是,Django 将这两个字段初始化为datetime.now() ,它们之间可能相差几微秒:

In [1]: u = User.objects.create(username="bla")

In [2]: u.date_joined - u.last_login
Out[2]: datetime.timedelta(0, 0, 23)

Current hack I am using: Assume the user has logged in at least once iff user.last_login - user.date_joined >= datetime.timedelta(seconds=1) . 我正在使用的当前黑客:假设该用户至少登录了一次iff user.last_login - user.date_joined >= datetime.timedelta(seconds=1)

Is there a better way to tell if a User has ever logged in? 有没有更好的方法来判断用户是否曾经登录过?

If newer versions of Django (> 1.8), you should use User.last_login. 如果是Django的较新版本(> 1.8),则应使用User.last_login。 This is what the documentation states: 这是文档指出的内容:

last_login 上次登录

A datetime of the user's last login. 用户上次登录的日期时间。

Changed in Django 1.8: This field will be null if the user has never logged in. Previously it was set to the current date/time by default. 在Django 1.8中更改:如果用户从未登录过,则此字段为null。以前,默认情况下将其设置为当前日期/时间。

I'm going to assume that you're right when you say that both the date_joined and last_login are set to the current date on creation, and that for some reason there is enough time elapsed between the two assignments to create a noticeable delta. 当您说date_joinedlast_login都设置为创建时的当前日期,并且由于某种原因,两次分配之间有足够的时间创建明显的增量时,我将假设您是对的。

If that's the case, then you could take @LAK's advice and create either a pre_save (and check if the instance's ID is 0) or post_save (and check the created parameter) and manually set the last_login field to either None or the same value as date_joined . 如果是这样,那么您可以采用@LAK的建议并创建pre_save (并检查实例的ID是否为0)或post_save (并检查created参数),然后手动将last_login字段设置为None或与date_joined From there all of your future data is preserved from the one-second fuzziness you seem to despise. 从那里开始,您所有未来的数据都会从您似乎不屑一秒的模糊性中保留下来。

As for your existing data, I think that you're stuck making the best guess with the data you've got. 至于您现有的数据,我认为您一直无法对已有的数据做出最好的猜测。 I don't see it as too risky to make the assumption that you're making. 我认为做出您的假设并不冒险。 If you wanted to clean it up to be the same as all new data, you could just update your database to set the last_login value. 如果您希望将其清除为与所有新数据相同,则只需更新数据库以设置last_login值即可。 If you're using South , this is easy enough to do with a data migration. 如果您使用的是South ,这很容易进行数据迁移。 Otherwise, you could just create a script to run after you deploy the new code changes. 否则,您可以只创建一个脚本,以在部署新代码更改后运行。

I honestly don't think that it's a problem that they're off by a little bit. 老实说,我认为这一点不是问题。 So long as you're making the assumption that it's most likely not humanly possible for a user to log in within the time frame that their account was created, there seems to be little reason to add a bunch of extra work to force it to be an exact match. 只要您假设用户在其帐户创建的时间范围内极有可能无法人工登录,那么似乎没有理由添加大量额外的工作来强迫用户登录完全匹配。

I would use an F object to compare last_login with date_joined . 我将使用F对象将last_logindate_joined进行比较。 If they're equal, then the user has never logged in (or at least never logged in after their initial signup, which is about the best you can determine) 如果它们相等,则用户从未登录过(或者至少在首次注册后从未登录过,这是您可以确定的最好水平)

from django.db.models import F

User.objects.filter(last_login=F('date_joined'))

UPDATE 更新

Well, it successfully worked in my environment, but I suppose it is possible the two could be off by a few microseconds or more. 那么,它曾成功地在我的环境,但我想这是可能的两个可以通过几个微秒以上关闭。 If that's the case, then I think your only recourse is to actually manually check each User . 如果是这样,那么我认为您唯一的选择是实际手动检查每个User You can do variations on the following if you need greater accuracy, but generally, I think a user who has never logged in after the day they initially created the count could be counted as "never logged in" for all intents and purposes -- either way, they've abandoned the account. 如果您需要更高的准确性,可以对以下内容进行更改,但是总的来说,我认为一个用户在最初创建计数之后就从未登录过,出于所有意图和目的,都可以算作“从未登录”。方式,他们放弃了该帐户。

if user.date_joined.date() == user.last_login.date():
    # do something

If you need greater accuracy: 如果需要更高的精度:

date_joined = datetime.combine(user.date_joined.date(), time(user.date_joined.hour, user.date_joined.minute))
last_login = datetime.combine(user.last_login.date(), time(user.last_login.hour, user.last_login.minute))
if date_joined == last_login:
    # do something

That essentially creates new datetime objects, removing the differences on seconds and microseconds. 这实际上创建了新的datetime对象,从而消除了以秒和微秒为单位的差异。 Surely a 1 minute accuracy level is good enough here. 当然,这里的1分钟准确度水平就足够了。

Here is a solution that can filter a QuerySet by users that either has or hasn't logged on the site. 这是一个解决方案,可以按已经或尚未登录站点的用户筛选QuerySet

The filter_user_has_used_site() and filter_user_hasnt_used_site() functions works on a QuerySet containing auth.User . filter_user_has_used_site()filter_user_hasnt_used_site()函数可在包含auth.UserQuerySet上使用。

The filter_resource_has_used_site() and filter_resource_hasnt_used_site() works on any queryset which model has a ForeignKey field named user to auth.User . filter_resource_has_used_site()filter_resource_hasnt_used_site()作用于该模型有任何查询集ForeignKey命名字段userauth.User

It only works for mysql but can easily be made to support other database engines. 它仅适用于mysql,但可以轻松地支持其他数据库引擎。

from datetime import timedelta
from django.db import connection

MYSQL = "ABS(TIMESTAMPDIFF(SECOND,auth_user.last_login,auth_user.date_joined))"


def filter_resource_has_used_site(qs):
    """
    Takes a QuerySet of resources and returns a QuerySet only containing
    resources that has logged in to site at least once.
    """
    vendor = connection.vendor
    if vendor == 'mysql':
        # Force the orm to join to auth_user.
        qs = qs.filter(user__username__isnull=False)
        where = "".join([MYSQL, '>1'])
        return qs.extra(where=[where])
    else:
        raise NotImplementedError('Vendor type {} not supported.'.format(
            vendor))


def filter_resource_hasnt_used_site(qs):
    """
    Takes a QuerySet of resources and returns a QuerySet only containing
    resources that never logged in to site.
    """
    vendor = connection.vendor
    if vendor == 'mysql':
        # Force the orm to join to auth_user.
        qs = qs.filter(user__username__isnull=False)
        where = "".join([MYSQL, '<2'])
        return qs.extra(where=[where])
    else:
        raise NotImplementedError('Vendor type {} not supported.'.format(
            vendor))


def filter_user_has_used_site(qs):
    """
    Takes a QuerySet of users and returns a QuerySet only containing
    users that has logged in to site at least once.
    """
    vendor = connection.vendor
    if vendor == 'mysql':
        where = "".join([MYSQL, '>1'])
        return qs.extra(where=[where])
    else:
        raise NotImplementedError('Vendor type {} not supported.'.format(
            vendor))


def filter_user_hasnt_used_site(qs):
    """
    Takes a QuerySet of users and returns a QuerySet only containing
    users that never logged in to site.
    """
    vendor = connection.vendor
    if vendor == 'mysql':
        where = "".join([MYSQL, '<2'])
        return qs.extra(where=[where])
    else:
        raise NotImplementedError('Vendor type {} not supported.'.format(
            vendor))


def user_has_used_site(user):
    """
    Returns if a auth.user has logged into the site.
    """
    return abs(user.last_login - user.date_joined) > timedelta(seconds=1)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM