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.
Related
I have a custom user model (subclassing AbstractUser):
from django.db import models
from django.contrib.auth.models import AbstractUser
from django.utils.translation import ugettext_lazy as _
from .managers import UserManager
class User(AbstractUser):
username = None
phone = models.CharField(max_length=16)
email = models.EmailField(_('email address'), unique=True)
is_director = models.BooleanField(default=False)
objects = UserManager()
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = []
And a custom view to handle the sign up form:
class SignUpView(CreateView):
template_name = 'form.html'
form_class = UserRegisterForm
form_title = "Family Registration Form"
form_description = "For our younger participants, their parent or guardian should fill out this form."
def get_success_url(self):
return reverse_lazy('Participants:portal', kwargs={'slug': 'scst'})
Mapped by the following url:
path('signup/', views.SignUpView.as_view(), name="signup"),
The form loads and successfully creates a user. However, I can't figure out how to redirect properly. Specifically, I am looking for this:
http://localhost:8000/scst/portal/
and am getting:
http://localhost:8000/accounts/login/?next=/scst/portal/
I don't understand how to get rid of the /accounts/login/?next= portion. I have searched through CreateView's various Classes and Mixins. I have tried overriding the get_redirect_url and I have tried setting the pattern in the settings.py file. There is no "next" value in either the HTML's form or submit elements. I'm at a loss of what to change to fix this.
Probably it happens because your view "Participants:portal" is login required, you must login new user after registration, you can do it in form_valid method. Like that
from django.contrib.auth import login
def form_valid(self, form)
response = super().form_valid(form)
login(self.request, self.object)
return response
I've been trying creating a user profile form using built-in User of django.contrib.auth.models. Everything is working fine but after filling the fields into the form(which is displaying), I am encountering an INTEGRITY ERROR AT / saying NOT NULL CONSTRAINT failed.
You can see this image using this link to know exactly what the error is showing.
This is my models.py file
from django.db import models
from django.contrib.auth.models import User
from django.core.validators import MaxValueValidator
# Create your models here.
class UserProfileInfo(models.Model):
user = models.OneToOneField(User,on_delete=models.CASCADE, null=True)
phone_number = models.PositiveIntegerField(validators=
[MaxValueValidator(9999999999)],blank=True)
def __str__(self): #This will print out this model
return self.user.username
This is my forms.py file.
from django import forms
from django.contrib.auth.models import User
from Login_Signup_Form.models import UserProfileInfo
class UserForm(forms.ModelForm):
password = forms.CharField(widget=forms.PasswordInput())
class Meta:
model=User
fields=('first_name','last_name','username','email','password',)
class UserProfileForm(forms.ModelForm):
class Meta:
model=UserProfileInfo #this is model
fields=('phone_number',)
This is my views.py file.
from django.shortcuts import render
from Login_Signup_Form.forms import UserForm,UserProfileForm
from Login_Signup_Form.models import UserProfileInfo
# Create your views here.
def index(request):
return render(request,'base.html')
def register(request):
registered=False
if request.method == 'POST':
user_form = UserForm(data=request.POST)
user_phone_number=UserProfileForm(data=request.POST)
if user_form.is_valid() and user_phone_number.is_valid():
user=user_form.save()
user.set_password(user.password)
user.save()
phone = user_phone_number.save()
phone.user=user
else:
#Printing the errors
print(user_form.errors,user_phone_number.errors)
else:
user_form = UserForm()
user_phone_number = UserProfileForm()
return render(request, 'base.html',{'user_form':user_form, '
phone_number':user_phone_number})
The error probably comes from an empty phone number in your form. You allow an empty phone_number in your form with blank=True but you don't allow it on the database level, you need to add null=True as well:
phone_number = models.PositiveIntegerField(validators=
[MaxValueValidator(9999999999)], blank=True, null=True)
See this great answer.
With blank=True the field is not required and the form will validate but it will raise the integrity error because null=True is not here. That wouldn't happen with a CharField though, the blank value would be stored as empty string. This only happens because of the PositiveIntegerField.
I have created custom Userclass in django(AbstarctUser). Everything works fine but my password is getting stored as plain text in database even after registering in admin.py. I do not have any forms.py explicitly defined.
Also I am using nested serializers following tutorial.
My code is as below
from django.contrib import admin
from .models import BasicUserInfo
from django.contrib.auth.admin import UserAdmin
class BasicUserAdmin(UserAdmin):
pass
admin.site.register(BasicUserInfo, BasicUserAdmin)
Edited to add Models and views
Models.py
class BasicUserInfo(AbstractUser):
email = models.EmailField(primary_key=True, unique=True, db_index=True)
class UserInfo(models.Model):
user = models.ForeignKey(BasicUserInfo, on_delete=models.CASCADE)
Views.py
serializer = AddUserSerializer(data=request.data)
if serializer.is_valid(raise_exception=ValueError):
serializer.create(validated_data=request.data)
Serializers.py
class BasicUserSerializer(serializers.ModelSerializer):
class Meta:
model = BasicUserInfo
fields = ('username', 'password', 'email')
print("hete")
def create(self, validated_data):
retval = BasicUserInfo.objects.create(**validated_data)
password = validated_data.pop('password')
self.password = make_password(password)
# self._password = password
return retval
class AddUserSerializer(serializers.ModelSerializer):
user = BasicUserSerializer(required=True)
class Meta:
model = UserInfo
fields = ('phoneNo')
def create(self, validated_data):
user_data = validated_data.pop('user')
user = BasicUserSerializer.create(BasicUserSerializer(), validated_data=user_data)
user_info, created = UserInfo.objects.update_or_create(user=user, phoneNo=validated_data.pop('phoneNo'))
return user_info
The trick is to use user.set_password(password) -> this internally triggers the password hashing mechanism: Here's the Django code that does this:
def set_password(self, raw_password):
self.password = make_password(raw_password)
self._password = raw_password
def make_password(password, salt=None, hasher='default'):
"""
Turn a plain-text password into a hash for database storage
Same as encode() but generate a new random salt. If password is None then
return a concatenation of UNUSABLE_PASSWORD_PREFIX and a random string,
which disallows logins. Additional random string reduces chances of gaining
access to staff or superuser accounts. See ticket #20079 for more info.
"""
if password is None:
return UNUSABLE_PASSWORD_PREFIX + get_random_string(UNUSABLE_PASSWORD_SUFFIX_LENGTH)
hasher = get_hasher(hasher)
salt = salt or hasher.salt()
return hasher.encode(password, salt)
So the problem is serializers.create(**validated_data) is not performing the make_password operation. The above answer works perfectly fine, except it does two things differently
- It saves the user twice (once in serailizer.create and again during `user.save())
- It does not hande everything within the serializer, part of the work is being split b/w the serializer and the view.
If you want to keep it all within the serializer, you can do the following:
class AddUserSerializer(serializers.ModelSerializer):
class Meta:
model = BasicUserInfo
def validate_password(self, value):
return make_password(value)
Update:
I've made a bunch of edits; and tried to explain why. Please read patiently, and incorporate changes as you see fit.
class BasicUserSerializer(serializers.ModelSerializer):
class Meta:
model = BasicUserInfo
fields = ('username', 'password', 'email')
def validate_password(self, value):
return make_password(value)
class AddUserSerializer(serializers.ModelSerializer):
user = BasicUserSerializer(required=True)
class Meta:
model = UserInfo
fields = ('phoneNo')
def create(self, validated_data):
user_data = validated_data.pop('user')
user_serializer = BasicUserSerializer(data=user_data)
if user_serializer.is_valid(raise_exception=True):
user = user_serializer.save()
validated_data['user'] = user
return UserInfo.objects.create(**validated_data)
You should not use like this:
serializer = AddUserSerializer(data=request.data)
if serializer.is_valid(raise_exception=ValueError):
serializer.create(validated_data=request.data)
if password in validated data
it is better to use like this:
password = request.data.pop('password', '')
if not password:
raise ValidationError('password must not be empty')
serializer = AddUserSerializer(data=request.data)
serializer.is_valid(raise_exception=ValueError):
user = serializer.create(validated_data=request.data)
user.set_password(password)
user.save()
I'm using userena 1.2 and Django 1.4. I'm trying to create multiple user profiles but I had no luck following many post found here on SO.
I've create 2 custom Models and a form like so:
class BaseProfile(UserenaBaseProfile):
""" Default profile """
user = models.OneToOneField(User, unique=True, verbose_name = _('user'), related_name='base_profile')
#property # accessing a method as a property
def is_seller(self):
"""Find out if this is a Seller user"""
try:
self.seller
return True
except Seller.DoesNotExist:
return False
def get_profile_type(self):
"""return the profile type model"""
if self.is_seller:
return self.seller
else:
return self.customer
class Seller(BaseProfile):
company = models.CharField(_('Company'),max_length=100, null=True, blank=True,)
class Customer(BaseProfile):
favourite_snack = models.CharField( _('favourite snack'), max_length=5 )
And overridden the Signup form:
class SignupFormExtra(SignupForm):
# stuff in here
def save(self):
# stuff in here
Now the problem is inside the save method.
Based on this answer I've tried to implement a custom manager but I had no luck (I'm a django newbie). The important point that I understood is that the Save method shoud return a Django User, not a userena profile.
def save(self):
user = super(SpamSignupForm,self).save()
new_customer = Customer.objects.create_user()
return new_user
Then I've tried something like this:
def save(self):
new_user = super(SignupFormExtra, self).save()
new_user.save()
customer = Customer(profile = new_user.get_profile(), user=new_user)
customer.save()
return new_user
The get_profile() method will always (I guess) returns the base profile defined in settings.AUTH_PROFILE_MODULE.
Also it seems wrong to me that the author used a profile field in the subprofile as a OneToOne relation to the Baseprofile WHILE implementing a multi-table inheritance, why? It doesn't seem right to me.
class Customer(Baseprofile):
profile = models.OneToOneField(Baseprofile,
unique=True,
verbose_name=_('profile'),
related_name='student')
Yes, basically I've spent a full day trying to figure this out and I'm still lost.
it is better to use django-registration. install and add it to INSTALLED_APPS =(.....'registration',
now create two user in
registration/models.py
.................
class Seller(models.Model):
user=models.OneToOneField(User)
companyname= models.CharField(max_length=100,blank=True)
def __unicode__(self):
return self.user.username
class Customer(models.Model):
user=models.OneToOneField(User)
birth_date = models.DateField(null=True, blank=True)
favourite_snack = models.CharField(max_length=5)
def __unicode__(self):
return self.user.username
in registration/views.py
........
from django.contrib.auth import authenticate, login
from registration.models import Seller,Customer
from django.http import HttpResponse
def auth_login(request,utype):
temp_login='registration/%slogin.html' %utype
try:
username = request.POST['username']
password = request.POST['password']
except KeyError:
return render(request,temp_login)
user = authenticate(username=username, password=password)
if utype=='seller':
try:Seller.objects.get(user=user)
except: return render(request,temp_login,{'errors':True})
if utype=='customer':
try:Customer.objects.get(user=user)
except: return render(request,temp_login,{'errors':True})
if user.is_active:
login(request,user)
return HttpResponseRedirect('/'+request.GET['next'])#,render(request,'%s/home.html' %utype)
return render(request,temp_login,{'errors':True})
edit registration/auth_urls.py
urlpatterns = patterns('',
url(r'^login/(employer|jobseeker)/$',
auth_login,name='auth_login'),
registration/backends/default/views.py
inside class RegistrationView(BaseRegistrationView):
inside def register(self, request, **cleaned_data): add
if self.args[0]=='e': usertype=Seller()
else: usertype=Customer()
usertype.user=new_user
usertype.save()
registration/backends/default/urls.py
add these line
url(r'^register/([ej])/$',
RegistrationView.as_view(),
name='registration_register'),
I'm trying to set up my custom user model in Django. The reason is that I want to use email as the username, and remove the username field entirely. I've run into a error, that I just can't figure out.
Manager isn't available; User has been swapped for 'app.MyUser'
Exception Location: .../django/db/models/manager.py in __get__, line 256
Python Version: 2.7.3
Python Path:
[...project specific files,
'/usr/lib/python2.7',
'/usr/lib/python2.7/plat-linux2',
'/usr/lib/python2.7/lib-tk',
'/usr/lib/python2.7/lib-old',
'/usr/lib/python2.7/lib-dynload',
'/usr/local/lib/python2.7/dist-packages',
'/usr/lib/python2.7/dist-packages',
'/usr/lib/python2.7/dist-packages/PIL',
'/usr/lib/python2.7/dist-packages/gtk-2.0',
'/usr/lib/pymodules/python2.7',
'/usr/lib/python2.7/dist-packages/wx-2.8-gtk2-unicode']
I've googled like crazy, but haven't found too many pages about this error message. I have found some pages, with suggestions on how to solve it, but none of the suggestions have worked for me.
My code: I've set the custom user model. I have declared the custom user model AUTH_USER_MODEL = 'app.MyUser' in settings.py. I have also set up a custom UserManager:
class MyUserManager(BaseUserManager):
def create_user(self, email, password=None):
"""
Creates and saves a User with the given email. Note that none of the optional fields gets values in the creation. These fields will have to be filled out later on.
"""
if not email:
raise ValueError('Users must have an email address')
user = self.model(email=MyUserManager.normalize_email(email))
user.set_password(password)
user.save(using=self._db)
return user
def create_superuser(self, email, password=None):
"""
Creates and saves a superuser with the the above mentioned attributes
"""
user = self.create_user(email, password=password)
user.is_admin = True
user.save(using=self._db)
return user
class MyUser(AbstractBaseUser, PermissionsMixin):
"""
Custom made User model. No username, instead email is used as unique field and index
"""
Genders = (('M', 'Man'), ('K', 'Woman'))
FirstName = models.CharField(max_length=30)
LastName = models.CharField(max_length=40)
Gender = models.CharField(max_length=2, choices=Genders, default='K')
email = models.EmailField(verbose_name='email address', max_length=255, unique=True, db_index=True,)
twitter = models.CharField(max_length=30)
is_admin = models.BooleanField(default=False)
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = []
def get_full_name(self):
# The user is identified by their email address
return self.email
def get_short_name(self):
# The user is identified by their email address
return self.email
def __unicode__(self):
return self.email
objects = MyUserManager()
I've tried to declare to different types of UserAdmins, none of which is making any difference,the first one I tried was;
class MyUserAdmin(UserAdmin):
# The forms to add and change user instances
#form = UserChangeForm
#add_form = FrontpageRegistrationForm
list_display = ('email', 'FirstName', 'LastName', 'Gender', 'twitter')
list_filter = ()
add_fieldsets = ((None, {'classes': ('wide',),'fields': ('email', 'password1', 'password2')}),)
search_fields = ('email',)
ordering = ('email',)
filter_horizontal = ()
admin.site.register(MyUser, MyUserAdmin)
I've commented out the two attributes add_form and form because they raised some form errors I wanted to get back to at a later point.
The second UserAdmin was made, after reading about a possible fix here. This didn't help the situation though;
class MyUserAdmin(admin.ModelAdmin):
# The forms to add and change user instances
#form = UserChangeForm
add_form = FrontpageRegistrationForm
add_fieldsets = ((None, {'classes': ('wide',),'fields': ('email', 'password1', 'password2')}),)
def get_fieldsets(self, request, obj=None):
if not obj:
return self.add_fieldsets
return super(MyUserAdmin, self).get_fieldsets(request, obj)
def get_form(self, request, obj=None, **kwargs):
defaults = {}
if obj is None:
defaults.update({'form': self.add_form,'fields': admin.util.flatten_fieldsets(self.add_fieldsets),})
defaults.update(kwargs)
return super(MyUserAdmin, self).get_form(request, obj, **defaults)
I've also tried deleting all tables in the db with no luck.
I would be eternally greatful to anyone who even looks at the problem. And if any one were to solve this, I would try my best to talk my wife into naming our firstborn after the Avatar that gave me a solution so that I could go on living my life.
EDIT:
I tried setting the AUTH_USER_MODELto mainfolder.app.MyUserI'm sure the "mainfolder" is on the pythonpath. init.py in the app should be correct. The new settings.py gave the following server error; auth.user: AUTH_USER_MODEL is not of the form 'app_label.app_name'.admin.logentry: 'user' has a relation with model smartflightsearch.SFSdb.MyUser, which has either not been installed or is abstract.registration.registrationprofile: 'user' has a relation with model, which has either not been installed or is abstract. A new clue I don't know how to interpret..
TL;DR: Use the code from the Solution part at the end of the following answer.
Longer explanation: You see, as of Django 1.5, it's not enough to subclass Django's UserAdmin to be able to interact with swappable user models: you need to override respective forms as well.
If you jump to django.contrib.auth.admin source, you'll see that the UserAdmin's form and add_form have these values:
# django/contrib/auth/admin.py
class UserAdmin(admin.ModelAdmin):
...
form = UserChangeForm
add_form = UserCreationForm
Which point us to forms in django.contrib.auth.forms that do not respect swappable user models:
# django/contrib/auth/forms.py
class UserCreationForm(forms.ModelForm):
...
class Meta:
model = User # non-swappable User model here.
class UserChangeForm(forms.ModelForm):
...
class Meta:
model = User # non-swappable User model here.
Solution: So, you should follow a great already existing answer (don't forget to vote it up!) which boils down to this:
from django.contrib.auth import get_user_model
from django.contrib.auth.admin import UserAdmin
from django.contrib.auth.forms import UserCreationForm, UserChangeForm
class MyUserChangeForm(UserChangeForm):
class Meta:
model = get_user_model()
class MyUserCreationForm(UserCreationForm):
class Meta:
model = get_user_model()
class MyUserAdmin(UserAdmin):
form = MyUserChangeForm
add_form = MyUserCreationForm
admin.site.register(MyUser, MyUserAdmin)
Hopefully, this would be fixed in the future releases of Django (here's the corresponding ticket in the bug tracker).
When you said you set AUTH_USER_MODEL = 'app.MyUser' I'm assuming your app where is located the MyUser class, have a structure, perharps, like this:
inside the app/ dir: init.py and models.py and stuff..
so inside the models.py you have the MyUser and inside the init.py:
from models import MyUser