Asking for your help, because can't find right answer by myself. I made custom registration form in django with additional fields. To save that fields in database i made custom user in my models. And it works ok(as i understand). But it seems that auth is using the old User model. I have two thoughts:
1- I need to overwrite some method in MyUser.
2- I found some information about adding AUTH_USER_MODEL variable in my settings ( but it call some errors)
forms.py
class MyRegistrationForm(UserCreationForm):
bdate=forms.DateField(required=True)
phone=forms.CharField(required=True)
captha=CaptchaField(required=True)
class Meta:
model=MyUser
fields=('username','first_name','last_name','email','bdate','phone','password1','password2')
def save(self, commit=True):
user = super(MyRegistrationForm, self).save(commit=False)
user.bdate=self.cleaned_data['bdate']
user.phone=self.cleaned_data['phone']
if commit:
user.save()
return user
models.py
class MyUser(User):
bdate=models.DateField()
phone=models.CharField(max_length=10)
You should not inherit from User, which is a concrete class. Inherit from AbstractUser instead.
Related
Hi I am new to Django and I have created a login/logout model Django inbuilt User and UserCreationForm. It is working fine but my issue is I have define two custom inputs in my form and it is displaying on the web page when I run the server but when I check the user under the admin, I only see the details of User defined fields not my custom fields.
How to save it's data to my User model?
or maybe If I defined the custom fields wrong how do I change it so that I can save it's data in my model.
My custom defined fields that is address and phone number is not showing in Admin User and it's data is not getting saved.
model.py
from django.db import models
from django.contrib import auth
# Create your models here.
class User(auth.models.User,auth.models.PermissionsMixin):
def __str__(self):
return "#{}".format(self.username)
forms.py
from django.contrib.auth import get_user_model
from django.contrib.auth.forms import UserCreationForm
from django import forms
class UserCreateform(UserCreationForm):
address = forms.CharField(max_length=150, required=True)
phone_number = forms.IntegerField(required=True)
class Meta(UserCreationForm.Meta):
model = get_user_model()
fields = ('username','email','password1','password2')
def __init__(self,*args,**kwargs):
super().__init__(*args,**kwargs)
self.fields['username'].label = 'Display Name'
views.py
from django.shortcuts import render
from django.urls import reverse_lazy
from django.views.generic import CreateView
from . import forms
# Create your views here.
class SignUp(CreateView):
form_class = forms.UserCreateform
success_url = reverse_lazy('login')
template_name = 'account/signup.html'
Adding fields to a ModelForm doesn't do anything in terms of saving them, if they are not fields of the Model. So of course, address and phone_number aren't saved, your User model doesn't have those fields.
You need to have a Model to save those fields. As explained here, you have two options:
Create a Profile model to save all extra fields
Create a custom User model, subclassing AbstractUser or AbstractBaseUser.
My advice: Do both. Save your extra fields in a Profile model.
And subclass AbstractUser, so you have the option to add useful methods and properties to the User model (right now, just __str__()).
Why not just subclass? Because as your app grows, you'll want to add more an more things to a user's profile. Maybe you want to create different types of profiles, e.g. the private profile and the professional profile. The User itself should be compact, just managing authentication and permissions.
Note: your current User model is wrong. You must not subclass auth.User but auth.AbstractUser.
I want to override the built-in django .save() method to perform a check against all other objects in the database.
For example:
class User(models.Model):
name = models.CharField(max_length=120)
class Admin(models.Model):
name = models.CharField(max_length=120)
class SecurityGroup(models.Model):
name = models.CharField(max_length=120)
users = models.ManytoManyField(User)
admins = models.ManytoManyField(Admin)
def save(self, *args, **kwargs):
# check admins don't exist in any other SecurityGroup prior to save
super(SecurityGroup, self).save(*args, **kwargs) # Call the "real" save() method.
The documentation example is pretty simple, and doesn't describe this type of pre-save check.
I have tried adding in lines to .save() such as:
`self.objects.filter(admins__name=self.admins.name).count()`
to call the other SecurityGroup objects but I receive the error:
`Manager is not accessible via SecurityGroup instance`
Is it possible to achieve this save functionality internal to the SecurityGroup Model, or do I need to create a form and use SecurityGroup.save(commit=False) for this type of pre-save check?
Thanks for the help.
The solution that worked for me was to override the Model's form in admin.py. This enabled a simple check whether admins already existed in a SecurityGroup or not.
from django.contrib import admin
from django.forms import ModelForm
from security.models import SecurityGroup
class SecurityGroupAdminForm(ModelForm):
class Meta:
model = SecurityGroup
fields = '__all__'
def clean(self):
# CHECK 1
if admins:
admins = self.cleaned_data['admins']
for a in admins:
existing_group = SecurityGroup.objects.filter(users__username=a.username)
if existing_group:
raise Exception("message")
return self.cleaned_data
Then, within the same admin.py file, indicate the custom form as part of the admin registration for the model of interest (in this case, SecurityGroup):
class UserSecurityGroupAdmin(admin.ModelAdmin):
# class Meta:
model = UserSecurityGroup
form = UserSecurityGroupAdminForm
admin.site.register(UserSecurityGroup, UserSecurityGroupAdmin)
The error is caused by accessing the Manager of a model through a model instance. You should have used
self.model_class().objects
I have the following models:
class UserProfile(models.Model):
user = models.OneToOneField(User)
class Property(models.Model):
user = models.ForeignKey(User)
I would like to create a TabularInline displaying every Property connected to a particular UserProfile on its Django admin page. The problem here is, of course, that Property does not have a ForeignKey directly to UserProfile, so I cannot simply write
class PropertyTabularInline(admin.TabularInline):
model = Property
class UserProfileAdmin(admin.ModelAdmin):
inlines = (PropertyTabularInline,)
How can I easily do what I want?
You can overwrite the User admin page to display both the Profile and the Property models.
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from myapp.models import *
class ProfileInline(admin.TabularInline):
model = Profile
class PropertyInline(admin.TabularInline):
model = Property
class UserAdmin(UserAdmin):
inlines = (ProfileInline, PropertyInline,)
admin.site.unregister(User)
admin.site.register(User, UserAdmin)
You can also remove any unwanted/unused User properties from being displayed (e.g. Groups or Permissions)
more here: https://docs.djangoproject.com/en/1.8/topics/auth/customizing/#extending-the-existing-user-model
and here:
https://docs.djangoproject.com/en/1.8/topics/auth/customizing/#a-full-example
class PropertyTabularInline(admin.TabularInline):
model = Property
def formfield_for_dbfield(self, field, **kwargs):
if field.name == 'user':
# implement your method to get userprofile object from request here.
user_profile = self.get_object(kwargs['request'], UserProfile)
kwargs["queryset"] = Property.objects.filter(user=user_profile)
return super(PropertyInLine, self).formfield_for_dbfield(field, **kwargs)
once this is done, you can add this inline to user UserProfileAdmin like:
class UserProfileAdmin(admin.ModelAdmin):
inlines = (PropertyTabularInline,)
Haven't tested it, but that should work.
It is achievable by making one change in your models.
Instead of creating OneToOne relationship from UserProfile to User, subclass User creating UserProfile. Code should look like that:
class UserProfile(User):
# some other fields, no relation to User model
class Property(models.Model):
user = models.ForeignKey(User)
That will result in creating UserProfile model that have hidden OneToOne relation to User model, it won't duplicate user model.
After doing that change, your code will work. There are some changes under the hood, like UserProfile no longer have it's own ID, you can access fields from User inside UserProfile and it's hard to swap User model using settings.AUTH_USER_MODEL (that will require creating some custom function returning proper type and changing migration by hand) but if this is not a problem for you, it may be good solution.
Implementing custom user for my project,
here is my account/models.py
class myUser(AbstractBaseUser, PermissionsMixin):
#blah
date_joined = models.DateTimeField(auto_now_add=True)
#blah
and my account/admin.py
class myUserDetail(admin.ModelAdmin):
list_display = ('email','password','is_active','is_staff','date_joined',)
fields = list_display
admin.site.register(myUser, myUserDetail)
The list_display works fine, but when I click into a user,
error is raised :
Unknown field(s) (date_joined) specified for myUser. Check fields/fieldsets/exclude attributes of class myUserDetail.
In fact it exists in postgres...
Please help!
The error is being triggered when the ModelForm is created automatically for the admin, specifically if there are missing fields. Because you are using auto_now_add=True, which implicitly sets editable=False, the field cannot be included in the automatically generated form. Because of this, an error is triggered.
I would recommend specifying fields and list_display independently, as they aren't actually the same.
When you use a custom User model in Django you should add the following line to your settings:
AUTH_USER_MODEL = 'accounts.MyUser'
I also think you should subclass from AbstractUser which is an abstract base class implementing a fully featured User model with admin-compliant permissions, and includes email, date_joined, etc. fields.
You can read more about customizing the user model in the Django documentation.
I'm trying to make sure that the first name and last name field are not optional for the auth User model but I'm not sure how to change it. I can't use a sub class as I have to use the authentication system.
Two solutions I can think of are:
to put the name in the user profile but it's a little silly to have a field that I can't use correctly.
To validate in the form rather than in the model. I don't think this really fits with Django's philosophy...
For some reason I can't seem to find a way to do this online so any help is appreciated. I would have thought that this would be a popular question.
Cheers,
Durand
Simplest solution
Just create a custom UserRegisterForm which inherits the django's default UserCreationForm.
The first_name and last_name are already attributes of django's default User. If you want to make them as required fields, then recreate those fields as forms.CharField(...).
Now use your own User register form.
# Contents usersapp/forms.py
from django import forms
from django.contrib.auth.models import User
from django.contrib.auth.forms import UserCreationForm
# Inherit Django's default UserCreationForm
class UserRegisterForm(UserCreationForm):
first_name = forms.CharField(max_length=50) # Required
last_name = forms.CharField(max_length=50) # Required
# All fields you re-define here will become required fields in the form
class Meta:
model = User
fields = ['username', 'email', 'first_name', 'last_name', 'password1', 'password2']
I would definitely go with validating on the form. You could even go as far as having more form validation in the admin if you felt like it.
Thanks Mbuso for the advice. Here's my full implementation for those who are interested. Before taking a look at the source, let's see what it looks like:
I've implemented a profile model, but this will work just fine without it.
from django.core.exceptions import ValidationError
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from django.contrib.auth.forms import UserChangeForm
from django.contrib.auth.models import User
from apps.profiles.models import Profile
# Define an inline admin descriptor for Profile model
# which acts a bit like a singleton
class UserProfileInline(admin.StackedInline):
model = Profile
can_delete = False
verbose_name_plural = 'profile'
class MyUserChangeForm(UserChangeForm):
def clean_first_name(self):
if self.cleaned_data["first_name"].strip() == '':
raise ValidationError("First name is required.")
return self.cleaned_data["first_name"]
def clean_last_name(self):
if self.cleaned_data["last_name"].strip() == '':
raise ValidationError("Last name is required.")
return self.cleaned_data["last_name"]
# Define a new User admin
class MyUserAdmin(UserAdmin):
form = MyUserChangeForm
inlines = UserProfileInline,
admin.site.unregister(User)
admin.site.register(User, MyUserAdmin)
Note: If you do implement a profile model, recommend using UserProfile as the name, since is this is what's in the documentation and seems to be the standard (this part was developed before I started working on the project). If you're using Django 1.5 or higher, skip UserProfile all together and extend the User model.
The Django way of extending the basic User model is through user profiles: see "Storing additional information about users".
If it does not fit your needs, django.contrib.auth is just a Django application, I would simply fork it. As long as you abide by the original interface, I think you will be out of trouble.
Another option is Pinax - it has OpenId support built in, you can use it with your own openid provider. OpenId native support is a battery I really miss in Django.