I want to create an authentication backend, because my usernames and passwords stores in existing DB table. In this table, there is more information than just usernames and passwords, that's why I created an extended user.
class MyUser(AbstractUser):
user = models.OneToOneField(User)
user_id = models.IntegerField()
code = models.BigIntegerField()
telefon = models.CharField(50)
remark = models.CharField(250)
fio = models.CharField(50)
As I understand, I don't have to include username and password in this model, because it's already included due to user = models.OneToOneField(User).
OK, then I create the backend:
from login.models import MyUser
class AuthBackend:
def authenticate(self, username=None, password=None):
try:
user = MyUser.user.objects.filter(username=username)
except MyUser.DoesNotExist:
return None
if user.is_pass_valid(password):
return user
else:
return None
def get_user(self, user_id):
try:
user = MyUser.user.objects.get(id=user_id)
except MyUser.DoesNotExist:
return None
return user
Is it all correct?
And main question: how my backend is going to return User object from DB table( not auth_user table, but existing table). I have to create models of users before authentication or what? Or I have an idea just to create such user if exists in method authenticate? And where should I call get_user method?
My idea:
def authenticate(self, username=None, password=None):
password = hashlib.md5(password).hexdigest()
cursor = connection.cursor()
cursor.execute("""
SELECT * FROM zusers
WHERE login = %s AND userpass = %s""", [username, password])
row = cursor.fetchone()
if row:
user = MyUser(username = row[0], password = row[1], code = row[2], telefon = row[3], remark = row[4], fio = row[5])
return user
else:
return None
I don't really get what you are trying...you are using md5 instead of something that actually works for encrypting passwords...
If you want to extend an user it is simple since django 1.5:
class MyUser(AbstractUser):
myCustomField = models.CharField()
#You don't need ids, oneToOne or anything weird...just extra fields for your user
Now in settings.py or whatever your settings module is you set:
AUTH_USER_MODEL = 'myapp.MyUser'
You can find the information in the documentation.
And...thats it, you don't need anything else to make it work. Have in mind that when you are going to use your users in other models you have 2 options:
from django.conf import settings
from django.db import models
class Article(models.Model):
author = models.ForeignKey(settings.AUTH_USER_MODEL)
Or:
from myapp.models import MyUser
from django.db import models
class Article(models.Model):
author = models.ForeignKey(MyUser)
Using this method you don't have to worry about anything else, just forget about the default...use MyUser every time.
Related
Writing my first web app using flask / SQLAlchemy. I have a many to many relationship between 'persons' and 'facilities.' When I successfully add a person using the registration form, the association table does not get a row added. Do I have to insert that row manually?
Here is the pertinent part of the model:
# app/models.py
from flask_login import UserMixin
from werkzeug.security import generate_password_hash, check_password_hash
from app import db, login_manager
# [START model]
# Build secondary table for many to many between facilities and persons
workers = db.Table('workers',
db.Column('facility_id', db.Integer, db.ForeignKey('facilities.id')),
db.Column('person_id', db.Integer, db.ForeignKey('persons.id'))
)
class Facility(db.Model):
__tablename__='facilities'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(60))
description = db.Column(db.String(128))
persons = db.relationship('Person', secondary='workers', backref='facilities', lazy = 'dynamic')
def __repr__(self):
return "<Facility name='%s')" % (self.name)
class Person(UserMixin, db.Model):
__tablename__ = 'persons'
id = db.Column(db.Integer, primary_key=True)
last_name = db.Column(db.String(60), index=True)
username = db.Column(db.String(60), index=True, unique=True)
email = db.Column(db.String(80), index=True)
password_hash = db.Column(db.String(128))
first_name = db.Column(db.String(60), index=True)
role = db.Column(db.Integer, db.ForeignKey('roles.id'))
is_person_active = db.Column(db.Boolean, index=True)
is_admin = db.Column(db.Boolean, default=False)
comments = db.Column(db.String(255))
animals = db.relationship('Animal', secondary='permissions', backref='persons', lazy = 'dynamic'))
#property
def password(self):
"""
Prevent password from being accessed
"""
raise AttributeError('password is not a readable attribute.')
#password.setter
def password(self, password):
"""
Set password to a hashed password
"""
self.password_hash = generate_password_hash(password)
def verify_password(self, password):
"""
Check if hashed password matches actual password
"""
return check_password_hash(self.password_hash, password)
def __repr__(self):
return "<Person name='%s', '%s', '%s')" % (self.first_name, self.last_name, self.username)
# Set up user_loader
#login_manager.user_loader
def load_user(user_id):
return Person.query.get(int(user_id))
And here is the view:
# app/auth/views.py
from flask import flash, redirect, render_template, url_for
from flask_login import login_required, login_user, logout_user
from . import auth
from .forms import LoginForm, RegistrationForm
from .. import db
from ..models import Person, Facility
#auth.route('/register', methods=['GET', 'POST'])
def register():
"""
Handle requests to the /register route
Add a person to the database through the registration form
"""
form = RegistrationForm()
form.facility_id.choices = [(f.id, f.name) for f in Facility.query.order_by('name')]
if form.validate_on_submit():
person = Person(facility=form.facility_id.data,
email=form.email.data,
username=form.username.data,
first_name=form.first_name.data,
last_name=form.last_name.data,
password=form.password.data)
# add person to the database
db.session.add(person)
db.session.commit()
flash('You have successfully registered! You may now login.')
# redirect to the login page
return redirect(url_for('auth.login'))
# load registration template
return render_template('auth/register.html', form=form, title='Register')
Thanks for the support #Michael. You were close enough that I found the problem; it was that I was not adding the person to the persons collection for the facility, so no row was inserted into the workers table. I added
facility = Facility.query.filter_by(id=form.facility_id.data).first()
facility.persons.append(person)
db.session.commit()
after the existing code
db.session.add(person)
db.session.commit()
in the registration view and it is correctly inserting rows in the workers table now.
If the above https://stackoverflow.com/a/60100671/1449799 doesn't work, I wonder if it's as simple as a spelling issue? You've said that the back ref from Facility to Person should be called facilities. perhaps in your call to the Person constructor in your register function you should change:
person = Person(facility=form.facility_id.data,
to
person = Person(facilities=[form.facility_id.data],
Perhaps this question is a duplicate of https://stackoverflow.com/a/25669256/1449799 ? It seems that the issue you're having is that in your register() function, there's no mention of facilities.
Without changing your model classes (e.g. to have the Person model know about its connected facilities in addition to the reverse of what you do have now in Facility for Person), I think you may be able to do something in register() like:
#this should maybe come after db.session.add(person), but before db.session.commit()
selected_facility = Facility.query.get(form.facility_id.data)
selected_facility.persons.append(person)
or alternatively
#this should maybe come after db.session.add(person), but before db.session.commit()
selected_facility = Facility.query.get(form.facility_id.data)
person.facilities.append(selected_facility)
I want new users to be redirected to a specific web page only when it's their first connexion ever.
I looked up online and on SO, but could not find anything.
Is there any easy way to do it with django ?
So there is what I tried :
Models.py
from django.db import models
from django.contrib.auth.models import User
from imagekit.models import ImageSpecField, ProcessedImageField
from imagekit.processors import ResizeToFill
# Create your models here.
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
manager = models.BooleanField(default=False)
first_visit = models.BooleanField(default=True)
Views.py
def verification(request):
#get currently logged in user
user = request.user
#check if the user is the first time
if user.first_visit == True:
#if this is the first time, change the value for false and redirect it to the place
user.first_visit = False
user.save()
return HttpResponseRedirect(reverse('profile'))
else:
return HttpResponseRedirect(reverse('profile'))
And in my settings
LOGIN_REDIRECT_URL = 'app:verification'
But I have an error message
NoReverseMatch at /users/accounts/signup/
'app' is not a registered namespace
I don't know if you're looking for that but you can add a field to the user models first_visit = models.BooleanField(default=True)
Then check in your view to see if this is user first visit
I would do it like that
1.) Create a custom user model with an additional field
from django.contrib.auth.models import AbstractUser
class User(AbstractUser):
first_visit = models.BooleanField(default=True)
2.) settings.py
#after logging in, redirect the user to the viewing view.
LOGIN_REDIRECT_URL = 'app:verification'
3.) views.py
def verification(request)
#get currently logged in user
user = request.user
#check if the user is the first time
if user.first_visit == True:
#if this is the first time, change the value for false and redirect it to the place
user.first_visit = False
user.save()
return HttpResponseRedirect(reverse('app:virst_visit_views'))
else:
return HttpResponseRedirect(reverse('app:next_visit_views'))
I want to override django all-auth signup view to input an extra field value.
In this case, User model has a foreign field which is combined to Company model.
from allauth.account.views import SignupView as AllAuthSignupView
from .models import Company
class SignupView(AllAuthSignupView):
def save(self):
user = super(SignupView, self).save()
a_company = get_object_or_404(Company, name='A')
user.company = a_company
return user
However, This only saves username, password, email. The company field is NULL.
I don't want an answer that recommends change the default value in Company model. That's not the way I try to solve this problem.
Rather than overriding the view, you can simply create a new Form to save the additional data. For example:
companies = (
('comapny_name_1', 'ABC'),
('comapny_name_2', 'DEF'),
)
class SignupForm(forms.Form):
company = forms.ChoiceField(choices=companies)
def signup(self, request, user):
user.company = Company.objects.get(name=self.cleaned_data['company'])
user.save()
Then add the Form Path to ACCOUNT_SIGNUP_FORM_CLASS in settings.py:
ACCOUNT_SIGNUP_FORM_CLASS = 'path.to.SignupForm'
More information can be found in documentation.
I'm been using the default user model in django for quite a abit and I realize , if I need to further enhance it , I would have to create my own custom User Model in django 1.5 .
I created my custom user model and I have a function which allows users to sign in .
I think my custom user model is incompatible with my function because it wouldn't allow me to do request.user . How can I fix this so I can use request.user again?
views
def LoginRequest(request):
form = LoginForm(request.POST or None)
if request.user.is_authenticated():
username = User.objects.get(username=request.user)
url = reverse('world:Profile', kwargs = {'slug': person.slug})
return HttpResponseRedirect(url)
if request.POST and form.is_valid():
user = form.authenticate_user()
login(request, user)
username= User.objects.get(username=request.user)
person = Person.objects.get(user=request.user)
url = reverse('world:Profile', kwargs = {'slug': person.slug})
return HttpResponseRedirect(url)
return render(request, 'login.html',{'form': form})
models
class PersonManager(BaseUserManager):
def create_user(self, email,date_of_birth, username,password=None,):
if not email:
msg = 'Users must have an email address'
raise ValueError(msg)
if not username:
msg = 'This username is not valid'
raise ValueError(msg)
if not date_of_birth:
msg = 'Please Verify Your DOB'
raise ValueError(msg)
user = self.model(
email=PersonManager.normalize_email(email),username=username,date_of_birth=date_of_birth)
user.set_password(password)
user.save(using=self._db)
return user
def create_superuser(self,email,username,password,date_of_birth):
user = self.create_user(email,password=password,username=username,date_of_birth=date_of_birth)
user.is_admin = True
user.is_staff = True
user.is_superuser = True
user.save(using=self._db)
return user
class Person(AbstractBaseUser, PermissionsMixin):
email = models.EmailField(verbose_name='email address',max_length=255,unique=True,db_index=True,)
username = models.CharField(max_length=255, unique=True)
date_of_birth = models.DateField()
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['username', 'date_of_birth',]
is_active = models.BooleanField(default=True)
is_admin = models.BooleanField(default=False)
is_staff = models.BooleanField(default=False)
objects = PersonManager()
def get_full_name(self):
return self.email
def get_short_name(self):
return self.email
def __unicode__(self):
return self.email
The problem is that User refers to django.contrib.auth.models.User and now you have got a Custom User pet.Person assuming you have in the settings.py
AUTH_USER_MODEL = "pet.Person"
you have to define User with the Custom User model and you can do this with get_user_model at the top of the file where you use User
from django.contrib.auth import get_user_model
User = get_user_model()
now you will be able to use Custom User model and the problem has been fixed.
For anyone else who might come across this problem, I also solved it by simply doing this on forms.py:
add this at the top of the forms.py file
from .models import YourCustomUser
and then add this to your forms.py CustomUser form:
class SignUpForm(UserCreationForm):
#profile_year = blaaa blaa blaaa irrelevant.. You have your own stuff here don't worry about it
# here is the important part.. add a class Meta-
class Meta:
model = YourCustomUser #this is the "YourCustomUser" that you imported at the top of the file
fields = ('username', 'password1', 'password2', #etc etc, other fields you want displayed on the form)
BIG NOTES, ATTENTION:
This code worked for my case. I have a view for signing users up, I had a problem here and I solved it, I haven't tried it for logging in users.
The include = () part is required, or you can add exclude = (), but you have to have one
Important caveat to update the above solutions...
If you're facing this kind of problem, you've probably tried various solutions around the web telling you to add AUTH_USER_MODEL = users.CustomUser to settings.py and then to add the following code to views.py forms.py and any other file that calls User:
from django.contrib.auth import get_user_model
User = get_user_model()
And then you scratch your head when you get the error:
Manager isn't available; 'auth.User' has been swapped for 'users.User'
Anytime your code references User such as:
User.objects.get()
Cause you know you already put objects = UserManager() in your custom user class (UserManager being the name of your custom manager that extends BaseUserManager).
Well as it turns out doing:
User = get_user_model() # somewhere at the top of your .py file
# followed by
User.objects.get() # in a function/method of that same file
Is NOT equivalent to:
get_user_model().objects.get() # without the need for User = get_user_model() anywhere
Perhaps not intuitive, but it turns out that that in python, executing User = get_user_model() once at the time of import does not then result in User being defined across subsequent calls (i.e. it does not turn User into a "constant" of sorts which you might expect if you're coming from a C/C++ background; meaning that the execution of User = get_user_model() occurs at the time of imports, but is then de-referenced before subsequent called to class or function/method in that file).
So to sum up, in all files that reference the User class (e.g. calling functions or variables such as User.objects.get() User.objects.all() User.DoesNotExist etc...):
# Add the following import line
from django.contrib.auth import get_user_model
# Replace all references to User with get_user_model() such as...
user = get_user_model().objects.get(pk=uid)
# instead of user = User.objects.get(pk=uid)
# or
queryset = get_user_model().objects.all()
# instead of queryset = User.objects.all()
# etc...
Hope this helps save others some time...
In forms.py
# change
from django.contrib.auth.models import User
# to
from django.contrib.auth import get_user_model
Then add the following code at the top
User = get_user_model()
All the solutions provided above did not work in my case. If you using Django version 3.1 there is another solution for you:
In auth/forms, comment out line 10 and change the model in line 104 & 153 to your defined model.
I am using Django1.4 with PostgreSQL. I am developing an application in which I have two models i.e. Students, Company.
class students(models.Model):
first_name = models.CharField(**option)
last_name = models.CharField(**option)
username = models.EmailField(max_length=100, unique=True)
password = models.CharField(_('password'), max_length=128)
# Some other attributes for Student models
class company(models.Model):
compnay_name = models.CharField(**option)
username = models.EmailField(max_length=100, unique=True)
password = models.CharField(_('password'), max_length=128)
#Some other attributes for company models
My Requirement:
Student and Company can create a new profile (provide a sign-up form)
Which creating a new profile for Student/Company, username i.e. email id should be unique. i.e. Email id should not exist in Student & Company models.(task completed )
Created 2 sign-In form for Student & Company login.
Issue:
As I am not using or extending User model, I am cannot use django in-built login & authenticate method.
How can I write a custom authentication method which should check user credentials in Student/Company username & password. (Have 2 different Sign-in form for Student & Company)
Please help me.
Thanks for reading my query.
backend.py
class LoginBackend:
def authenticate(self, username=None, password=None, model=None):
if model == "Student":
lookup_model = Student
elif model == "Employer":
lookup_model = Employer
try:
user = lookup_model.objects.get(email=username)
except Exception, e:
return None
return user
views.py
def check_auth(request):
user_object = Student.objects.get(email__iexact = unicode(email))
if check_password(password, user_object.password):
print authenticate(username = email, password = password, model = "Student")
login(request, user_object)
settings.py
AUTHENTICATION_BACKENDS = ("proj.app.backends.LoginBackend",)
Error
AttributeError at /xxx/login/
'Student' object has no attribute 'backend'
Write a custom authentication backend. Read this:
Writing an authentication backend
Handling authorization in custom backends
settings.AUTHENTICATION_BACKENDS
[update]
By writing and registering a custom authentication backend, you just have to use the standard Django authentication patterns. Looking at your sample code, I'm under the impression that you have understood it differently.
Since email is your unique key, I suggest using email for the login key, first check the login/password against Student, and if it fails, check against Company.
from django.contrib.auth.models import User
class JayapalsBackend(object):
def authenticate(self, username=None, password=None):
try:
o = Student.objects.get(email=username, password=password)
except Student.DoesNotExist:
try:
o = Company.objects.get(email=username, password=password)
except Company.DoesNotExist:
return None
return User.objects.get(email=o.email)
def get_user(self, user_id):
try:
return User.objects.get(pk=user_id)
except User.DoesNotExist:
return None
Then just use standard Django decorators:
#login_required
def some_private_view(request):
...