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

时间:2016-07-02 05:07:02

标签: django

我为登录系统创建了自定义身份验证后端。当然,当我在python shell中尝试它时,自定义后端工作。但是,当我在服务器中运行它时出错。错误显示“此模型中不存在以下字段,或者是m2m字段:last_login”。我是否需要在客户模型中包含last_login字段,或者是否有其他解决方案来解决问题? 这是我的示例代码:

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

在backends.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

在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))

在setting.py

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

2 个答案:

答案 0 :(得分:2)

这是因为你使用django的login()函数来记录用户。

Django的login函数使用您提供的user_logged_in实例作为参数发出名为user的信号。 See login() source

这个信号在django的contrib.auth.models中被收听。它尝试更新名为last_login的字段,假设您提供的用户实例是django的默认AbstractUser模型的子类。

为了解决这个问题,您可以执行以下操作之一:

  1. 停止使用django附带的login()功能并创建自定义功能。
  2. 断开user_logged_in接收器的update_last_login信号。 Read how
  3. 将名为last_login的字段添加到您的模型
  4. 从django的基础身份验证模型扩展您的模型。 Read how

答案 1 :(得分:0)

谢谢,我定义了一个自定义login方法,以在自动测试中解决此问题,默认情况下,我不会关闭信号。

这是一个工作代码示例。

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)

依次将其用于测试中,如下所示:

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(...)
        ...

希望有帮助。