简体   繁体   English

关于 Django 自定义身份验证和登录的错误?

[英]Error about Django custom authentication and login?

I create a custom Authentication backends for my login system.我为我的登录系统创建了一个自定义身份验证后端。 Surely, the custom backends works when I try it in python shell.当然,当我在 python shell 中尝试时,自定义后端可以工作。 However, I got error when I run it in the server.但是,当我在服务器中运行它时出现错误。 The error says "The following fields do not exist in this model or are m2m fields: last_login".错误提示“此模型中不存在以下字段或为 m2m 字段:last_login”。 Do I need include the last_login field in customer model or Is there any other solution to solve the problem?我需要在客户模型中包含 last_login 字段还是有其他解决方案来解决这个问题? Here is my sample code:这是我的示例代码:

In my models.py

class Customer(models.Model):

  yes_or_no = ((True, 'Yes'),(False, 'No'))
  male_or_female = ((True,'Male'),(False,'Female'))

  name = models.CharField(max_length=100)
  email = models.EmailField(max_length=100,blank = False, null = False)
  password = models.CharField(max_length=100)
  gender = models.BooleanField(default = True, choices = male_or_female)
  birthday = models.DateField(default =None,blank = False, null = False)
  created = models.DateTimeField(default=datetime.now, blank=True)
  _is_active = models.BooleanField(default = False,db_column="is_active")

  @property
  def is_active(self):
    return self._is_active
  # how to call setter method, how to pass value ?
  @is_active.setter
  def is_active(self,value):
    self._is_active = value

  def __str__(self):
    return self.name

In backends.py在后端.py

from .models import Customer
from django.conf import settings

class CustomerAuthBackend(object):

    def authenticate(self, name=None, password=None):
        try:
            user = Customer.objects.get(name=name)

            if password == getattr(user,'password'):
                # Authentication success by returning the user
                user.is_active = True
                return user
            else:
                # Authentication fails if None is returned
                return None
        except Customer.DoesNotExist:
            return None

    def get_user(self, user_id):
        try:
            return Customer.objects.get(pk=user_id)
        except Customer.DoesNotExist:
            return None

In views.py在views.py中

@login_required(login_url='/dataInfo/login/')
def login_view(request):
    if request.method == 'POST':

        username = request.POST['username']
        password = request.POST['password']
        user = authenticate(name=username,password=password)
        if user is not None:

            if user.is_active:
                login(request,user)
                #redirect to user profile
                print "suffcessful login!"
                return HttpResponseRedirect('/dataInfo/userprofile')
            else:
                # return a disable account
                return HttpResponse("User acount or password is incorrect")
        else:
            # Return an 'invalid login' error message.
            print "Invalid login details: {0}, {1}".format(username, password)
            # redirect to login page
            return HttpResponseRedirect('/dataInfo/login')
    else:

        login_form = LoginForm()
    return render_to_response('dataInfo/login.html', {'form': login_form}, context_instance=RequestContext(request))

In setting.py在设置.py

AUTHENTICATION_BACKENDS = ('dataInfo.backends.CustomerAuthBackend', 'django.contrib.auth.backends.ModelBackend',)

This is happening because you are using django's login() function to log the user in.发生这种情况是因为您正在使用 django 的login()函数来登录用户。

Django's login function emits a signal named user_logged_in with the user instance you supplied as argument. Django 的login函数发出一个名为user_logged_in的信号,其中包含您提供的user实例作为参数。 See login() source . 请参阅login()

And this signal is listened in django's contrib.auth.models .这个信号在 django 的contrib.auth.models被监听。 It tries to update a field named last_login assuming that the user instance you have supplied is a subclass of django's default AbstractUser model.它尝试更新名为last_login的字段,假设您提供的用户实例是 django 默认AbstractUser模型的子类。

In order to fix this, you can do one of the following things:为了解决这个问题,您可以执行以下操作之一:

  1. Stop using the login() function shipped with django and create a custom one.停止使用 django 附带的login()函数并创建一个自定义函数。
  2. Disconnect the user_logged_in signal from update_last_login receiver.断开user_logged_in信号与update_last_login接收器的连接。 Read how . 阅读如何
  3. Add a field named last_login to your model将名为last_login的字段添加到您的模型中
  4. Extend your model from django's base auth models.从 django 的基本身份验证模型扩展您的模型。 Read how 阅读方法

Thanks, I defined a custom login method as follows to get through this issue in my automated tests in which I by default keep the signals off.谢谢,我定义了一个自定义login方法,如下所示,以在我的自动测试中解决这个问题,我默认情况下保持信号关闭。

Here's a working code example.这是一个工作代码示例。

def login(client: Client, user: User) -> None:
    """
    Disconnect the update_last_login signal and force_login as `user`
    Ref: https://stackoverflow.com/questions/38156681/error-about-django-custom-authentication-and-login
    Args:
        client: Django Test client instance to be used to login
        user: User object to be used to login
    """
    user_logged_in.disconnect(receiver=update_last_login)
    client.force_login(user=user)
    user_logged_in.connect(receiver=update_last_login)

This in turn is used in tests as follows:这反过来又用于测试,如下所示:

class TestSomething(TestCase):
    """
    Scenarios to validate:
    ....
    """

    @classmethod
    @factory.django.mute_signals(signals.pre_save, signals.post_save)
    def setUpTestData(cls):
        """
        Helps keep tests execution time under control
        """

        cls.client = Client()
        cls.content_type = 'application/json'

    def test_a_scenario(self):
        """
        Scenario details...
        """
        login(client=self.client, user=<User object>)
        response = self.client.post(...)
        ...

Hope it helps.希望它有帮助。

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

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