Django request.user becomes anonymous after changing page. Using custom user model - python

I'm using a custom user model that extends AbstractBaseUser. This is the user model:
class cUser(AbstractBaseUser):
def save(self, *args, **kwargs):
pass
role_id = models.IntegerField()
user_id = models.IntegerField()
email = models.CharField(max_length=40)
password = models.CharField(max_length=40)
f_name = models.CharField(max_length=40)
l_name = models.CharField(max_length=40)
address_id = models.IntegerField()
phone_num = models.IntegerField()
loan_item_count = models.IntegerField()
id = models.IntegerField(unique = True, primary_key = True)
def __init__(self, dictionary, *args, **kwargs):
self.role_id = int(dictionary['role_id'])
self.user_id = dictionary['user_id']
self.email = dictionary['email']
self.password = dictionary['password']
self.f_name = dictionary['f_name']
self.l_name = dictionary['l_name']
self.address_id = dictionary['address_id']
self.phone_num = dictionary['phone_num']
self.loan_item_count = dictionary['loan_item_count']
self.id = self.user_id
USERNAME_FIELD = 'user_id'
class Meta:
managed = False
I don't want the model to affect the DB in any way. I'm loading it by a simple raw SQL query from a gateway method.
This is how I'm handling login:
def login_request(request):
if request.method == 'POST':
form = LoginForm(request.POST)
if form.is_valid():
username = request.POST['username']
password = request.POST['password']
user = userGateway(username,password)
if user is not None:
print("=========USER==========")
print(user.email)
user.backend = 'django.contrib.auth.backends.ModelBackend'
login(request,user)
print(request.user.is_authenticated)
if user.role_id==1:
return render(request, 'biblioteca/admin/landing.html')
# return HttpResponseRedirect('/')
else:
return render(request, 'biblioteca/landing.html')
else:
print("=========NOT USER==========")
else:
if(request.user is not None and not request.user.is_anonymous):
return render(request, 'biblioteca/admin/landing.html')
form = LoginForm()
return render(request, 'biblioteca/login.html', {'form': form})
As you can see, I'm setting the back-end before login to authenticate without having to pass through a password - the password check is being done when the user object is created by comparing the password passed in with the password retrieved from the DB.
If I return a render, as seen here, the next page contains the proper request.user object. If I return a redirect, it does not. Additionally, if I leave that page at all, the user becomes unauthenticated and anonymous again, losing the session.
Any help as to why this happens would be much appreciated.

When you redirect, the request is completed and a redirect code is sent to the client, who then makes a new request to the new page without the POST data for the login form. The user may not be getting their token. In that case, render a page to log them in that delays for a few seconds, then redirects them to the right spot.

So, this was solved by turning userGateway() into a proper auth backend and implementing it like that.
Explanation as to why:
The sessionID in django stores a hash of the user's PK as well as the auth backend used to log in. Whenever a new request is sent, data needed is lazy-loaded from the DB using this hash.
This is why login() was properly authenticating the user on the login page, because it was pretty much forced to. As soon as another request happened, though, it would be unable to load the user's data from the DB, and would flush the sessionid.

Related

Field 'id' expected a number but got '-'

I'm basically new to the Django and got such ValueError.
I have a basic user model inheriting from AbstractUser class.
Also I have Profile model (in separate app), which also contain OneToOneField refering to my User model.
First of all, lets take a look on my Profile model:
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='profile')
country = models.ForeignKey('Country', on_delete=models.SET_DEFAULT, default='-')
full_name = models.CharField(max_length=32, default='-')
about = models.TextField(max_length=1000, default="User don't set any information about")
register_date = models.DateField(auto_now=True)
image = models.ImageField(upload_to='images/', default='images/no_avatar.png')
active = models.BooleanField(default=False)
website = models.URLField(default='-')
github = models.URLField(default='-')
twitter = models.URLField(default='-')
instagram = models.URLField(default='-')
facebook = models.URLField(default='-')
Secondly when I create a new User I have to also create his own profile (model instance refering to the user instance).
Let's look on my view.py where I creating a new user.
class SignupView(View):
def get(self, request):
"""
:param request:
:return: Register page
"""
return render(request, "login/register.html", {
'form': SignupForm()
})
def post(self, request):
"""
Tests data validity,creating user if everything is ok.
:param request:
:return: Particular user page if data is valid, otherwise it will return couple of mistake messages
"""
form = SignupForm(request.POST)
if form.is_valid():
username = form.cleaned_data['username']
email = form.cleaned_data['email']
password1 = form.cleaned_data['password']
password2 = form.cleaned_data['confirm_password']
if password1 == password2:
try:
user = User.objects.create_user(username=username, email=email, password=password1)
profile = Profile.objects.create(user=user)
user.save()
profile.save()
return HttpResponseRedirect(reverse('myprofile:index', args=(user.id, )))
except IntegrityError:
return render(request, 'login/register.html', {
'message': 'Username is already taken',
'form': form
})
else:
return render(request, 'login/register.html', {
'message': 'Passwords must match',
'form': form
})
return render(request, 'login/register.html', {
'form': form
})
I left the whole class based view but you likely need to check post function out.
And this is where the problem begins.
I getting an error on "profile = Profile.objects.create(user=user)" line. On this moment Django stopping an execution and raising an ValueError with "Field 'id' expected a number but got '-'.
".
What I already have tried?
Remove a database and also remove migrations by "python3 manage.py migrate app zero --fake" and then reapplying migrations to a new database.
So I don't probably think this is a problem with migration (not sure).
But what did I miss?
I will be very grateful for any help.

Django - Authenticate against existing DB

Apologies if this is simple or my terminology is off, this is my first django project. I haven't been able to find a similar solution for this online.
I have an existing application, with a postgres DB where I authenticate my users. I have wrote an application in Django to interact with some tables and display info to the user. I would like to use Django to login and track User sessions against this DB. so I can use the features like
{% if user.is_authenticated %}
but I don't want to use the migrate command so I don't want to change the existing DB. I can access the table where the account info is as I created a model for it.
I see you can use remote user logon param but I cant find any sample or guide on how to use it and am completely lost.
Right now I create a login form in the views page. Then get the username and password that is entered, but I don't know what to do next. Also would need to hash the password. Is there a libray in djano that will do that for the app.
Any pointers or online guides for this would be appreciated.
Here is the views for login
if request.method == "POST":
form = LoginForm(request.POST)
if form.is_valid():
email = form.data['account_email']
password = form.data['account_password']
user = authenticate(username=email)
if user.check_password(password):
login(request, user)
return redirect('myapp:cust_select')
else:
# Username and password did not match
raise ValidationError('Invalid Username/Password')
return render(request, 'myapp/login.html', {'form' : form}
backends.py
from django.conf import settings
from django.contrib.auth import get_user_model
class UserAuthBackend(object):
def authenticate(self, username=None, password=None):
try:
account = get_user_model()
user = account.objects.get(account_email=username)
if user:
return user
except account.DoesNotExist:
print "account not found"
return None
def get_user(self, user_id):
try:
account = get_user_model()
return account.objects.get(pk=user_id)
except User.DoesNotExist:
return None
models.py
class Accounts(AbstractUser):
account_id = models.AutoField(primary_key=True)
account_email = models.CharField(max_length=100)
account_password = models.CharField(max_length=20)
def __str__(self):
return self.account_email
class Meta:
managed = False
db_table = 'accounts'
settings.py
AUTHENTICATION_BACKENDS = ( 'myapp.backends.UserAuthBackend', )
Its keeps exiting with the same error in the sql query.
column accounts.password does not exist
LINE 1: SELECT "accounts"."password", "accounts"."last_login", "acco...
It doesnt appear to be using my Account model. It does select it from that table but how can i get it to stop requesting accounts.password and accounts.last_login as they dont exist in y Accounts model
For reference
Note: You need to do try, catch to get this code working
def login(request):
form = LoginForm()
if request.method == "POST":
form = LoginForm(request.POST)
if form.is_valid():
username = form.data['account_email']
password = form.data['account_password']
# First authenticate
user = authenticate(request, username=username, password=password)
if user is not None :
# Succeed, now log user in
login(request,user)
return redirect('myapp:select')
else:
# Username and password did not match
raise ValidationError('Invalid Username/Password')
return render(request, 'myapp/login.html', {'form' : form})

Django user turns into Anonymous User

I'm trying to perform django authentication using a custom user model. Note this is for a school project and not
I have the following User model
class User(AbstractBaseUser):
userID = models.AutoField(primary_key=True)
username = models.CharField(max_length=20, unique=True)
password = models.CharField(max_length=24)
first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=50)
USERNAME_FIELD = "username"
REQUIRED_FIELDS = ["first_name", "last_name"]
def get_full_name(self):
return self.first_name + " " + self.last_name
def get_short_name(self):
return self.username
class Meta:
db_table = "User"
app_label = "funbids"
managed = False
I have my model defined in settings.py
AUTH_USER_MODEL = 'funbids.User'
I'm also using a custom auth backend. Note this is just a test using cleartext passwords (I'm aware this is a terrible idea to do in any production environment). I'm using a custom auth backend because a requirement for this app is to authenticate using raw SQL queries with an existing database.
class AuthBackend(object):
"""
Authenticate a user in funbids
"""
def authenticate(self, request, username=None, password=None):
# Test credentials
cursor = connections["funbids"].cursor()
cursor.execute("SELECT 1 FROM User WHERE username=%s AND password=%s", [username, password])
if cursor.fetchone():
# Have to grab the model, then authenticate
user = User.objects.get(username=username)
return user
else:
return None
def get_user(self, user_id):
try:
return User.objects.get(username=user_id)
except User.DoesNotExist:
return None
Everything seems to work in my login view.
def login_user(request, login_failed=False):
# Redirect the user to the index if they're already authenticated and arrive at the login page
if request.user.is_authenticated:
return redirect("funbids:index")
# Get the username and password from POST data
username = request.POST.get("username", "")
password = request.POST.get("password", "")
next = request.POST.get("next", "")
# Attempt to authenticate the user if both a username and password are present
if username and password:
log.debug("User %s requesting login" % username)
# Test credentials
user = authenticate(request, username=username, password=password)
if user is not None:
login(request, user)
log.debug("authenticated user is: %s" % request.user)
request.session.set_expiry(46800)
else:
login_failed = True
if request.user.is_authenticated:
# Authentication succeeded. Send the user to the original page they requested
# using the the "next" POST data or the index.
log.debug("Successfully authenticated user %s" % request.user)
if next:
return redirect(next)
else:
return redirect("funbids:index")
else:
# Failed authenticate, send back to login page
log.debug("Failed to authenticate user %s" % username)
# No credentials present/user failed auth - just load the page and populate the "next" form input
# in case the user was redirected here from a view they couldn't access unauthenticated.
next = request.GET.get("next", "")
try:
template = loader.get_template("funbids/login.html")
except TemplateDoesNotExist:
raise Http404("Page Not Found")
context = {
"pagetitle": "Welcome to FunBids!",
"next": next,
"login_failed": login_failed,
"template": template,
"request": request,
}
# Return the rendered page for display.
return render(request, template_name="funbids/page.html", context=context)
The debug statement prints our the username perfectly, something like:
[DEBUG] Successfully authenticated user adam
HOWEVER
Once I switch to another view, I'm no longer logged in. Instead, I'm an anonymous user, for instance:
def search(request):
log.debug("request user is: %s" % request.user)
try:
template = loader.get_template("funbids/search.html")
except TemplateDoesNotExist:
raise Http404("Page Not Found")
context = {
"pagetitle": "Search items for sale",
"template": template,
"request": request,
}
# Return the rendered page for display.
return render(request, template_name="funbids/page.html", context=context)
This time the debug statement prints out:
[DEBUG] request user is: AnonymousUser
I've done some reading on the issue and found that this happens when a user authenticates, but doesn't login. However, I can login successfully without any issues, so I'm not sure what's going on.
Would appreciate any help...thanks.
It turns out the problem was very subtle. Django wasn't able to get my user because my get_user method was wrong in my auth backend.
return User.objects.get(username=user_id)
should be
return User.objects.get(userID=user_id)
as the "userID" field is the primary key.

Django 1.9 using django sessions in two page forms

How can I use Django sessions to have a user be able to start a form on one page, and move to the next page and have them complete the form?
I have looked into pagination and wizard forms, but I don't get them at all.
When I have one page with a small portion of a model I'm using - in forms - and another page with the rest of the model - forms.py with the rest of the model info - I can use the first form perfectly.
But when I move to the next page, I get an error saying (1048, "Column 'user_id' cannot be null").
My best guess is to use Django sessions to fix this issue. I don't want the user to have to put in their username and password a second time to get this to work. Any ideas?
my models/forms.py:
class Contact(models.Model):
user = models.OneToOneField(User)
subject = models.CharField(max_length=100, blank=True)
sender = models.EmailField(max_length=100, blank=True)
message = models.CharField(max_length=100, blank=True)
def __str__(self):
return self.user.username
class UserForm(forms.ModelForm):
password = forms.CharField(widget=forms.PasswordInput())
class Meta:
model = Contact
fields = ('username', 'password', 'email')
class ContactForm1(forms.Form):
class Meta:
model = Contact
fields = ('subject', 'sender')
class ContactForm2(forms.Form):
message = forms.CharField(widget=forms.Textarea)
class Meta:
model = Contact
fields = ('message',)
views:
def contact(request):
registered = False
if request.method =='POST':
user = UserForm(request.POST)
contact = ContactForm1(request.POST)
if user.is_valid() and contact.is_valid():
user = user.save()
user.set_password(user.password)
user.save()
contact = contact.save(commit=False)
contact.user = user
registered = True
return render(request, 'mysite/contact.html', {'user': user, 'contact': contact, 'registered': registered})
def contact_second(request):
if request.method =='POST':
contact = ContactForm2(request.POST)
if contact.is_valid():
contact = contact.save(commit=False)
contact.save()
return render(request, 'mysite/contact_two.html', {'contact': contact}
I think it's a good idea to use sessions to store the forms because you don't want on each page to store the user input into the database because what if s/he change mind in the 3rd page and s/he wants to discard the registration or whatever it is?
I think is better to store the forms in session until you get in the last page, you ensure that everything is valid and then save the data in the database.
So let's say that the bellow code is in one of the view that will serve the first form/page. You retrieve the data from the POST request and you check if the given data are valid using the form.is_valid(), you then store the form.cleaned_data (which contains the user's validated data) to a session variable.
form = CustomForm(request.POST)
...
if form.is_valid():
request.session['form_data_page_1'] = form.cleaned_data
Note here that you may need to add code to redirect the user to the next page if form.is_valid() is true, something like this:
if form.is_valid():
request.session['form_data_page_1'] = form.cleaned_data
return HttpResponseRedirect(reverse('url-name-of-second-page'))
Then in any other view let's say in the view that is going to serve the second page you can retreive the from data from the first page like this:
first_page_data = request.session['form_data_page_1']
And you can do whatever you want with them as if it was after you executed the form.is_valid() in the first view.

Avoid access to other pages via URL, if not logged on (Django)

Team, I have used Django Authentication models which is validating the login to the my blog, but it still permits users to access other restricted pages through url, I need to avoid this, please help on that. Please add maximum details as you can, I am struggling a lot with that
Models:
from django.db import models
from django.db.models import permalink
from django.contrib.auth.models import User
class Post(models.Model):
title = models.CharField(max_length=100)
body = models.TextField()
datposted = models.DateTimeField('date posted')
category = models.ForeignKey('Category')
owner = models.ForeignKey('UserProfile')
def __str__(self):
return '%s' % self.title
class Category(models.Model):
title = models.CharField(max_length=100)
def __str__(self):
return self.title
class UserProfile(models.Model):
# This line is required. Links UserProfile to a User model instance.
user = models.OneToOneField(User)
# The additional attributes we wish to include.
website = models.URLField(blank=True)
picture = models.ImageField(upload_to='profile_images', null=True)
def __unicode__(self):
return self.user.username
class Logout(User):
force_logout_date = models.DateTimeField(null=True, blank=True)
Views:
def index(request):
template = "index.html"
return render(request,template)
def menu(request):
return render(request,"menu.html")
def view_posts(request):
return render_to_response('posts.html',{'posts':Post.objects.all()})
def view_post(request, post_id=1):
return render_to_response('view_post.html',{'post':Post.objects.get(id=post_id)})
def view_by_year(request):
cur_year=timezone.now().year
posts_cur_year = Post.objects.filter(datposted__year=cur_year)
return render_to_response('view_by_year.html',{'posts_cur_year':posts_cur_year})
def view_by_month(request):
cur_month=timezone.now().month
posts_cur_month = Post.objects.filter(datposted__month=cur_month)
return render_to_response('view_by_month.html',{'posts_cur_month':posts_cur_month, 'cur_month':cur_month})
def view_by_owner(request):
user = request.user
posts_owner = Post.objects.filter(owner__user=request.user)
return render_to_response('view_by_owner.html',{'view_owner':posts_owner})
def register(request):
# Like before, get the request's context.
context = RequestContext(request)
# A boolean value for telling the template whether the registration was successful.
# Set to False initially. Code changes value to True when registration succeeds.
registered = False
if request.method == 'POST':
# Attempt to grab information from the raw form information.
# Note that we make use of both UserForm and UserProfileForm.
user_form = UserForm(data=request.POST)
profile_form = UserProfileForm(data=request.POST)
# If the two forms are valid...
if user_form.is_valid() and profile_form.is_valid():
# Save the user's form data to the database.
user = user_form.save()
# Now we hash the password with the set_password method.
# Once hashed, we can update the user object.
user.set_password(user.password)
user.save()
# Now sort out the UserProfile instance.
# Since we need to set the user attribute ourselves, we set commit=False.
# This delays saving the model until we're ready to avoid integrity problems.
profile = profile_form.save(commit=False)
profile.user = user
profile.save()
registered = True
else:
print user_form.errors, profile_form.errors
# Not a HTTP POST, so we render our form using two ModelForm instances.
# These forms will be blank, ready for user input.
else:
user_form = UserForm()
profile_form = UserProfileForm()
# Render the template depending on the context.
return render_to_response(
'register.html',
{'user_form': user_form, 'profile_form': profile_form, 'registered': registered},
context)
def user_login(request):
# Like before, obtain the context for the user's request.
context = RequestContext(request)
# If the request is a HTTP POST, try to pull out the relevant information.
if request.method == 'POST':
# Gather the username and password provided by the user.
# This information is obtained from the login form.
username = request.POST['username']
password = request.POST['password']
# Use Django's machinery to attempt to see if the username/password
# combination is valid - a User object is returned if it is.
user = authenticate(username=username, password=password)
# If we have a User object, the details are correct.
# If None (Python's way of representing the absence of a value), no user
# with matching credentials was found.
if user:
# Is the account active? It could have been disabled.
if user.is_active:
# If the account is valid and active, we can log the user in.
# We'll send the user back to the homepage.
login(request, user)
return HttpResponseRedirect('/menu/')
else:
# An inactive account was used - no logging in!
return HttpResponse("Sua conta nao esta ativa.")
else:
# Bad login details were provided. So we can't log the user in.
print "Credenciais Incorretas: {0}, {1}".format(username, password)
return HttpResponse("Login invalido.")
# The request is not a HTTP POST, so display the login form.
# This scenario would most likely be a HTTP GET.
else:
# No context variables to pass to the template system, hence the
# blank dictionary object...
return render_to_response('login.html', {}, context)
def create_post(request):
if request.method == 'POST':
form = CreatePostForm(request.POST)
if form.is_valid():
post = form.save(commit=False)
post.datposted = datetime.datetime.now()
#post.owner = request.user()
post.save()
return HttpResponseRedirect('/posts/')
else:
return HttpResponse("Favor. Verifique os campos necessarios")
else:
form = CreatePostForm()
f = {'form' : form}
return render(request,'create_post.html',f)
def logout(request):
auth.logout(request)
return render_to_response('logout.html')
You can use the #login_required decorator above each view that you want to protect:
#login_required
def index(request):
template = "index.html"
return render(request,template)
This will ensure that the user has logged in before allowing them access to each view that utilizes this decorator.
See the Documentation for more information.

Categories