Is it possible to add more fields to admin/auth/user? - django

models.py
class UserProfile(User):
bio = models.TextField(blank=True, null=True)
pfp = models.ImageField(verbose_name='Profile Picture', blank=True, null=True, upload_to="images/profile")
forms.py
class UserRegisterForm(UserCreationForm):
email = forms.EmailField(widget=forms.EmailInput(attrs={"class":"form-control", "placeholder":"example#example.com"}))
first_name = forms.CharField(widget=forms.TextInput(attrs={"class":"form-control"}))
last_name = forms.CharField(widget=forms.TextInput(attrs={"class":"form-control"}))
pfp = forms.ImageField(required=False, widget=forms.FileInput(attrs={"class":"form-control"}))
bio = forms.CharField(required=False, widget=forms.Textarea(attrs={"class":"form-control", 'rows':5, "placeholder":"Write something about yourself..."}))
class Meta:
model = UserProfile
fields = ['first_name', 'last_name', 'username', 'email', "pfp", "bio"]
When creating the user, the two newly added fields (bio and pfp) are not being saved in admin/auth/user, so my question is, is it possible to add those fields to the admin users database?
views.py
class SignUpView(CreateView):
form_class = UserRegisterForm
template_name = "registration/signup.html"
success_url = reverse_lazy("login")

are not being saved in admin/auth/user
Indeed, these are safed on the UserProfile model.
You thus can make a ModelAdmin for this:
# app_name/admin.py
from django.contrib import admin
from app_name.models import UserProfile
#admin.register(UserProfile)
class AuthorAdmin(admin.ModelAdmin):
list_display = ('first_name', 'last_name', 'username', 'email', 'pfp', 'bio')
Then the details can be seen in the admin/app_name/userprofile section.

Create a custom user model by inheriting the AbstractUser and adding extra fields needed. After that, register that custom user model by assigning it to AUTH_USER_MODEL.
Check here for detailed implementation.

Related

Django - Remove currently displayed image link In An Image Edit Form

I am using Django 2.2 for a project, I want to remove the currently displayed image link from the user update form as shown in the image below, how do I do this?
image
forms.py
from .models import Profile
class CreateUserForm(UserCreationForm):
class Meta:
model = get_user_model()
fields = ['username', 'email', 'password1', 'password2']
class UserUpdateForm(forms.ModelForm):
email = forms.EmailField()
class Meta:
model = User
fields = ['username', 'email']
help_texts = {
'username': None,
}
class ProfileUpdateForm(forms.ModelForm):
class Meta:
model = Profile
fields = ['profile_pic']
You can try to change ImageField to FileInput using widgets. I use Django version 4.1.1 and it works for me.
# forms.py
class ProfileUpdateForm(forms.ModelForm):
profile_pic = forms.ImageField(widget=forms.FileInput)
class Meta:
...

Django Custom User is not showing a extra field with foreign key as Select list

I have a Custom User
class CustomUser(AbstractUser):
registration_code = models.ForeignKey(RegistrationCode, null=True)
...
def __str__(self):
return self.username
class RegistrationCode(models.Model):
code = models.CharField(max_length=30, null=True, blank=True)
def __str__(self):
return self.code
Within the admin page I would like to be able to set the "registration_code".
class CustomUserAdmin(UserAdmin):
add_form = CustomUserCreationForm
form = CustomUserChangeForm
model = CustomUser
list_display = ['email', 'username', 'registration_code']
fieldsets = (
(None, {
'fields': ('email', 'username', 'registration_code')
}),
)
admin.site.register(CustomUser, CustomUserAdmin)
But on the admin page I do not get a select list. But a default input field:
And if I edit the registration_code and try to save I get:
Cannot assign "'2'": "CustomUser.registration_code" must be a "RegistrationCode" instance.
Which sounds logical because I need to enter a RegistrationCode instance.
The same construction works for other models but for a Custom User it is not?? This is driving me crazy. Anybody an idea why I do not get the Select list?
Django handle foreign key by model instance not assignment. So the problem come from your CustomUserChangeForm where registratio_code must be ModelChoiceField

Django - Creating an instance via the serializer with a foreign key reference

I have a agencies and users. I want to create User instances via the UserSerializer which have an agency_id. However the serializer's validated_data does not have the agency_id after calling is_valid().
class Agency(models.Model):
name = models.CharField(max_length=60)
class User(modes.Model):
username = models.CharField(max_length=60)
agency = models.ForeignKey(Agency, blank=True, null=True)
class UserSerializer(serializers.ModelSerializer):
class Meta:
User = get_user_model()
model = User
fields = ( 'id', 'username', 'agency_id' )
read_only_fields = ['id']
Try to create a user via the serializer which belongs to the Acme Agency:
agency = Agency.objects.create(name="Acme Agency")
serializer = UserSerializer(data={ 'username':'wiley', 'agency_id': agency.id} )
serializer.is_valid() # True
serializer.validated_data.get('agency_id') # None
Creating a user via the UserManager using the agency id works just fine:
user = User.objects.create(username='wiley', agency_id=1)
user.agency.id # 1
use agency instead of agency_id in UserSerializer as
class UserSerializer(serializers.ModelSerializer):
class Meta:
User = get_user_model()
model = User
fields = ('id', 'username', 'agency')
read_only_fields = ['id']
and use the serailizer as,
serializer = UserSerializer(data={ 'username':'wiley', 'agency': agency.id} )

Combine two models with OneToOne relationship into one form - Django

I created two custom user models (AbstractBaseUser and a separate for extra information). Is there a way to combine the two models to create one form that the user can use to update all of their information between the two models?
For example, it would be ideal to have something like this (although I know not possible):
class ProfileChangeForm(forms.ModelForm):
class Meta:
model = UserProfile
fields = ['username', 'email', first_name', 'last_name', 'bio', 'website']
Thank you in advance for your help! The models are below:
MyUser:
class MyUser(AbstractBaseUser, PermissionsMixin):
username = models.CharField(max_length=30, unique=True)
email = models.EmailField(max_length=255, unique=True)
first_name = models.CharField(max_length=120, null=True, blank=True)
last_name = models.CharField(max_length=120, null=True, blank=True)
UserProfile:
class UserProfile(models.Model):
user = models.OneToOneField(MyUser)
bio = models.TextField(null=True, blank=True)
website = models.CharField(max_length=120, null=True, blank=True)
Following solution worked for me. I used formsets to create this solution.
My models were as follows,
Models:
#Custom user model
class CustomUserManager(BaseUserManager):
def create_user(self, email, password, **extra_fields):
if not email:
raise ValueError(_('The Email must be set'))
email = self.normalize_email(email)
user = self.model(email=email, **extra_fields)
user.set_password(password)
user.save()
return user
class CustomUser(AbstractUser):
username = None
email = models.EmailField(_('email address'), unique=True)
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = []
objects = CustomUserManager()
#Related model(One-to-One relationship with custom user)
class Student(models.Model):
user = models.OneToOneField(CustomUser,on_delete = models.CASCADE)
first_name = models.CharField(max_length=50)
middle_name = models.CharField(max_length=50,blank=True,null=True)
last_name = models.CharField(max_length=50,blank=True,null=True)
After that I created two ModelForms
Forms
from django.contrib.auth.forms import UserCreationForm
from .models import CustomUser,Student
from django import forms
# Form for custom user
class SignUpForm(UserCreationForm):
class Meta:
model = CustomUser
fields = ('email', 'password1', 'password2')
class StudentCreationForm(forms.ModelForm):
class Meta:
model = Student
fields = ['user','first_name','middle_name','last_name']
Now the main part, I created a simple inline formset factory to handle Student model as an inline form.
Formset
from django.forms import inlineformset_factory
from .models import CustomUser,Student
from .forms import StudentCreationForm
# As parameters I provided parent Model(CustomUser),child Model(Student) and the Child
# ModelForm(StudentCreationForm)
StudentCreationFormSet = inlineformset_factory(CustomUser, Student,form=StudentCreationForm,extra=1,can_delete = False)
In views, I created the SignUpForm and StudentCreationFormSet object respectively. And in the POST request first I validated the CustomUser form and saved it without comitting it(commit=False). I created an object of custom user and passed it as a instance to the StudentCreationFormSet to validate the related form. If everything goes fine my both forms will be saved else the errors will be shown in the template.
View
from django.shortcuts import render,redirect
from .forms import SignUpForm
from .formsets import StudentCreationFormSet
def createAccountView(request):
student_account_form = SignUpForm()
student_details_formset = StudentCreationFormSet()
if request.method == 'POST':
student_account_form = SignUpForm(request.POST)
if student_account_form.is_valid():
# Validating Custom User form and creating object of it(not comitting as formset needed to be verified)
student_account = student_account_form.save(commit=False)
# Getting Custom User object as passing it as instance to formset
student_details_formset = StudentCreationFormSet (request.POST,instance=student_account)
if student_details_formset.is_valid():
student_account_form.save()
student_details_formset.save()
return redirect('login')
else:
student_details_formset = StudentCreationFormSet (request.POST)
context = {
'student_account_form':student_account_form,
'student_details_form':student_details_formset
}
return render(request, 'account/createStudentPage.html',context=context)
Also note that I am passing both the form and formset in single post request.
Template (createStudentPage.html)
<form method="POST" >
{% csrf_token %}
{{ student_account_form.as_p }}
{{ student_details_form.as_p }}
<button type="submit">Sign Up</button>
</form>
I think you can do something like :
class ProfileChangeForm(forms.ModelForm):
class Meta:
model = UserProfile
fields = ['user__username', 'user__email', 'user__first_name', 'user__last_name', 'bio', 'website']

Django Admin ManyToManyField

I've made a model (models.py):
class opetest(models.Model):
name = models.CharField(max_length=200)
author = models.ForeignKey(User, related_name='author')
description = models.TextField(u'Test description', help_text = u'Some words about quiz')
pub_date = models.DateTimeField('date published', blank=False)
vacancies = models.ManyToManyField(Vacancy, blank=True)
students = models.ManyToManyField(User, blank=True, related_name='opetests') #This field I want to edit on "User change page"
estimate = models.IntegerField(default = 0, help_text = u'Estimate time in hours. \'0\' - unlimited')
then I try to add inline block to allow assign opetest on 'change user' page (admin.py):
class ProfileAdmin(UserAdmin):
filter_horizontal = ('opetests',)
admin.site.unregister(User)
admin.site.register(User, ProfileAdmin)
And I got an error:
'ProfileAdmin.filter_horizontal' refers to field 'opetests' that is missing from model 'User'.
I want to show opetests like Groups on change user page. How can I achieve that?
Hmm, I don't think you want inlines here.
You want to be using the Django admin's filter_horizontal:
https://docs.djangoproject.com/en/dev/ref/contrib/admin/#django.contrib.admin.ModelAdmin.filter_horizontal
class ProfileAdmin(UserAdmin)
filter_horizontal = ('opetest',)
That will give you the widget that you're describing, used to add/remove Groups on the User Change page.
Ok, based on your edits, updated answer - basically, what we have is a UserProfile, linked to each user.
The UserProfile contains a m2m relationship to opetest - which we show in the admin with a filter_horizontal. End result is something like this:
models.py
from django.db import models
from django.contrib.auth.models import User
class opetest(models.Model):
name = models.CharField(max_length=200)
author = models.ForeignKey(User, related_name='author')
description = models.TextField(u'Test description', help_text = u'Some words about quiz')
pub_date = models.DateTimeField('date published', blank=False)
#vacancies = models.ManyToManyField(Vacancy, blank=True)
students = models.ManyToManyField(User, blank=True, related_name='opetests') #This field I want to edit on "User change page"
estimate = models.IntegerField(default = 0, help_text = u'Estimate time in hours. \'0\' - unlimited')
class UserProfile(models.Model):
user = models.OneToOneField(User, unique=True)
ope = models.ManyToManyField(opetest)
test_flag = models.BooleanField()
admin.py
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from django.contrib.auth.models import User
from secondapp.models import UserProfile, opetest
admin.site.unregister(User)
class opetestAdmin(admin.ModelAdmin):
pass
class UserProfileInline(admin.StackedInline):
model = UserProfile
filter_horizontal = ('ope',)
class CustomUserAdmin(UserAdmin):
#filter_horizontal = ('user_permissions', 'groups', 'ope')
save_on_top = True
list_display = ('username', 'email', 'first_name', 'last_name', 'is_staff', 'last_login')
inlines = [UserProfileInline]
admin.site.register(User, CustomUserAdmin)
admin.site.register(opetest, opetestAdmin)
Let me know if you have any questions, or need anything further.