I'm writing a simple (for now) Django app. I'm having trouble with authentication: I'm trying to use all of the out-of-the-box components, and both in the app itself and in tests, I can't authenticate users. So, for example, here's a test that fails:
from django.conf import settings
from django.contrib.auth import authenticate
from django.contrib.auth.models import User
from django.test import TestCase
[...]
class UserTestCase(TestCase):
def setUp(self):
self.testu = User(username="thename", password="thepassword", first_name="thefirstname")
self.testu.save()
def testAuthenticate(self):
u = authenticate(username="thename", password="thepassword")
self.assertEqual(u.first_name, "thefirstname")
I get an AttributeError:
'NoneType' object has no attribute "first_name".
I think this is because authenticate() is returning None (representing that there is no such user).
This fails whether or not I include the line "self.testu.save()".
I have other tests that pass, so I don't think the problem is with the test infrastructure. I can successfully create users and retrieve their information from the database.
The only mention of User in models.py is:
from django.contrib.auth.models import User
I've read through a lot of documentation but can't figure out what's going on. Can anyone help? Thanks in advance.
You can not create a User object with a password like that. The password needs to be hashed. Therefore, you should use the .set_password(..) method [Django-doc]:
class UserTestCase(TestCase):
def setUp(self):
self.testu = User(username="thename", first_name="thefirstname")
self.testu.set_password("thepassword")
self.testu.save()
# …
Related
I have a django server with an admin panel.
Different users make changes there and this is saved via auditlog in the database and displayed in the "history".
But there are situations when a user enters under the account of another user and makes changes on his behalf.
In order to identify from which device this or that change was made, it was a nice decision to also record data about the IP of the user from whom the change was made, and his unique device number.
By overloading several methods in the "AuditlogMiddleware" class, I got the desired result via "uuid.UUID(int=uuid.getnode())".
(Tested locally, because the prod server is heavily loaded and there is no way to do test committees)
from __future__ import unicode_literals
import threading
import time
from auditlog.middleware import AuditlogMiddleware
threadlocal = threading.local()
class ExtendedAuditlogMiddleware(AuditlogMiddleware):
def process_request(self, request):
threadlocal.auditlog = {
'signal_duid': (self.__class__, time.time()),
'remote_addr': request.META.get('REMOTE_ADDR'),
}
super(ExtendedAuditlogMiddleware, self).process_request(request)
**#changes here
import uuid
threadlocal.auditlog['additional_data'] = str(uuid.UUID(int=uuid.getnode()))+" | "+request.META["USERNAME"]**
# #staticmethod
def set_actor(self, user, sender, instance, signal_duid, **kwargs):
super(ExtendedAuditlogMiddleware, self).set_actor(user, sender, instance, signal_duid, **kwargs)
**#changes here
instance.additional_data = threadlocal.auditlog['additional_data']**
But the problem is that I think I get the UUID not of the user, but of the server, because there is no access to the user, i guess. I couldn't find the information, and I couldn't come up with my own solution either.
Question - is it even possible to get information from the server about django admin users' devices??
If not, what can i use instead of UUID to identify which device was used while making changes in django admin panel??
Thank you all in advance!
try to use javascript in side of your template to send this info thrugh
How to add the last_login_ip, when the user login if using rest-auth?
Befor ask the post I searched one post bellow:
How can I do my logic in `http://127.0.0.1:8000/rest-auth/login/` of `django-rest-auth`?
The answer is perfect for custom verify the login params.
But, how about after login, then add attribute to the User?
I mean, the answer is just for verify the data, I want to after user login success, I want to add the last_login_ip to the user instance.
EDIT
Please be attention, I know how to get the remote IP who visit my site. I mean I use the rest-auth to login, and customized LoginSerializer like the link. The link is for verify the login data, I can not to change the last_login_ip data in the LoginSerializer, I want when the user login success, then change the user's last_login_ip. but where to write my change last_login_ip code?
EDIT-2
I follow the #at14's advice, in the init.py (as the same level as apps.py):
default_app_config = '管理员后台.用户管理.qiyun_admin_usermanage.apps.QiyunAdminUsermanageConfig'
and in the apps.py:
from django.apps import AppConfig
class QiyunAdminUsermanageConfig(AppConfig):
name = 'qiyun_admin_usermanage'
def ready(self):
import 管理员后台.用户管理.qiyun_admin_usermanage.api.signals
There will get bellow error:
django.core.exceptions.ImproperlyConfigured: Cannot import 'qiyun_admin_usermanage'. Check that '管理员后台.用户管理.qiyun_admin_usermanage.apps.QiyunAdminUsermanageConfig.name' is correct.
and I also tried to comment the default_app_config = '管理员后台.用户管理.qiyun_admin_usermanage.apps.QiyunAdminUsermanageConfig' in the init.py, but still not work.
Use django's user logged in signal to save this information into the database.
In signals.py,
from django.contrib.auth.signals import user_logged_in
from django.dispatch import receiver
#receiver(user_logged_in)
def user_logged_in_callback(sender, user, request, **kwargs):
// Get the IP Address and save it
Read more about signals here.
Do pay attention on how to import signals in your app ready method
Make an apps.py (it might already exist) in your app directory,
from django.apps import AppConfig
class YourAppConfig(AppConfig):
name = '管理员后台.用户管理.qiyun_admin_usermanage'
def ready(self):
import 管理员后台.用户管理.qiyun_admin_usermanage.api.signals # noqa
In your apps init.py file,
default_app_config = '管理员后台.用户管理.qiyun_admin_usermanage.apps.QiyunAdminUsermanageConfig'
I am (trying) to use Django Rest Framework's Token-based authentication. I have the following in my app's models.py
from django.db.models.signals import post_save
from django.dispatch import receiver
from rest_framework.authtoken.models import Token
from django.conf import settings
#receiver(post_save, sender=settings.AUTH_USER_MODEL)
def create_auth_token(sender, instance=None, created=False, **kwargs):
if created:
Token.objects.create(user=instance)
Whenever I create a new account (either through 'createsuperuser' or by using my registration form), the user is correctly added to the 'auth_user' table. However, although the 'authtoken_token' table is created, there is nothing added to it. This leads me to believe that my #reciever may not be working properly.
I can however manually create tokens using the Django shell. Those tokens will be properly added to the authtoken_token table.
Any help on this would be greatly appreciated,
Thanks
Things to check:
Is the receiver even running? Add a print or log inside to check that it is called in the first place
If AUTH_USER_MODEL is not defined, you may need to use get_user_model() instead
Make sure that this code is inside the models.py file of an app that is in settings.INSTALLED_APPS
I've used DRF for several years and never had this problem. One of the above is likely your culprit.
I have written my test cases in two separate test files (e.g. test_1 and test_2). In both of test cases that I am testing my models I have code duplications because of similar processes.
For example, I need to login the user and test the credential.
Sample of code:
import test_data
from django.test import TestCase
from UserData.models import MyModel
from django.contrib.auth.models import User
class UserDataMyModelTestCalls(TestCase):
#classmethod
def setUpTestData(cls):
cls.test_user = User.objects.create_user(test_data.test_user_data['user_name'],
test_data.test_user_data['email'],
test_data.test_user_data['password'])
def test_faulty_login_credentials(self):
self.client.login(username=test_data.faulty_user_data['user_name'], password=test_data.faulty_user_data['password'])
response = self.client.get('/userdata/mymodelurl/', {})
self.assertEqual(response.status_code, 403)
I am using a separate file with user credentials to avoid duplications again.
Sample of test_data file:
test_user_data = {'id': u'1',
'user_name': 'tempUsername',
'password': 'tempPassword',
'email': 'tempEmaily#test.com'}
Update: Adding the UserTests class that I want to use as a common class for all my test cases. I am defining and calling the test through the test_1.py like this:
import UserTests
from django.test import TestCase
class UserDataWayPointTestCalls(TestCase):
testCasesObject = UserTests.UserDataTestCalls()
test_user = testCasesObject.setUpTestData()
response = testCasesObject.test_faulty_login_credentials()
My UserDataTestCalls class is defined like this:
import test_data
from django.test import Client
from django.test import TestCase
from django.contrib.auth.models import User
class UserDataTestCalls(TestCase):
def __init__(self):
self.test_user = None
self.faulty_login_response = None
def setUpTestData(self):
self.client = User.objects.create_user(test_data.test_user_data['user_name'],
test_data.test_user_data['email'],
test_data.test_user_data['password'])
self.client = Client()
return self.client
def test_faulty_login_credentials(self):
self.client.login(username=test_data.faulty_user_data['user_name'],
password=test_data.faulty_user_data['password'])
response = self.client.get('/userdata/mymodelurl/', {})
return response
When I execute the code above I get IntegrityError: (1062, "Duplicate entry 'tempUsername' for key 'username'"). Temporarily I modify the username value to proceed and I get the following error AttributeError: 'UserDataTestCalls' object has no attribute '_testMethodName'.
I tried to create a separate class with name e.g. UserDataTestCalls and include the common parts of my test cases such as User.objects.create_user, self.client.login etc...
Unfortunately I end up getting errors that the database although it said Destroying test database for alias 'default'... on the next run I got username duplications e.g. Duplicate entry 'tempUsername' for key 'username' etc...
When I tried to overcome this problem by changing the username for testing purposes then I got another problem 'NoneType' object has no attribute 'login'.
Which it points that the self.client variable is not binded with the test_user that I am creating.
I tried to search online and find documentation on how to overcome my problem but all the documentation are pointing to use separate scripts for your tests individually, which I can understand if you have different test cases. In my case 90% of my test cases are exactly the same.
So I am sure there is a way to create a user in a separate class and create all my test cases in that class too, so I could call them from a separate test file(s) when I need them.
Can someone point me to the correct direction or provide some links with examples/documentation that I could read from?
Thank you in advance for your time and effort.
Try creating a common test class.
class CreateUserTestCase(TestCase):
def setUpTestData(self):
self.user = User.objects.create_user(
test_data.test_user_data['user_name'],
test_data.test_user_data['email'],
test_data.test_user_data['password'],
)
You want to assign the new user to self.user. Don't replace self.client which should be the test client, not the user. You don't need to do self.client = Client(), the Django test case will take care of this for you.
Then subclass the test case and add your tests.
class UserDataTestCalls(CreateUserTestCase):
def test_faulty_login_credentials(self):
self.client.login(
username=test_data.faulty_user_data['user_name'],
password=test_data.faulty_user_data['password'],
)
response = self.client.get('/userdata/mymodelurl/', {})
return response
From your question, I wasn't sure if test_data is different for each class. If so, you'll have to change this slightly.
I am using Factory Boy to create test factories for my django app. The model I am having an issue with is a very basic Account model which has a OneToOne relation to the django User auth model (using django < 1.5):
# models.py
from django.contrib.auth.models import User
from django.db import models
class Account(models.Model):
user = models.OneToOneField(User)
currency = models.CharField(max_length=3, default='USD')
balance = models.CharField(max_length="5", default='0.00')
Here are my factories:
# factories.py
from django.db.models.signals import post_save
from django.contrib.auth.models import User
import factory
from models import Account
class AccountFactory(factory.django.DjangoModelFactory):
FACTORY_FOR = Account
user = factory.SubFactory('app.factories.UserFactory')
currency = 'USD'
balance = '50.00'
class UserFactory(factory.django.DjangoModelFactory):
FACTORY_FOR = User
username = 'bob'
account = factory.RelatedFactory(AccountFactory)
So I am expecting the factory boy to create a related UserFactory whenever AccountFactory is invoked:
# tests.py
from django.test import TestCase
from factories import AccountFactory
class AccountTest(TestCase):
def setUp(self):
self.factory = AccountFactory()
def test_factory_boy(self):
print self.factory.id
When running the test however, it looks like multiple User models are being create, and I am seeing an integriy error:
IntegrityError: column username is not unique
The documentation does mention watching out for loops when dealing with circular imports, but I am not sure whether that is whats going on, nor how I would remedy it. docs
If anyone familiar with Factory Boy could chime in or provide some insight as to what may be causing this integrity error it would be much appreciated!
I believe this is because you have a circular reference in your factory definitions. Try removing the line account = factory.RelatedFactory(AccountFactory) from the UserFactory definition. If you are always going to invoke the account creation through AccountFactory, then you shouldn't need this line.
Also, you may consider attaching a sequence to the name field, so that if you ever do need more than one account, it'll generate them automatically.
Change: username = "bob" to username = factory.Sequence(lambda n : "bob {}".format(n)) and your users will be named "bob 1", "bob 2", etc.
To pass result of calling UserFactory to AccountFactory you should use factory_related_name (docs)
Code above works next way:
AccountFactory for instantiating needs SubFactory(UserFactory).
UserFactory instantiates User.
UserFactory after instantiating calls RelatedFactory(AccountFactory)
Recursion,.. that is broken due to unique username constraint (you probably want to generate usernames via FuzzyText or Sequence)
So you need write UserFactory like this:
class UserFactory(factory.django.DjangoModelFactory):
account = factory.RelatedFactory(AccountFactory, factory_related_name='user')
username = factory.Sequence(lambda a: 'email%04d#somedomain.com' % a)
# rest of code
But you can still experience issues with already written tests. Imagine you have in tests places like next:
user = UserFactory()
account = Account(user=user)
Then adding RelatedFactory will break tests. If you haven't lots of tests and contributors in your project, you could rewrite them. But if not, it is not an option. Here is how it could be handled:
class UserFactory(factory.django.DjangoModelFactory):
class Params:
generate_account = factory.Trait(
account=factory.RelatedFactory(AccountFactory, factory_related_name='user')
)
Then code above won't be broken, because default call of UserFactory won't instantiate AccountFactory. To instantiate user with account:
user_with_account = UserFactory(generate_account=True)