Can I safely use `self.request.user.is_authenticated` in views? - python

We are using Django 2.1 for Speedy Net. There is a view which I want to be different for admin (staff) than for regular users. I added the following (3 first) lines to the code:
def get_user_queryset(self):
if (self.request.user.is_authenticated):
if ((self.request.user.is_staff) and (self.request.user.is_superuser)):
return User.objects.get_queryset()
return User.objects.active()
But the problem is, one of the tests fails with an error message:
AttributeError: 'WSGIRequest' object has no attribute 'user'
(link) And I want to know - Is it safe to use self.request.user.is_authenticated in views? Should I fix the test to work or can my code fail in production? The test uses RequestFactory(), is there a problem with RequestFactory() not containing attribute 'user'? Or is there a problem with my code which may also fail in production?
If I should fix the test, how do I do it? Here is the code of the test which fails:
class UserMixinTextCaseMixin(object):
def set_up(self):
super().set_up()
self.factory = RequestFactory()
self.user = ActiveUserFactory(slug='look-at-me', username='lookatme')
self.other_user = ActiveUserFactory()
def test_find_user_by_exact_slug(self):
view = UserMixinTestView.as_view()(self.factory.get('/look-at-me/some-page/'), slug='look-at-me')
self.assertEqual(first=view.get_user().id, second=self.user.id)
By the way, I'm not sure what is the purpose of slug='look-at-me' in the view = ... line of the test?

Yeah... It's safe to use request.user or self.request.user in Django and it is the Django way of checking whether the User is authenticated or not.
AttributeError: 'WSGIRequest' object has no attribute 'user'
Django assigning the authenticated user to the request object within the Django Middleware execution, specifically within the AuthenticationMiddleware class (source-code of AuthenticationMiddleware class).
So, adding the AuthenticationMiddleware class to the Middleware settings or creating your own middleware class is will solve the AttributeError: 'WSGIRequest' object has no attribute 'user' error.

I found out this answer on Stack Overflow.
From the docs:
# Recall that middleware are not supported. You can simulate a
# logged-in user by setting request.user manually.
request.user = self.user
# Or you can simulate an anonymous user by setting request.user to
# an AnonymousUser instance.
request.user = AnonymousUser()
Adding request.user = AnonymousUser() in the tests solves the problem:
class UserMixinTextCaseMixin(object):
def set_up(self):
super().set_up()
self.factory = RequestFactory()
self.user = ActiveUserFactory(slug='look-at-me', username='lookatme')
self.other_user = ActiveUserFactory()
def test_find_user_by_exact_slug(self):
request = self.factory.get('/look-at-me/some-page/')
request.user = AnonymousUser()
view = UserMixinTestView.as_view()(request=request, slug='look-at-me')
self.assertEqual(first=view.get_user().id, second=self.user.id)
Replacing MIDDLEWARE with MIDDLEWARE_CLASSES doesn't solve the problem and we are already using 'django.contrib.auth.middleware.AuthenticationMiddleware', but this is not used by RequestFactory().

Related

can't adapt type 'SimpleLazyObject' with get_context_data (Class Based View)

I have a Django web app deployed to Heroku.
App is deployed and working well, except for the related issue.
Some specs :
In my local env I use SQLite 3 DB
In Heroku env I use Postgress DB
When I try to render a class based view this error happens to me:
can't adapt type 'SimpleLazyObject'
After some checks about this issue I suspect it is related to the User object. but i don't know how to approach it.
View Code:
class ProfileListView(LoginRequiredMixin, ListView):
model = Profile
template_name = 'profiles/profile_list.html'
context_object_name = 'qs'
def get_queryset(self):
qs = Profile.objects.get_all_profiles(self.request.user)
return qs
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
return context
URL :
urlpatterns = [
path('', ProfileListView.as_view(), name='all-profiles-view'),
]
Customize manager:
class ProfileManager(models.Manager):
def get_all_profiles(self, me):
profiles = Profile.objects.all().exclude(user=me)
return profiles
EDIT:
It likely seems the source of the problem is related to :
user = User.objects.get(username__iexact=self.request.user)
The problem seems from get_queryset. Because django adds a user attribute to request that is an instance of SimpleLazyObject. Since your error traceback shows it was problem with SimpleLazyObject, I suspect the problem comes from request.user could be either an object of User or anonymous user. So, it the following answer, I tried to add if statement to make sure that only the authenticated user will have qs.
You can tweak it a bit for your needs.
try replace with
def get_queryset(self):
qs= []
if self.request.user.is_authenticated():
qs = Profile.objects.get_all_profiles(self.request.user)
return qs

Django Allauth Signup Prevent Login

Is there anyway to prevent the Allauth Signup form from automatically logging in the User?
I found a similar post here with no answers:
prevent user login after registration using django-allauth
You get logged in because this behavior is baked into the signup view of the allauth. When the form is valid, the view calls the function called complete_signup that does two things:
Emits the user_signed_up signal
Logs a user in
To solve this, we need to leave the step 1 and replace the step 2 with a simple redirect.
Here's how this can be done:
Extend the SignupView from allauth/account/views.py and override its form_valid method like this:
class CustomSignupView(SignupView):
def form_valid(self, form):
# By assigning the User to a property on the view, we allow subclasses
# of SignupView to access the newly created User instance
self.user = form.save(self.request)
try:
signals.user_signed_up.send(
sender=self.user.__class__,
request=self.request,
user=self.user,
**{}
)
return HttpResponseRedirect(self.get_success_url())
except ImmediateHttpResponse as e:
return e.response
Haven't tested the code, but it should be working.
Wire your new view up in urls.py, so it replaces the old Signup view url.
I am using django-allauth 0.42.0
There is no need to extend the SignUp view, it can be achieved by the setting:
ACCOUNT_EMAIL_VERIFICATION = 'mandatory'
in your project settings.py file.

Django: How to get domain name inside a signal handler

I'm trying to send an email with my website address from django signal. I found this question: https://stackoverflow.com/a/15521046/2385132 and proceeded as was advised in the accepted answer, but when using that code, I'm getting this error:
AttributeError: 'NoneType' object has no attribute 'get_host'
Which is coming from the get_current_site in my code:
#receiver(post_save, sender=MyModel)
def post_obj_save(sender, instance: MyModel, **kwargs):
def _get_html(obj: MyModel):
return render_to_string('confirmation_email.html', _get_context(obj))
def _get_context(obj: MyModel):
current_site = get_current_site(request=None)
domain = current_site.domain
action = reverse('obj_activation', request=None, format=None, kwargs={})
url = '{protocol}://{domain}/{action}'.format(protocol=PROTOCOL, domain=domain, action=action)
return {
'header': _('Thank you for registering with ASDF.'),
'prompt': _('In order to be able to log in into ASDF administrator panel, you have to activate your account using'),
'link_name': _('this link'),
'activation_url': url
}
send_mail(
_('ASDF account activation'),
_get_html(instance),
EMAIL_FROM,
[obj.owner.email],
fail_silently=False,
)
So the question is: how do I get full url of my view in a signal?
Btw. I'm using django-rest-framework.
In recent Django versions (probably your case), the domain is always taken from the request if SITE_ID is not defined in your settings. See this change introduced in 1.8 Django version:
Changed in Django 1.8:
This function will now lookup the current site based on
request.get_host() if the SITE_ID setting is not defined.
So, in your case request=None you must have the sites framework enabled, an entry for your current site/domain and SITE_ID setting pointing to the right instance in the Site table, try this and you will see :)

Authenticate in Django without a database

I have a Django app that gets it's data completely from apis. so I don't have to use database. Session data is stored on signed cookies. I tried to code a custom User model and a custom auth backend like on the docs, but I get the following error:
django.core.exceptions.ImproperlyConfigured: AUTH_USER_MODEL refers to model 'my_app.MyUser' that has not been installed
My settings.py:
AUTH_USER_MODEL = 'my_app.MyUser'
AUTHENTICATION_BACKENDS = ('django.contrib.auth.backends.ModelBackend',
'my_app.backends.LoginAuthBackend',)
models.py:
class MyUser(object):
def save(self):
pass
objects = None
username = ""
Here, If a try use the AbstractUser from django instead of Object I got the following error: AttributeError: 'NoneType' object has no attribute '_meta' or the db table doesn't exit.
backends.py
class LoginAuthBackend(object):
def authenticate(self, username=None, password=None):
if username and password:
try:
response = my_auth_function(username, password)
if response.status_code == 200:
token = response.get('my_key')
user = MyUser()
return user
except MyCustomException:
return None
It's drives me crazy. Looks like Django that's not easy to use without a DB.
EDIT
After several of tries, a simple way to solve this is remove 'django.contrib.auth.backends.ModelBackend' from AUTHENTICATION_BACKENDS and AUTH_USER_MODEL from settings. The model continues basically the same way. works smoothly
The default set of authentication back-end processors is defined in the AUTHENTICATION_BACKENDS setting. See the Django documentation for Customizing authentication.
By default, AUTHENTICATION_BACKENDS is set to:
['django.contrib.auth.backends.ModelBackend']
That’s the basic authentication backend that checks the Django users database and queries the built-in permissions.
So, if you don't want the django.contrib.auth.backends.ModelBackend authentication method, remove that from the list. You'll probably want to find (or create) a different one and add that to the list.

request.user not being populated by my django auth backend

I have written a authorization backend class that implements a authenticate method and a get_user method as per the django docs. I've added
AUTHENTICATION_BACKENDS = ('src.lib.auth_backend.MyBackend',)
to my settings.py file. Through print statements I can see that my code is being run and that it is returning a user object of the class I defined with AUTH_USER_MODEL in my settings.py.
By the time I get the request object within my django-rest-framework has_object_permsion function, request.user is always set to AnonymousUser.
Am I missing a step?
I've tried this with and without django.contrib.auth.middleware.AuthenticationMiddleware installed and get the same outcome.
This failure is happening in the following unit test
def test_get_user(self):
client = APIClient() # rest_framework.test.APIClient
client.login(username='user1',password='user1Password')
res = client.get('/websvc/users/' + str(user.user_id) + '/') # request.user will be AnonymousUser
self.assertEqual(res.status_code, 200) # it will be 403, which is appropriate for AnonymousUser
A couple of things may be wrong.
Have you created the user, before runnign the test?
What returns client.login?
Show us the whole test, to tell you what's wrong.

Categories

Resources