How to override change model object form? - django

I am trying to override change model form for adming page but don't know why it doesn't work.
Can smb help me please?
My forms.py:
from django import forms
from django.contrib.auth.forms import UserChangeForm
from django.contrib.auth.models import User
class CustomUserChangeForm(UserChangeForm):
email = forms.EmailField(required=True, label="Email")
phone = forms.CharField(label="Phone number")
class Meta:
model = User
fields = ("username", "email", "phone", )
My admin.py:
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from .forms import CustomUserCreationForm, CustomUserChangeForm
from .models import CustomUser
class CustomUserAdmin(UserAdmin):
add_form = CustomUserCreationForm
form = CustomUserChangeForm
model = CustomUser
list_display = ['email', 'username', 'phone', ]
admin.site.register(CustomUser, CustomUserAdmin)
My models.py
class CustomUser(AbstractBaseUser, PermissionsMixin):
username = models.CharField(_('username'), max_length=30,
unique=True)
first_name = models.CharField(_('first name'), max_length=30,
blank=True)
last_name = models.CharField(_('last name'), max_length=30,
blank=True)
email = models.EmailField(_('email address'), unique=True,
blank=True, null=True)
phone_regex = RegexValidator(regex=r'^\+?1?\d{9,15}$',
message="Phone number must be entered in the
format: '+999999999'. Up to 15 digits allowed.")
phone = models.CharField(_('phone number'), validators=[phone_regex],
max_length=17,
unique=True, blank=True, null=True)
I am trying to override change model form but don't know why it doesn't work.
Can smb help me please?

At first sight you used the wrong model in your form: it should be:
from myapp.models import CustomUser
from django.contrib.auth.forms import UserChangeForm
class CustomUserChangeForm(UserChangeForm):
email = forms.EmailField(required=True, label='Email')
phone = forms.CharField(label='Phone number')
class Meta:
model = CustomUser
fields = ('username', 'email', 'phone', )
That being said, since your CustomerUserChangeForm does not have any fields to change passwords or permissions, you can use a simple ModelForm [Django-doc]:
from django.forms import ModelForm
from myapp.models import CustomUser
class CustomUserChangeForm(ModelForm):
email = forms.EmailField(required=True, label='Email')
phone = forms.CharField(label='Phone number')
class Meta:
model = CustomUser
fields = ('username', 'email', 'phone', )

Related

Extended the User model, but not quite sure how to serialize fields for both User and UserExtended

I extended my User model with a new model just called UserExtended:
# Django imports
from django.db import models
from django.contrib.auth.models import User
class UserExtended(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
crm_guid = models.UUIDField(unique=True)
security_q1 = models.CharField(max_length=255, blank=True, null=True)
security_a1 = models.CharField(max_length=255, blank=True, null=True)
security_q2 = models.CharField(max_length=255, blank=True, null=True)
security_a2 = models.CharField(max_length=255, blank=True, null=True)
attempts = models.SmallIntegerField(blank=False, null=False, default=0)
key = models.CharField(max_length=255, blank=True, null=True)
key_expires = models.DateTimeField(blank=True, null=True)
method = models.CharField(max_length=4, blank=True, null=True)
class Meta:
db_table = 'auth_user_extended'
I was hoping by just doing that some Django magic would take care of the rest and I wouldn't have to change my views.py or serializers.py. But when I send a request to the end-point I get:
[api] django.core.exceptions.ImproperlyConfigured: Field name `guid` is not valid for model `User`.
So it does apparently need to be specified. I've been looking at the documentation and similar SO questions to find an answer.
This is what I have for my views.py:
# Django imports
from django.contrib.auth.models import User
# Third party imports
from rest_framework import generics
from rest_framework.permissions import IsAdminUser
# App imports
from users.serializers import UserSerializer
class UsersListCreateView(generics.ListCreateAPIView):
permission_classes = [IsAdminUser]
serializer_class = UserSerializer
def get_queryset(self):
queryset = User.objects.all()
email = self.request.query_params.get('email')
username = self.request.query_params.get('username')
if email:
queryset = queryset.filter(email=email)
if username:
queryset = queryset.filter(username=username)
return queryset
class UserRetrieveUpdateDeleteView(generics.RetrieveUpdateDestroyAPIView):
permission_classes = [IsAdminUser]
queryset = User.objects.all()
serializer_class = UserSerializer
For my serializers.py I just have:
# Django imports
from django.contrib.auth.models import User
from users.models import UserExtended
from django.contrib.auth.hashers import make_password
# Third party imports
from rest_framework import serializers
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ['id', 'last_login', 'first_name',
'last_name', 'username', 'email', 'is_active', 'guid']
If I change model = User to model = UserExtemded, then I'll get an error like:
[api] django.core.exceptions.ImproperlyConfigured: Field name `last_login` is not valid for model `UserExtended`.
I'm thinking I need to do one of two things:
Create a serializer class for both models and call them both from the views.py. I've toyed with this a little by trying to pass a list or tuple in of serializer_class (apparently singular for a reason).
Setup the relationship in the serializers.py. I'm looking into this now.
Suggestions for how to resolve this issue?
You need a different serializer and viewset to operate on UserExtended
My suggestion would be keep old serializer as is and create UserExtendedSerializer
class UserExtendedSerializer(serializers.ModelSerializer):
user = UserSerializer(many=False, read_only=True)
class Meta:
model = UserExtended
fields = "__all__"
and viewset would be simply:
class UserExtendedViewSet(ModelViewSet):
serializer_class = UserExtendedSerializer
queryset = UserExtended.objects.all()
this should solve your issue

Add foreign key field and choices charfield to Django user creation form

I would like to modify Django UserCreationForm so that it would support creating my custom user. There are required fields company and role which both should offer some kind of selection to pick the correct choice (there will be only 3 roles but there can be hundreds of companies).
I believe I need to extend UserCreationForm and modify UserAdmin. However, I have followed several different examples but so far in vain. Below is the model. How can I command Django to add the extra fields to the user creation form?
ROLE_CHOICES = [
('role1', 'Role 1'),
('role1', 'Role 2'),
('janitor', 'Janitor'),
]
class Company(models.Model):
created = models.DateTimeField(auto_now_add=True)
name = models.CharField(max_length=100)
class Meta:
ordering = ('created',)
db_table = "company"
def __str__(self):
return self.name
class CustomUser(AbstractUser):
created = models.DateTimeField(auto_now_add=True)
username = models.CharField(max_length=100, unique=True)
email = models.EmailField(max_length=200, unique=True)
company = models.ForeignKey(Company, on_delete=models.CASCADE, related_name='%(class)s_company')
role = models.CharField(
max_length=100,
choices=ROLE_CHOICES,
default='janitor',
)
phone_number = models.CharField(null=True, blank=True, max_length=20)
class Meta:
ordering = ('created',)
db_table = "custom_user"
def __str__(self):
return self.username
You don't have to extend UserCreationForm. just use this:
forms.py
from django import forms
from .models import CustomUser
class UserRegistrationForm(forms.ModelForm): # you can name it anything
class Meta:
model = CustomUser
fields = ('username', 'email', 'company',....) # add the fields here you want in form just not created. it's auto fill
Use this form.
If you want admin. Write this in admins.py
from .models import CustomUser
class CustomUserAdmin(admin.ModelAdmin):
list_display = ('username', 'email', 'company'...) # add fields as you want
admin.site.register(CustomUser, CustomUserAdmin)
Hope this help. If not, please comment.

DJANGO createsuperuser not working...TypeError: create_superuser() missing 1 required positional argument: 'username'

I am trying to create a RESTful API using Django and DRF. I have a User model which extends AbstractUser. I'm neither able to create normal users nor a superuser. For some reason, It says
When I run:
python manage.py createsuperuser
I get the following error:
"TypeError: create_superuser() missing 1 required positional argument: 'username'"
Here's the models.py file:
import uuid
from django.db import models
from django.conf import settings
from django.dispatch import receiver
from django.contrib.auth.models import AbstractUser
from django.utils.encoding import python_2_unicode_compatible
from django.db.models.signals import post_save
from rest_framework.authtoken.models import Token
from api.fileupload.models import File
#python_2_unicode_compatible
class User(AbstractUser):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
profile_picture = models.ForeignKey(File, on_delete=models.DO_NOTHING, null=True, blank=True)
email = models.EmailField('Email address', unique=True)
name = models.CharField('Name', default='', max_length=255)
phone_no = models.CharField('Phone Number', max_length=255, unique=True)
company_name = models.CharField('Company Name', default='', max_length=255)
address = models.CharField('Address', default='', max_length=255)
address_coordinates = models.CharField('Address Coordinates', default='', max_length=255)
country = models.CharField('Country', default='', max_length=255)
pincode = models.CharField('Pincode', default='', max_length=255)
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = []
def __str__(self):
return self.email
#receiver(post_save, sender=settings.AUTH_USER_MODEL)
def create_auth_token(sender, instance=None, created=False, **kwargs):
if created:
Token.objects.create_user(user=instance)
The serializers.py file:
from django.contrib.auth.password_validation import validate_password
from rest_framework import serializers
from .models import User
from api.fileupload.serializers import FileSerializer
class UserSerializer(serializers.ModelSerializer):
profile_picture = FileSerializer()
def create(self, validated_data):
user = User.objects.create(**validated_data)
return user
def update(self, instance, validated_data):
instance.name = validated_data.get('name', instance.name)
instance.company_name = validated_data.get('company_name', instance.company_name)
instance.address = validated_data.get('address', instance.address)
instance.country = validated_data.get('country', instance.country)
instance.pincode = validated_data.get('pincode', instance.pincode)
instance.phone_no = validated_data.get('phone_no', instance.phone_no)
instance.email = validated_data.get('email', instance.email)
instance.profile_picture = validated_data.get('profile_picture', instance.profile_picture)
instance.save()
return instance
class Meta:
unique_together = ('email',)
model = User
fields = (
'id', 'password', 'email', 'name', 'phone_no', 'company_name', 'address', 'country', 'pincode', 'profile_picture',
)
extra_kwargs = {'password': {'write_only': True}}
The views.py file:
from rest_framework import viewsets, mixins
from .models import User
from .serializers import UserSerializer
class UserViewSet(mixins.RetrieveModelMixin,
mixins.ListModelMixin,
mixins.CreateModelMixin,
viewsets.GenericViewSet):
queryset = User.objects.all()
permission_classes = (AllowAny,)
serializer_class = UserSerializer
When you use createsuperuser command, it will require you to input some required field, USERNAME_FIELD is a required field, and all fields inside REQUIRED_FIELDS. Your code set REQUIRED_FIELDS=[] so there is no required field, just need to add email and password.
But when createsuperuser has been called, it will call create_superuser method which require username field:
def create_superuser(self, username, email=None, password=None, **extra_fields):
so that your input data is not enough to pass the data into create_superuser method.
To solve this, you can simply just add username into REQUIRED_FIELDS:
REQUIRED_FIELDS = ['username']
then createsuperuser will require you to add username field for create_superuser method.
Hope that helps.

How to Modify Emailfield in AbstractUser Model so that Email is Required

How to set emailfield to required when extending AbstractUser?
I was wondering how to override the AbstractUser's emailfield model to set it as required. Currently my form list the email field but does not set as required. By default shouldn't all models be required?
From the documentation it shows an example of how to set a field to required when creating a super user but I am not sure how to complete the task from the AbstractUser model without using AbstractBaseUser.
model.py
from django.db import models
from django.contrib.auth.models import AbstractUser
class CustomUser(AbstractUser):
display_name = models.CharField(max_length=50, unique=True)
bio = models.CharField(max_length=500, blank=True, null=True)
authorization_code = models.CharField(max_length=20)
forms.py
from django import forms
from django.contrib.auth.forms import UserCreationForm, UserChangeForm
from .models import CustomUser
class CustomUserCreationForm(UserCreationForm):
class Meta (UserCreationForm):
model = CustomUser
fields = ('username', 'email', 'display_name', 'authorization_code')
class CustomUserChangeForm(UserChangeForm):
class Meta:
model = CustomUser
fields = ('username', 'email', 'bio')
admin.py
from django.contrib import admin
from django.contrib.auth import get_user_model
from django.contrib.auth.admin import UserAdmin
from .forms import CustomUserCreationForm, CustomUserChangeForm
from .models import CustomUser
class CustomUserAdmin(UserAdmin):
add_form = CustomUserCreationForm
form = CustomUserChangeForm
model = CustomUser
list_display = ['email', 'username', 'display_name', 'bio']
list_display_links = ('username', 'display_name')
fieldsets = (
(None, {'fields': ('username', 'display_name', 'email', 'bio', 'password')}),
('Permissions', {'fields': ('is_staff', 'is_active')}),
)
add_fieldsets = (
(None, {
'classes': ('wide',),
'fields': ('username', 'display_name', 'email', 'bio', 'password1', 'password2', 'is_staff', 'is_active')}
),
)
admin.site.register(CustomUser, CustomUserAdmin)
Solution:
# It was as simple as updating my CustomUser model as shown:
class CustomUser(AbstractUser):
display_name = models.CharField(max_length=50, unique=True)
bio = models.CharField(max_length=500, blank=True, null=True)
authorization_code = models.CharField(max_length=20)
email = models.EmailField(max_length=50, unique=True)
You can do it by overriding email in AbstractBaseUser.
from django.contrib.auth.models import AbstractBaseUser, UserManager
class User(AbstractBaseUser):
email = models.EmailField(_('email address'), blank=False)
objects = UserManager()

I want to call validate_even method during validation of username but it shows name 'validate_even' is not defined

I am stuck to create custom validation for username my forms.py
from .models import User
from django import forms
from django.contrib.auth import get_user_model
from django.contrib.auth.forms import UserCreationForm
from django.core.exceptions import ValidationError
from PIL import Image
from django.utils.translation import ugettext_lazy as _
class SignUpForm(UserCreationForm):
email = forms.EmailField(max_length=254, help_text='Required. Inform a valid email address.')
first_name = forms.CharField(max_length=30, required=False, help_text='Optional.')
last_name = forms.CharField(max_length=30, required=False, help_text='Optional.')
phone = forms.CharField(max_length=30, help_text='Phone number', required=False)
# vv = forms.CharField(max_length=100, help_text='custom', required=False)
picture = forms.ImageField()
username = forms.CharField(error_messages={'unique': 'A user with that username already exists.'}, help_text='required.',validators=[validate_even], max_length=150),
class Meta:
model = get_user_model()
fields =('username', 'first_name', 'last_name', 'email', 'password1', 'password2', 'phone','picture')
my models.py is
from django.db import models
from django.contrib.auth.models import AbstractUser
from PIL import Image
from django.contrib.auth.validators import UnicodeUsernameValidator
class User(AbstractUser):
pp = models.CharField(max_length=100, blank=True, null=True)
xx = models.CharField(max_length=100, blank=True, null=True)
dd = models.CharField(max_length=100, blank=True, null=True)
vv = models.CharField(max_length=100, blank=True, null=True)
picture = models.ImageField(upload_to = 'uploads/', default = 'var/www/html/typo3/uploads/no-img.jpg')
username = models.CharField(error_messages={'unique': 'A user with that username already exists.'}, help_text='baal required.',validators=[validate_even], max_length=150),
my validators.py is
import re
from django.core import validators
from django.utils.deconstruct import deconstructible
from django.utils.translation import gettext_lazy as _
from django.core.exceptions import ValidationError
def validate_even(value):
if value % 2 != 0:
raise ValidationError(
_('%(value)s is not an even number'),
params={'value': value},
)
What mistake I had made, I can not understand . How to call my custom method in validotrs?
Add this line at the begin of models.py and forms.py:
from .validators import validate_even