5

I create a custom Authentication backends for my login system. Surely, the custom backends works when I try it in 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". Do I need include the last_login field in customer model or Is there any other solution to solve the problem? 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

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

@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

AUTHENTICATION_BACKENDS = ('dataInfo.backends.CustomerAuthBackend', 'django.contrib.auth.backends.ModelBackend',)
1
  • How did you manage to solve it? Can you add your own answer? Also when I specify AUTH_USER_MODEL in the settings it fails. Did you omit it? Also is it necessary to makemigrations? Commented Jun 18, 2020 at 9:57

2 Answers 2

8

This is happening because you are using django's login() function to log the user in.

Django's login function emits a signal named user_logged_in with the user instance you supplied as argument. See login() source.

And this signal is listened in django's 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.

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.
  2. Disconnect the user_logged_in signal from update_last_login receiver. Read how.
  3. Add a field named last_login to your model
  4. Extend your model from django's base auth models. Read how
Sign up to request clarification or add additional context in comments.

3 Comments

I add a last_login field in the Customer model, but the same error still exists.Do I do need to modify somewhere else?
Are you sure you've added it as a datetime field? Have you migrated your database?
Yes, I did. I realized that the field name must be exactly as "last_login", but I named it as private variable(_last_login) and this may be the reason to cause the issue.
2

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.

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.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.