I'd like to have 3 differents table and use them to authenticate.
The Contact will authenticate himself with his phone number, ChainStore with his identifier and User (django user admin) will have his normal behavior (with username). ChainStore and Contact have forms to authenticate, how can i do ?
Here is my models:
class Contact(models.Model):
id = models.UUIDField(primary_key=True, default=uuid4)
first_name = models.CharField(max_length=10)
last_name = models.CharField(max_length=10)
address = models.ForeignKey(
Address, on_delete=models.CASCADE, blank=True, null=True
)
email = models.CharField(blank=True, null=True, max_length=140)
mobile = models.CharField(max_length=12, unique=True)
password = models.CharField(max_length=128)
optin_email = models.BooleanField(default=False)
optin_sms = models.BooleanField(default=False)
class ChainStore(models.Model):
id = models.UUIDField(primary_key=True, default=uuid4)
password = models.CharField(max_length=64)
last_login = models.DateTimeField(blank=True, null=True)
rules = models.JSONField(default=dict)
I've tried to use inheritance with AbstractBaseUser but i had problems like
auth.User.groups: (fields.E304) Reverse accessor for 'User.groups' clashes with reverse accessor for 'Contact.groups'
My Forms:
class ContactSignUp(forms.Form):
first_name = forms.CharField(max_length=10)
last_name = forms.CharField(max_length=10)
email = forms.EmailField()
mobile = forms.RegexField(max_length=12)
password = forms.CharField(max_length=32)
password_verification = forms.CharField(max_length=32)
class ContactAuth(forms.Form):
mobile = forms.CharField(max_length=12)
password = forms.CharField(max_length=32)
My views:
class ContactSignUpView(FormView):
form_class = ContactSignUp
model = Contact
template_name = "sign_up.html"
success_url = "/"
def form_valid(self, form):
"""This method is called when valid form data has been POSTed."""
Contact(
first_name=form.cleaned_data["first_name"],
last_name=form.cleaned_data["last_name"],
email=form.cleaned_data["email"],
mobile=form.cleaned_data["mobile"],
password=form.cleaned_data["password"]
).save()
return super().form_valid(form)
class ContactAuthView(FormView):
form_class = forms.ContactAuth
model = Contact
template_name = "auth.html"
success_url = "/"
def form_valid(self, form):
"""This method is called when valid form data has been POSTed."""
user = ContactBackend().authenticate(
form.cleaned_data["mobile"],
form.cleaned_data["password"],
)
return super().form_valid(form)
My backend:
class ContactBackend(BaseBackend):
"""
Mobile Authentication Backend
Allows a user to sign in using an mobile/password pair rather than
a username/password pair.
"""
def authenticate(self, username=None, password=None):
try:
contact = models.Contact.objects.get(mobile=username)
if check_password(password, contact.password): # Imported from django.contrib.auth.hashers
return contact
except models.Contact.DoesNotExist:
return None
def get_user(self, user_id):
try:
return models.Contact.objects.get(pk=user_id)
except models.Contact.DoesNotExist:
return None
And finally my settings:
AUTH_USER_MODEL = "auth.User"
AUTHENTICATION_BACKENDS = [
"django.contrib.auth.backends.ModelBackend",
"contact.backends.ContactBackend"
]
Related
I am trying to get a list of all pathologists in my system. I need to filter the user on 2 basis i-e is_pathologist and Lab_Id=request.data[email]
I have tried switching between filter and get but then I get
Authentication.models.User.MultipleObjectsReturned: get() returned more than one User -- it returned 12!
Error traceback here
This is the code of my view
#api_view(['POST'])
def getAllPathologists(request):
user = get_user_model().objects.get(is_pathologist=True)
# If user exists, get the employee
print("user is: ", user)
pathologist = Employee.objects.get(user=user.email, Lab_Id=request.data['email'])
pathologistSerializer = EmployeeSerializer(pathologist, many=True)
return Response(pathologistSerializer.data)
This is user model
class User(AbstractUser):
# Add additional fields here
id = None
email = models.EmailField(max_length=254, primary_key=True)
name = models.CharField(max_length=100)
password = models.CharField(max_length=100)
contact_number = models.CharField(max_length=100)
is_patient = models.BooleanField(default=False)
is_doctor = models.BooleanField(default=False)
is_homesampler = models.BooleanField(default=False)
is_pathologist = models.BooleanField(default=False)
is_admin = models.BooleanField(default=False)
is_staff = models.BooleanField(default=False)
is_active = models.BooleanField(default=True)
is_lab = models.BooleanField(default=False)
date_joined = models.DateTimeField(auto_now=True,editable=False)
last_login = models.DateTimeField(auto_now=True)
first_name = None
last_name = None
username = None
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['name', 'password']
objects = CustomUserManager()
def __str__(self):
return self.email
# Ensure that the password is hashed before saving it to the database
def save(self, *args, **kwargs):
self.password = make_password(self.password)
super(User, self).save(*args, **kwargs)
def has_perm(self, perm, obj=None):
return self.is_superuser
This is Employee model
class Employee(models.Model):
user = models.OneToOneField(
get_user_model(), on_delete=models.CASCADE, primary_key=True)
CNIC = models.CharField(max_length=100, unique=True)
Lab_Id = models.ForeignKey(Lab, on_delete=models.CASCADE)
def __str__(self):
return self.user.name
This is employee serializer
class EmployeeSerializer(serializers.ModelSerializer):
userData = UserSerializer(read_only=True, source='user')
email = serializers.EmailField(write_only=True)
password = serializers.CharField(write_only=True)
name = serializers.CharField(write_only=True)
contact_number = serializers.CharField(write_only=True)
is_homesampler = serializers.BooleanField(write_only=True)
class Meta:
model = Employee
# fields = " __all__"
fields = ["CNIC", "Lab_Id", "userData",
"name", "contact_number", "email", "password", "is_homesampler"]
def create(self, validated_data):
print("validated data = ", validated_data)
email = validated_data.pop("email")
password = validated_data.pop("password")
name = validated_data.pop("name")
contact_number = validated_data.pop("contact_number")
is_homesampler = validated_data.pop("is_homesampler")
user = get_user_model().objects.create_user(
email=email, password=password, name=name, contact_number=contact_number)
if (is_homesampler):
user.is_homesampler = True
else:
user.is_pathologist = True
user.save()
EmployeeObj = Employee.objects.create(user=user, **validated_data)
return EmployeeObj
You are getting objects and querysets conflated, filter() will return a queryset whereas get() tries to return an object. Below are the reasons for your errors:
The reason for your error with filter() is that a queryset is essentially a group of user objects. The queryset itself has no attribute email, but each user object within the group would. You therefore need to extract a single user from the queryset using first() or last(), for example.
Your error with get() is that your parameters are too broad and thus 12 users are returned. You need to adjust your code to handle this, it's usually done with either a try/except block or using the get_object_or_404 Django shortcut. Once you successfully get the user object, you can call user.email without issue.
Here is My Seller Model which has User as OneToOneField
#models.py
.
.
class CompanyProfile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, related_name="user")
company = models.CharField(max_length=255)
pincode = models.IntegerField()
city = models.ForeignKey(City, on_delete=models.CASCADE)
address = models.TextField()
profileActive = models.BooleanField(default=False)
.
.
Purchase Model
#models.py
class PurchaseOrder(models.Model):
company = models.ForeignKey(CompanyProfile, on_delete=models.CASCADE)
message = models.TextField(blank=True, null=True)
expectedDelivery = models.DateField(blank=True, null=True)
isCancel = models.BooleanField(default=False))
updated_at = models.DateTimeField(auto_now=True)
#Custom permission_classes
class SellerCompanyActive(BasePermission):
message = 'Only Seller with activated company account is allowed'
def has_permission(self, request, view):
user = AuthUser.objects.get(id=request.user.id)
if user.is_seller:
try:
company = CompanyProfile.objects.get(user=user)
if company.profileActive:
return True
else:
self.message = "Company profile is not active"
except CompanyProfile.DoesNotExist:
self.message = "Company not found first create company"
return False
In ModelViewSet
#views.py
class SellerPurchaseOrder(viewsets.ModelViewSet):
queryset = PurchaseOrder.objects.all()
serializer_class = PurchaseOrderSerializer
authorization_classes = [TokenAuthentication]
permission_classes = [IsAuthenticated, SellerCompanyActive]
def get_queryset(self):
user = self.request.user
company = CompanyProfile.objects.get(user=user)
return self.queryset.filter(company=company)
Now here I always had to use this
user = self.request.user
company = CompanyProfile.objects.get(user=user)
As their are lot of other views also, Is their any other way to send data from my custom permission_classes i.e from SellerCompanyActive to direct SellerPurchaseOrder->get_queryset
You can set message attribute to be a dict, something like this:
class SellerCompanyActive(BasePermission):
message = {'error': 'Only Seller with activated company account is allowed'}
def has_permission(self, request, view):
...
you can add more fields to the dictionary to send more data
I'm making a login for my app, with a custom user model and form, using an e-mail as username field in the model, and asking for email and password during the login process. The problem is that no matches are found in the authenticate method in my view, so I can't log in my users. Here is the code:
user model
class usuarios(AbstractBaseUser):
nombre = models.CharField(max_length=32, null=False)
apellido = models.CharField(max_length=32, null=False)
correo = models.EmailField(null=False, unique=True)
password = models.CharField(max_length=200, null=False)
fecha_nacimiento = models.DateField(null=False)
USERNAME_FIELD = 'correo'
objects = userManager()
def __str__(self):
return self.correo+' '+self.nombre+' '+self.apellido
login form
class loginForm(forms.Form):
correo = forms.EmailField()
password = forms.CharField(
widget=forms.PasswordInput()
)
class Meta:
model = usuarios
fields = ("correo","password")
login view
class usuario_login(FormView):
template_name = "user/usuario_login.html"
form_class = loginForm
success_url = reverse_lazy('user:crear')
def form_valid(self, form):
usuario = authenticate(
username=form.cleaned_data['correo'],
password=form.cleaned_data['password']
)
if usuario is not None:
login(self.request, usuario)
else:
return HttpResponseRedirect(
reverse_lazy('user:login')
)
return super(usuario_login, self).form_valid(form)
As you can see, in the login view I try to authenticate username (that's the email) and password before doing the login. I appreciate any help and then you.
Nevermind, the problem was that as I changed the AbstractBaseUser, I had to change the authentication user model too, so I just added this in settings as the documentation states:
AUTH_USER_MODEL = 'user.usuarios'
I have created 3 custom user models. However only one user under the models Users() is able to login in into a sells dashboard that I have created. I want the two user namelly, Buyer() and Supplier() to be able to login to the dashboard but not to the admin area. The following is my code. Please help me see the error.
# models.py
# These are my three custom models
from django.db import models
from django.contrib.auth.models import AbstractUser, AbstractBaseUser, UserManager, BaseUserManager, PermissionsMixin
from django.conf import settings
# Superuser model
class Users(AbstractUser):
username = models.CharField(max_length=25, unique=True)
email = models.EmailField(unique=True, null="True")
objects = UserManager()
USERNAME_FIELD = 'username'
REQUIRED_FIELDS = ['email']
# Returns username
def __str__(self):
return self.username
# Supplier user model
class Supplier(AbstractBaseUser):
sname = models.CharField(max_length=255, verbose_name='Supplier Name', unique=True)
phone_number = models.CharField(max_length=255, verbose_name='Phone Number')
email_address = models.CharField(max_length=255, verbose_name='Email Address', null=True)
physical_address = models.CharField(max_length=255, verbose_name='Physical Address')
description = models.TextField(max_length=255, verbose_name='Describe yourself')
is_active = models.BooleanField(default=True)
objects = Users()
USERNAME_FIELD = 'sname'
def __str__(self):
return self.sname
# This model save inventory of a supplier
class Inventory(models.Model):
pname = models.CharField(max_length=255, verbose_name='Product Name')
quantity = models.PositiveIntegerField(verbose_name='Quantity (kgs)')
measurement = models.CharField(max_length=255, verbose_name='Measurement')
orginal_price = models.PositiveIntegerField(verbose_name='Original Price')
commission = models.PositiveIntegerField(verbose_name='Commission')
selling_price = models.PositiveIntegerField(verbose_name='Selling Price (MWK)')
supplier = models.ForeignKey(Supplier, on_delete=models.CASCADE, verbose_name='Supplier')
def __str__(self):
return self.pname
# This model saves the transaction a buyer has made
class Transaction(models.Model):
cust_name = models.CharField(max_length=255, verbose_name='Customer Name')
pid = models.ForeignKey(Inventory, on_delete=models.CASCADE, verbose_name='Product')
quantity_r = models.PositiveIntegerField(verbose_name='Quantity (KGS)')
success = models.BooleanField(default=False)
uid = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, verbose_name='User Id')
# Calculates actual price
def actual_price(self):
return int(self.quantity_r) * int(self.pid.selling_price)
# Returns customer name
def __str__(self):
return self.cust_name
# Calculates total costs of suppliers products
class SupplierProductCostView(models.Model):
id = models.PositiveIntegerField(primary_key=True)
sname = models.CharField(max_length=255)
price = models.PositiveIntegerField()
class Meta:
db_table = 'home_supplierproductcostview'
managed = False
def __str__(self):
return str(self.id) + ' ' + self.sname + ' ' + str(self.price)
# Buyer user model
class Buyer(AbstractBaseUser):
username = models.CharField(max_length=255, unique=True)
company_name = models.CharField(max_length=255, verbose_name='Company Name')
phone_number = models.CharField(max_length=255, verbose_name='Phone Number')
email_address = models.CharField(max_length=255, verbose_name='Email Address')
address = models.CharField(max_length=500, verbose_name='Physical Address')
description = models.TextField(max_length=255, verbose_name='Describe your company')
is_active = models.BooleanField(default=True)
objects = Users()
USERNAME_FIELD = 'username'
# Returns username
def __str__(self):
return self.username
# forms.py
# This is my registration and login form
from django import forms
from .models import Supplier, Buyer
# Custom supplier registration form
class SupplierRegistrationForm(forms.ModelForm):
password = forms.CharField(label='Password', widget=forms.PasswordInput)
password2 = forms.CharField(label='Repeat password', widget=forms.PasswordInput)
# Supplier registration form fields
class Meta:
model = Supplier
fields = ('sname', 'phone_number', 'email_address', 'physical_address', 'description')
# Password check
def clean_password2(self):
cd = self.cleaned_data
if cd['password'] != cd['password2']:
raise forms.ValidationError('Passwords don\'t match.')
return cd['password2']
# Custom buyer registration form
class BuyerRegistrationForm(forms.ModelForm):
password = forms.CharField(label='Password', widget=forms.PasswordInput)
password2 = forms.CharField(label='Repeat password', widget=forms.PasswordInput)
# Buyer registration form fields
class Meta:
model = Buyer
fields = ('username', 'company_name', 'phone_number', 'email_address', 'address', 'description')
# Password check
def clean_password2(self):
cd = self.cleaned_data
if cd['password'] != cd['password2']:
raise forms.ValidationError('Passwords don\'t match.')
return cd['password2']
# Login form
class LoginForm(forms.Form):
username = forms.CharField()
password = forms.CharField(widget=forms.PasswordInput)
#views.py
# These are my login and logout views
from django.shortcuts import render, redirect, reverse
from django.contrib.auth import authenticate, login, logout
from users.forms import LoginForm
# User logs in to dashboard
def index(request):
if request.method == 'POST':
loginform = LoginForm(request.POST)
# Check if form is valid
if loginform.is_valid():
username = loginform.cleaned_data['username']
password = loginform.cleaned_data['password']
user = authenticate(username=username, password = password)
# If user does not exist
if user is not None:
login(request, user)
return redirect(reverse('home'))
else:
context = {
'form':loginform,
'error': 'Could not login, Please try again...',
}
return render(request, 'users/index.html', context)
loginform = LoginForm()
context = {
'form' : loginform,
}
# Returns login form
return render(request, 'users/index.html', context)
# Logout
def logout_user(request):
logout(request)
return redirect(reverse('login'))
I extended the custom user model with a OneToOneField but now I Am getting this error "django.db.utils.IntegrityError: null value in column "users_id" violates not-null constraint" apparently there is something wrong with my registration view. Can you help?
# Buyer registration view
def buyer_form_register(request):
if request.method == 'POST':
buyer_form = BuyerRegistrationForm(request.POST)
if buyer_form.is_valid():
# Create a new user object but avoid saving it yet
new_user = buyer_form.save(commit=False)
# Set the chosen password
new_user.set_password(
buyer_form.cleaned_data['password'])
# Save the User object
new_user.save()
# Create the user profile
return render(request, 'register/register_done.html')
else:
buyer_form = BuyerRegistrationForm()
return render(request, 'register/register_buyer.html', {'buyer_form': buyer_form})
# Edited model
class Buyer(AbstractBaseUser):
# New field
users = models.OneToOneField(Users, on_delete=models.CASCADE)
username = models.CharField(max_length=255, unique=True)
company_name = models.CharField(max_length=255, verbose_name='Company Name')
phone_number = models.CharField(max_length=255, verbose_name='Phone Number')
email_address = models.CharField(max_length=255, verbose_name='Email Address')
address = models.CharField(max_length=500, verbose_name='Physical Address')
description = models.TextField(max_length=255, verbose_name='Describe your company')
objects = Users()
USERNAME_FIELD = 'username'
def __str__(self):
return self.username
You can't have 3 custom user models, in settings.py you can only set AUTH_USER_MODEL to one model.
Extend your user model with profiles (OneToOneField) instead to differentiate between the different user types.
I have a django app with which every registered user can create categories. For the authentication I am using django-all-auth. My models.py looks like this:
class Profile(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
user = models.OneToOneField(User, on_delete=models.CASCADE)
create_date = models.DateTimeField('date added', auto_now_add=True)
modify_date = models.DateTimeField('date modified', default=timezone.now)
class Category(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
user = models.ForeignKey(Profile, on_delete=models.CASCADE)
name = models.CharField(max_length=200, unique=True)
create_date = models.DateTimeField('date added', auto_now_add=True)
modify_date = models.DateTimeField('date modified', default=timezone.now)
On the index page the user can see the created categories and create new ones.
The views.py:
def CategoryView(request):
user = 0
if request.user.is_authenticated():
user = request.user
form = CategoryNameForm()
form.user = user
context = {
'categories': Category.objects.all(),
'form': form,
'user':user,
}
if request.method == 'POST':
form = CategoryNameForm(request.POST)
form.user = user
if form.is_valid():
form.save()
return render(request, 'myapp/index.html',context)
forms.py:
class CategoryNameForm(forms.ModelForm):
class Meta:
model = Category
fields = ('name',)
The authentication works. So I was thinking to just put pass the user field into the form :
class CategoryNameForm(forms.ModelForm):
class Meta:
model = Category
fields = ('name','user',)
hide it and then, just select it via JS since the user is in the context. I was just wondering if there is an easier way. This form.user = user for some reason didn't work, I get a NOT NULL constraint failure
There are couple of ways but here is one:
class CategoryNameForm(forms.ModelForm):
class Meta:
model = Category
fields = ('name',) # take out user you don't need it here
def save(self, **kwargs):
user = kwargs.pop('user')
instance = super(CategoryNameForm, self).save(**kwargs)
instance.user = user
instance.save()
return instance
Then in view:
if form.is_valid():
form.save(user=request.user, commit=False)
Make sure your CategoryView is only accessible by authenticated user. Otherwise you will still get NOT NULL constraint failure for user.