How do i add custom fields to UserManager in Django - python

I am trying to create a user profile, i followed through a tutorial which has registration for only username, email and password but i want to be able to add other custom fields.
What i did:
Models.py:
class UserManager(BaseUserManager):
def create_user(self, username, email, password=None,):
if username is None:
raise TypeError('User should have a userame')
if email is None:
raise TypeError('Users should have a Email')
user = self.model(username=username , email = self.normalize_email(email))
user.set_password(password)
user.save()
return user
def create_superuser(self, username, email, password=None):
if password is None:
raise TypeError('User should have a password')
user=self.create_user(username,email,password)
user.is_superuser = True
user.is_staff = True
user.save()
return user
class User(models.Model):
dso = models.ForeignKey(Dso,related_name='dso',default=NULL,blank=False,on_delete=models.CASCADE)
name = models.CharField(max_length=70, blank=False, default='')
email = models.EmailField(max_length=70, blank=False, default='')
password = models.CharField(max_length=70, blank=False, default='')
address = models.CharField(max_length=70, blank=False, default='')
roleId = models.IntegerField(blank=False, default='1')
isActive = models.BooleanField(blank=False, default=True)
customerId = models.CharField(max_length=70, blank=False, default='')
dateJoined = models.DateTimeField(auto_now_add=False, blank=False, default=NULL)
#property
def energy_data(self):
energydata = EnergyData.objects.filter(customerId=self.customerId).first()
return energydata
Serializers.py:
class RegisterSerializer(serializers.ModelSerializer):
password = serializers.CharField(max_length = 68, min_length=6, write_only = True)
class Meta:
model=User
fields=['email','username','password','name','address','customerId',
'dso', 'roleId']
def validate(self, attrs):
email = attrs.get('email', '')
username = attrs.get('username', '')
if not len(username) >= 4:
raise serializers.ValidationError('Username must be morethan 4 letters or characters')
return attrs
def create(self, validated_data):
return User.objects.create_user(**validated_data)
Views.py:
class RegisterView(generics.GenericAPIView):
serializer_class= RegisterSerializer
def post(self, request):
user = request.data
serializer = self.serializer_class(data=user)
serializer.is_valid(raise_exception=True)
serializer.save()
user_data = serializer.data
user= User.objects.get(email=user_data['email'])
token = RefreshToken.for_user(user).access_token
current_site = get_current_site(request).domain
relativeLink = reverse('email-verify')
absolute_url = 'http://'+current_site+relativeLink+"?token="+str(token)
email_body= 'Hi '+ user.username + ' Use this link below to verify your email \n'+ absolute_url
data = {'email_subject': 'Verify Your Email', 'email_body': email_body , 'to_email': user.email}
Util.send_email(data)
return Response(user_data, status = status.HTTP_201_CREATED)
URL Path:
path('register/', RegisterView.as_view(), name="register" )
When i do this and try to test i get the error, 'UserManager.create_user() got an unexpected keyword argument 'name''
Please kindly help as i am new to django rest frameworrk.

In your serializers.py you have the fields list that includes the variable 'name', but it is never defined in the models.py
Try to change serializers.py
fields=['email','username','password','address','customerId',
'dso', 'roleId']
And modify the variable name in models.py to be username instead of name
username = models.CharField(max_length=70, blank=False, default='')

Based on my experience with Django having a Model called "user" is going to create problems at some point since Django already have a User model pre-installed in the backend.
I know this is not the exact answer you were looking for, this will probably spare you a headache in the future.
To create a user profile I created the following model linking the User model with a OneToOneField.
class Profile(models.Model):
user = models.OneToOneField(User, null=True, on_delete=models.CASCADE)
bio = models.TextField()
profile_pic = models.ImageField(null=True, blank=True,upload_to="images/")
def __str__(self):
return str(self.user)
and obvioulsy imported
from django.contrib.auth.models import User
As a result,
I would remove your User model
Import "from django.contrib.auth.models import User"
add to the first line of your
UserManager Model, I would add
"user = models.OneToOneField(User, null=True, on_delete=models.CASCADE)

Related

How to define the foreign key relationship?

I'm new to django and the database concepts.
I'm getting the 1042 error code when I'm trying to register student via frontend and don't know where my mistake is
if anyone can help that'd be great.
I tried different ways but none was beneficial.
I can add the student via the admin panel of django but cannot do the same via frontend.
models.py
class Session(models.Model):
session = models.CharField(max_length=150, default=" ")
def __str__(self):
return self.session
def get_absolute_url(self):
return reverse('datapage')
class Student(models.Model):
user = models.OneToOneField(User, on_delete = models.CASCADE, primary_key = True)
phone_number = models.CharField(max_length=20, blank = True)
semester = models.CharField(max_length=20, blank = True)
session_year = models.ForeignKey(Session, on_delete = models.CASCADE)
def __str__(self):
return str(self.user)
forms.py
session_years = Session.objects.all()
session_year_list = []
for session_year in session_years:
single_session_year = (session_year.id, session_year.session)
session_year_list.append(single_session_year)
class StudentSignUpForm(UserCreationForm):
first_name = forms.CharField(required=True)
last_name = forms.CharField(required=True)
email = forms.EmailField(required=True)
username = forms.CharField(required=True)
phone_number = forms.CharField(required=True)
semester = forms.CharField(required=True)
def clean_email(self):
email = self.cleaned_data.get('email')
email = email.lower()
if User.objects.filter(email__iexact=email).exists():
raise forms.ValidationError('A user has already registered using this email')
return email
def clean_username(self):
username = self.cleaned_data.get('username')
if User.objects.filter(email__iexact=username).exists():
raise forms.ValidationError('A user has already registered using this username')
return username
class Meta(UserCreationForm.Meta):
model = User
fields = ['first_name', 'last_name', 'email', 'username']
#transaction.atomic
def save(self):
user = super().save(commit=False)
user.is_student = True
user.first_name = self.cleaned_data.get('first_name')
user.last_name = self.cleaned_data.get('last_name')
user.email = self.cleaned_data.get('email')
user.username = self.cleaned_data.get('username')
user.save()
student = Student.objects.create(user=user)
student.phone_number=self.cleaned_data.get('phone_number')
student.semester=self.cleaned_data.get('semester')
student.session_year=self.cleaned_data.get('session_year')
student.save()
return user
views.py
class StudentRegister(LoggedInRedirectMixin, CreateView):
model = User
form_class = StudentSignUpForm
template_name = 'registration/user_register.html'
def form_valid(self, form_class):
user = form_class.save()
return redirect('datapage')
#Vatsai Vohera - Correct me if I am wrong.
session = Session.objects.get(session=self.cleaned_data.get('session_year')
student.session_year = session
You are adding your requested data into session_year field which is not working rather you have to save your requested data to your model Session field session. I hope this will work for you.
You can use related_name='...' field in your ForeignKey attribute
and also add this:
session_year = models.ForeignKey(Session,related_name='some thing', on_delete = models.CASCADE, null = True)
then you have to call session_year by related name

Django saving the registration extends AbstractBaseUser

Good day SO.
I am new to Django and having troubles with something basic. What I am trying to do is when I click on register, I want to create an Account and at the same time, a company account.
When I click on sumbit, the template returns my Account(the OneToOneField) This field is required.
Though my methods might be not aligned with good practice, but I hope that you can help me with this. I have been trying to check with other resources for two days but I can't seem to find the solution to my concern.
Here is my forms.py:
from django import forms
from django.contrib.auth.forms import UserCreationForm
from .models import Account, CompanyAccount
class AccountCreationForm(UserCreationForm):
email = forms.EmailField(max_length=60, help_text="Required")
class Meta:
model = Account
fields = ("email", "username", "password1", "password2", "account_type")
class CompanyAccountForm(forms.ModelForm):
class Meta:
model = CompanyAccount
fields = "__all__"
my models.py:
from django.db import models
from django.contrib.auth.models import AbstractBaseUser, BaseUserManager
# Create your models here.
class MyAccountManager(BaseUserManager):
def create_user(self, email, username, account_type, password):
if not email:
raise ValueError("Users must have an Email Address")
if not username:
raise ValueError("Users must have an Username")
if not account_type:
raise ValueError("Users must have an Account Type")
user = self.model(
email=self.normalize_email(email),
username=username,
password=password,
account_type=account_type,
)
user.set_password(password)
user.save(using=self._db)
return user
def create_superuser(self, email, username, account_type, password):
user = self.create_user(
email=self.normalize_email(email),
username=username,
password=password,
account_type=account_type,
)
user.is_admin = True
user.is_staff = True
user.is_superuser = True
user.save(using=self._db)
return user
class Account(AbstractBaseUser):
email = models.EmailField(verbose_name='Email', max_length=60, default='', null=False, unique=True)
username = models.CharField(verbose_name='Username', max_length=50, default='', null=False, unique=True)
date_joined = models.DateTimeField(verbose_name='date joined', auto_now_add=True)
last_joined = models.DateTimeField(verbose_name='last joined', auto_now_add=True)
is_admin = models.BooleanField(default=False)
is_active = models.BooleanField(default=True)
is_staff = models.BooleanField(default=False)
is_superuser = models.BooleanField(default=False)
ACCOUNT_TYPE_CHOICES = (
(1, 'Applicant'),
(2, 'Company'),
(3, 'Client'),
)
account_type = models.PositiveSmallIntegerField(default=0, choices=ACCOUNT_TYPE_CHOICES)
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['username', 'account_type', ]
objects = MyAccountManager()
def __str__(self):
return self.username
def has_perm(self, perm, obj=None):
return self.is_admin
def has_module_perms(self, app_label):
return True
class CompanyAccount(models.Model):
account = models.OneToOneField(Account, on_delete=models.CASCADE)
created = models.DateTimeField(auto_now_add=True)
company_name = models.CharField(max_length=100, default='', null=False)
views.py
context = {}
if request.method == "POST":
rForm = AccountCreationForm(request.POST)
cForm = CompanyAccountForm(request.POST)
if rForm.is_valid() and cForm.is_valid():
rForm.save()
cForm.save()
else:
rForm = AccountCreationForm()
context['rForm'] = rForm
cForm = CompanyAccountForm()
context['cForm'] = cForm
return render(request, 'registration/company_registration_form.html', context)
If you want your model's field to be allowed to be empty when submitting forms, add blank=True:
account = models.OneToOneField(Account, on_delete=models.CASCADE, blank=True)
https://docs.djangoproject.com/en/3.1/ref/models/fields/

Django CustomUser redefine password field

Suddenly I had the need to use one of my existing models for authentication. I know that there are several approaches how I can do that, but here I'm wondering is it possible to redefine password field with a way I choosed?
To be more specific: I want django to know that my access_key field is a User password field.
But current code presupposes that I gonna add another field password to Company model.
from django.contrib.auth.models import BaseUserManager, AbstractBaseUser
from django.db import models
from django_extensions.db.fields.encrypted import EncryptedCharField
class CompanyManager(BaseUserManager):
def create_user(self, name, email, access_key=None):
if not email:
raise ValueError('Company must have an email address')
company = self.model(
email=CompanyManager.normalize_email(email),
)
if access_key is not None:
company.access_key = access_key
company.save(using=self._db)
return company
def create_superuser(self, name, email, access_key):
company = self.create_user(name, email, access_key)
company.is_admin = True
company.save()
return company
class Company(AbstractBaseUser):
name = models.CharField(max_length=64, blank=False, null=False,
unique=True)
email = models.CharField(max_length=254, blank=False, null=False,
unique=True)
access_key = EncryptedCharField(max_length=18)
verification_key = EncryptedCharField(max_length=20, blank=True,
null=True)
is_confirmed = models.BooleanField(default=False)
is_admin = models.BooleanField(default=False)
callback_url = models.URLField(blank=False, null=True)
objects = CompanyManager()
USERNAME_FIELD = 'name'
REQUIRED_FIELDS = ['email']

No module named base_user - Django

In one of my models files in my Django project, I am trying to import AbstractBaseUser, and BaseUserManager from django.contrib.auth.base_user. I checked my site packages, and there is definitely a base_user python file in Django's auth directory, but I am getting this error when trying to make migrations.
ImportError: No module named base_user
If I was using the wrong Django version, it wouldn't show up in my site packages Django directory correct ? Also in Pycharm, my IDE, its not underlining it red with any errors.
models.py
from django.conf import settings
from django.contrib.auth.models import AbstractBaseUser, BaseUserManager
from django.contrib.auth.hashers import get_hasher
from django.contrib.auth.models import PermissionsMixin
from django.db import models
from django.utils import timezone
from django.utils.text import slugify
class AccountUserManager(BaseUserManager):
# username is not used here, but is needed for facebook login to work correctly
def create_user(self, email, password=None, username=None, is_active=True):
if not email:
raise ValueError('Users must have an email address')
user = self.model(
email=self.normalize_email(email),
)
user.set_password(password)
user.last_login = timezone.now()
user.language_code = settings.LANGUAGE_CODE
user.is_active = is_active
user.save(using=self._db)
return user
def create_superuser(self, email, password):
user = self.create_user(email,
password=password,
)
user.is_staff = True
user.is_superuser = True
user.save(using=self._db)
return user
class Account(AbstractBaseUser, PermissionsMixin):
email = models.EmailField(unique=True, max_length=255, verbose_name='Email',
help_text='Used for login and password recovery. Is also an account\'s display name if no Name is specified.')
first_name = models.CharField(max_length=100, blank=True, null=True, verbose_name='First Name',
help_text='User\'s first name.')
last_name = models.CharField(max_length=100, blank=True, null=True, verbose_name='Last Name',
help_text='User\'s last name.')
date_joined = models.DateTimeField(auto_now_add=True, verbose_name='Date Joined')
is_active = models.BooleanField(default=True, null=False, db_index=True, verbose_name='Is Active',
help_text='Uncheck to prevent user from being allowed to login.')
is_staff = models.BooleanField(default=False, null=False, verbose_name='Is Staff',
help_text='Grants administrator privileges.')
activation_key = models.CharField(max_length=40, blank=True, null=True)
key_expires = models.DateTimeField(blank=True, null=True)
objects = AccountUserManager()
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = []
def get_full_name(self):
full_name = ''
if self.first_name is not None:
full_name += self.first_name
if self.last_name is not None:
full_name += ' ' + self.last_name
return full_name
# #property
def _full_name(self):
return self.get_full_name()
_full_name.short_description = 'Name'
full_name = property(_full_name)
def get_display_name(self):
if self.full_name.strip():
return self.full_name.rstrip()
return self.email.rstrip()
#property
def slug(self):
if self.full_name:
return slugify(self.full_name)
email_parts = self.email.split('#')
return slugify(email_parts[0])
def __unicode__(self):
return unicode(self.email)
class Meta:
verbose_name = 'User Account'
verbose_name_plural = 'User Accounts'

Django-rest: How to implement authentication and permissions for custom User models?

views.py
class UserList(generics.ListCreateAPIView):
queryset = User.objects.all()
model = User
serializer_class = UserSerializer
paginate_by = 10
def get_queryset(self):
queryset = User.objects.all()
search_query = self.request.query_params.get('user', None)
if search_query is not None:
queryset = queryset.filter(name__istartswith=search_query)
queryset = queryset.order_by('name')
return queryset
class UserDetail(generics.RetrieveUpdateDestroyAPIView):
queryset = User.objects.all()
model = User
serializer_class = UserSerializer
models.py
class UserRole(models.Model):
class Meta:
ordering = ["name"]
db_table = 'userrole'
name = models.CharField(max_length=50)
status = models.CharField(max_length=100)
class User(models.Model):
class Meta:
ordering = ["name"]
db_table = 'user'
name = models.CharField(max_length=100)
email = models.EmailField(unique=True, max_length=100)
password = models.CharField(max_length=100)
status = models.CharField(max_length=100, default='active')
roleid = models.ForeignKey(UserRole, on_delete=models.CASCADE,
default=None, blank=True, db_column='roleid')
createdby = models.CharField(max_length=100, blank=True, default="")
createdon = models.DateTimeField(blank=True, auto_now_add=True)
updatedon = models.DateTimeField(blank=True, auto_now=True)
Serializers.py
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
class UserRoleSerializer(serializers.ModelSerializer):
class Meta:
model = UserRole
I've gone through with Django-rest documentation but not able to find a reference for implementing authentication and permission for custom created Users.
Please advise.
Check Django Document for User authentication in Django and Permissions in Django Rest Framework.
Below is the sample models structure for custom BaseUser and BaseUserManager.
from django.conf import settings
from django.contrib.auth.models import BaseUserManager, AbstractBaseUser, PermissionsMixin
from django.template.defaultfilters import slugify
User = settings.AUTH_USER_MODEL
class BaseUserManager(BaseUserManager):
def create_user(self, useremail, display_name, password=None):
if not useremail:
raise ValueError('Users must have an email address')
now = timezone.now()
user = self.model(useremail=BaseUserManager.normalize_email(useremail))
user.display_name = display_name
user.email = useremail
user.profile_slug = getUniqueValue(BaseUser,slugify(useremail.split("#")[0]),field_name="profile_slug")
user.set_password(password)
user.status='A'
user.last_login = user.date_joined = now
user.save(using=self._db)
return user
def create_superuser(self, useremail, display_name, password):
user = self.create_user(useremail=useremail,
display_name=display_name, password=password)
user.email = useremail
user.display_name = display_name
user.is_superuser = True
user.is_staff = True
user.status='A'
user.save(using=self._db)
return user
class BaseUser(AbstractBaseUser, PermissionsMixin):
display_name = models.CharField(max_length=25)
profile_slug = models.CharField(max_length=25,null=True)
gender = models.CharField(max_length=1, blank=True, choices=Gender)
useremail = models.EmailField(unique=True)
is_staff = models.BooleanField(default=False)
user_status = models.CharField(max_length=1, default='A')
USERNAME_FIELD = 'useremail'
REQUIRED_FIELDS = ['display_name']
objects = BaseUserManager()
def __unicode__(self):
return self.display_name
In your permission.py, you may define permissions as:
from rest_framework import permissions
class CheckPermission(permissions.BasePermission):
def has_permission(self, request, view):
try:
# some code
return True
except:
return False

Categories

Resources