How to authenticate user form credentials from PostgreSQL with Django - python

I am able to register users to my custom table in the Postgres database. I would like to know how can I use the credentials from my Postgres table to authenticate login from my django web app. I know I have to create a custom backend but I don't know how we can import the credentials from Postgres to authenticate against the credentials that user entered.
My model:
from django.conf import settings
from django.db import models
from django.contrib.auth.models import User
from django.contrib.auth.models import AbstractBaseUser
# Create your models here.
# Database model Users has one to one relationship with Django User table
from django.db.models.signals import post_save
from django.dispatch import receiver
class Profile(AbstractBaseUser):
first_name = models.TextField()
last_name = models.TextField()
user = models.OneToOneField(to=settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
user_name = models.TextField(unique=True)
email = models.TextField()
password1 = models.TextField()
password2 = models.TextField()
date_created = models.DateTimeField(auto_now_add=True)
USERNAME_FIELD = 'user_name'
REQUIRED_FIELDS = []
def __str__(self):
return self.user.username
#receiver(post_save, sender=User)
def update_profile_signal(sender, instance, created, **kwargs):
if created:
Profile.objects.create(user=instance)
instance.profile.save()
I have my django project pointing towards my custom user model too.
I just don't know how to create a custom backend for authentication. If I could be guided towards it, I would really appreciate.

If you set the password for each user when creating a new user, the default authentication backend for Django will work just fine for you.
When saving a new Profile you should just do a instance.set_password(password) after validating if password and password2 are the same.
Also I would recommend not using receiver but rather maintaining a CustomManager for your table.
For now this should do the trick for you:
#receiver(post_save, sender=User)
def update_profile_signal(sender, instance, created, **kwargs):
if created:
Profile.objects.create(user=instance)
if password == password2:
instance.set_password(password)
instance.profile.save()
Then you can limit access to Views using the #login_required decorator. Some other ways are mentioned here

Related

Django add every user in foreign key model with default values

I have created a Django model with a foreign key from user, I want that all created user before be created in that table with a default value:
Models.py
from django.contrib.auth.models import User
class UserProfiles(models.Model):
myuser = models.OneToOneField(User, on_delete=models.CASCADE)
userstatus = models.CharField(default='active', max_length=20)
Can you help me that I can migrate this table and be created to all users who are registered before?
Try something like this:
post_save
from django.contrib.auth.models import User
from django.models.signals import post_save
from django.dispatch import receiver
#receiver(post_save, sender=UserProfiles)
def create_user_profile(sender, instance, created, **kwargs):
if created is True:
UserProfiles(instance)
I recommend creating this function in your app folder under signals.py
I hope this is what you are looking for

How to select a OnetoOne field from a QuerySet in a view?

See all users (that don't have search_hidden enabled) view
#login_required
def users(request):
"""List all users page"""
t_users = User.objects.all()
users = t_users.usersettings.filter(search_hidden=False).select_related('user')
context = {'users': users}
return render(request, 'users/users.html', context)
UserSettings model
from django.db import models
from django.contrib.auth.models import User
from django.db.models.signals import post_save
from django.dispatch import receiver
class UserSettings(models.Model):
"""Stores the user's settings."""
user = models.OneToOneField(User, related_name='usersettings', on_delete=models.CASCADE)
public_profile = models.BooleanField(default=True)
search_hidden = models.BooleanField(default=False)
class Meta:
verbose_name_plural = 'usersettings'
def __str__(self):
return f"{self.user}'s settings"
#receiver(post_save, sender=User)
def create_user_usersettings(sender, instance, created, **kwargs):
if created:
UserSettings.objects.create(user=instance)
#receiver(post_save, sender=User)
def save_user_usersettings(sender, instance, **kwargs):
instance.usersettings.save()
All users have a UserSettings model tied to them when their accounts are created. In my view, I want to select all users that have search_hidden disabled, however what I've tried doesn't work.The error 'QuerySet' object has no attribute 'usersettings' is displayed whenever the page is requested. I probably need to select each user and retrieve the settings, but I don't know how to do that in an efficient manner.
iliya commented that filtering using t_users.objects.filter(search_hidden=False) would return users where search_hidden is not true in their settings object.

Make User email unique django

How can I make a Django User email unique when a user is signing up?
forms.py
class SignUpForm(UserCreationForm):
email = forms.EmailField(required=True)
class Meta:
model = User
fields = ("username", "email", "password1", "password2")
def save(self, commit=True):
user = super(SignUpForm, self).save(commit=False)
user.email = self.cleaned_data["email"]
if commit:
user.save()
return user
I'm using the from django.contrib.auth.models User.
Do I need to override the User in the model. Currently the model doesn't make a reference to User.
views.py
class SignUp(generic.CreateView):
form_class = SignUpForm
success_url = reverse_lazy('login')
template_name = 'signup.html'
The best answer is to use CustomUser by subclassing the AbstractUser and put the unique email address there. For example:
from django.contrib.auth.models import AbstractUser
class CustomUser(AbstractUser):
email = models.EmailField(unique=True)
and update the settings with AUTH_USER_MODEL="app.CustomUser".
But if its not necessary for you to store the unique email in Database or maybe not use it as username field, then you can update the form's clean method to put a validation. For example:
from django.core.exceptions import ValidationError
class YourForm(UserCreationForm):
def clean(self):
email = self.cleaned_data.get('email')
if User.objects.filter(email=email).exists():
raise ValidationError("Email exists")
return self.cleaned_data
Update
If you are in mid project, then you can follow the documentation on how to change migration, in short which is to:
Backup you DB
Create a custom user model identical to auth.User, call it User (so many-to-many tables keep the same name) and set db_table='auth_user' (so it uses the same table)
Delete all Migrations File(except for __init__.py)
Delete all entry from table django_migrations
Create all migrations file using python manage.py makemigrations
Run fake migrations by python manage.py migrate --fake
Unset db_table, make other changes to the custom model, generate migrations, apply them
But if you are just starting, then delete the DB and migrations files in migration directory except for __init__.py. Then create a new DB, create new set of migrations by python manage.py makemigrations and apply migrations by python manage.py migrate.
And for references in other models, you can reference them to settings.AUTH_USER_MODEL to avoid any future problems. For example:
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.DO_NOTHING)
It will automatically reference to the current User Model.
Here is a working code
Use the below code snippets in any of your models.py
models.py
from django.contrib.auth.models import User
User._meta.get_field('email')._unique = True
django version : 3.0.2
Reference : Django auth.user with unique email
Working Code for Django 3.1
models.py
from django.contrib.auth.models import User
User._meta.get_field('email')._unique = True
SETTINGS.PY
AUTHENTICATION_BACKENDS = [
'django.contrib.auth.backends.ModelBackend'
]
There is a great example of this in Django's docs - https://docs.djangoproject.com/en/2.1/topics/auth/customizing/#a-full-example.
You have to declare the email field in your AbstractBaseUser model as unique=True.
class MyUser(AbstractBaseUser):
email = models.EmailField(
verbose_name='email address',
max_length=255,
unique=True,
)
date_of_birth = models.DateField()
is_active = models.BooleanField(default=True)
is_admin = models.BooleanField(default=False)
Easy way:
you can user signal
Example
from django.db.models.signals import post_save, pre_save
from django.dispatch import receiver
from django.contrib.auth.models import User
from django.forms import ValidationError
#receiver(pre_save, sender=User)
def check_email(sender, instance, **kwargs):
email = instance.email
if sender.objects.filter(email=email).exclude(username=instance.username).exists():
raise ValidationError('Email Already Exists')
You might be interested in:
django-user-unique-email
Reusable User model with required unique email field and mid-project support.
It defines custom User model reusing of the original table (auth_user) if exists. If needed (when added to existing project), it recreates history of applied migrations in the correct order.
I'll appreciate any feedback.
A better way of doing then using AbstractBaseUser
#forms.py
from django.core.exceptions import ValidationError
from django.contrib.auth.models import User
from django.contrib.auth.form import UserCreationForm
from some_app.validators import validate_email
def validate_email(value):
if User.objects.filter(email = value).exists():
raise ValidationError((f"{value} is taken."),params = {'value':value})
class UserRegistrationForm(UserCreationForm):
email = forms.EmailField(validators = [validate_email])
class Meta:
model = User
fields = ['username', 'email', 'password1', 'password2']
In case of use CustomUser model inherit from AbstractBaseUser you can override the full_clean() method to validate unique constraints on the model fields you specified unique=True. This is safer than form (i.e. FormClass) validation.
Example:
from django.contrib.auth.models import AbstractBaseUser
from django.db import models
class CustomUser(AbstractBaseUser):
email = models.EmailField(unique=True)
# ...
def full_clean(self, **kwargs):
"""
Call clean_fields(), clean(), and validate_unique() on the model.
Raise a ValidationError for any errors that occur.
"""
super().full_clean()
Note: Tested on Django 3.1
Improvement for solution with form validation
Instead of raising a ValidationError, it would be better to use the add_error method so that all errors of the forms are sent, and not only the one raised by ValidationError.
class SignUpForm(UserCreationForm):
email = forms.EmailField(max_length=254, help_text='Required. Inform a valid email address.')
class Meta:
model = User
fields = ('username', 'email', 'password1', 'password2', )
def clean(self):
cleaned_data = super().clean()
email = cleaned_data.get('email')
if User.objects.filter(email=email).exists():
msg = 'A user with that email already exists.'
self.add_error('email', msg)
return self.cleaned_data
You can edit model in meta as follow
Note: This will not update the original model
class SignUpForm(UserCreationForm):
email = forms.EmailField(required=True)
class Meta:
model = User
model._meta.get_field('email')._unique = True
fields = ("username", "email", "password1", "password2")

Django: OneToOneField has no attribute model

I am trying to create profile system where my users django username will be their username on the site, and also allow them to create a bio. However when I try to migrate, I get this error:
AttributeError: 'OneToOneField' object has no attribute 'model'
Here is my models.py files:
from django.db import models
from django.contrib.auth.models import User
class UserProfile(models.Model):
user = models.OneToOneField(User, unique=True)
bio = models.TextField(null=True)
slug = models.SlugField(default=user)
def __unicode__(self):
return "%s's profile" % self.user
def create_profile(sender, instance, created, **kwargs):
if created:
profile, created = UserProfile.objects.get_or_create(user=instance)
# Signal while saving user
from django.db.models.signals import post_save
post_save.connect(create_profile, sender=User)
And here is my admin.py:
from django.contrib import admin
from profiles.models import UserProfile
class UserProfileAdmin(admin.ModelAdmin):
list_display = ["user"]
admin.site.register(UserProfile, UserProfileAdmin)
Anyone know what the problem is? Thanks.
Try without importing the User model from django.contrib.auth.
Remove (or comment out) the User import line, the signal binding, and switch to string based model specification of relation in OneToOneField. The example of string based relation specification can be found in the docs.
This looks like a circular dependency thing.
If you'd still get an error, try to remove stuff you don't need from INSTALLED_APPS.

Extending User model in django

I am trying to register a user in django extending the auth module. I read from here and there and now I am all over the place and dont know what I am doing.
Initially what I wanted to do is register a user with information name, email, mobile and password. since mobile is not in the default django auth I tried to extend auth.
here is the code.
models.py
from django.db import models
from django.contrib.auth.models import User
from django.db.models.signals import post_save
class CustomerUserProfile(models.Model):
mobile = models.CharField(max_length = 20)
user = models.ForeignKey(User, unique=True)
def __str__(self):
return "%s's profile" % self.user
def create_user_profile(sender, instance, created, **kwargs):
if created:
profile, created = CustomerUserProfile.objects.get_or_create(user=instance)
post_save.connect(create_user_profile, sender=User)
forms.py
from django import forms
from django.contrib.auth.models import User
from django.contrib.auth.forms import UserCreationForm
from neededform.models import CustomerUserProfile
class CustomerRegistrationForm(UserCreationForm):
email = forms.EmailField(required = True)
mobile = forms.CharField(max_length = 20)
class Meta:
model = User
fields = ('username','email','mobile','password1','password2')
views.py
from django.shortcuts import render_to_response
from django.http import HttpResponseRedirect
from django.template import RequestContext
from django.core.context_processors import csrf
from neededform.forms import CustomerRegistrationForm
from neededform.models import CustomerUserProfile
from django.contrib.auth.models import User
def register(request):
if request.method == 'POST':
form = CustomerRegistrationForm(request.POST)
if form.is_valid():
f = form.save()
return HttpResponseRedirect('/registered/')
else:
args = {}
args.update(csrf(request))
args['form'] = CustomerRegistrationForm()
return render_to_response('User_Registration.html', args ,context_instance = RequestContext(request))
This code upon execution makes an entry in auth_user table and an entry in CustomerUserProfile but the mobile coloumn is blank always.
what else am I suppose to add in the code ?
PS. please can some one explain me what is this code doing
def create_user_profile(sender, instance, created, **kwargs):
if created:
profile, created = CustomerUserProfile.objects.get_or_create(user=instance)
post_save.connect(create_user_profile, sender=User)
as I copied it from somewhere I want to know how this works actually.
Thank you.
This is a 2-year old question but I am surprised that there was no correct answer.
The mobile field is not saved in your profile, simply because you create your CustomerUserProfile via the post_save signal, and the mobile field is not forwarded by the post_save signal.
I would suggest to simply update your CustomerUserProfile when you handle form.is_valid() in your register function.

Categories

Resources