Inheriting Models from django.forms - python

I am linking my models to my forms by using forms.ModelForm and running the server. I get the error "ModelForm has no model class specified"
Here is the code I am using
class UserForm(forms.ModelForm):
password=forms.CharField(widget=forms.PasswordInput())
class meta:
Model= User
fields=('username' , 'email' , 'password')

You made some errors in your Meta class and model attribute: it is Meta (starting with an uppercase), and model (starting with a lowercase):
class UserForm(forms.ModelForm):
password=forms.CharField(widget=forms.PasswordInput())
class Meta:
model = User
fields = ('username' , 'email' , 'password')
But that will not be sufficient. You can not set the password of the User model by setting the attribute. Django hashes the password. You should use the User.set_password(..) method [Django-doc]. You can do so by overriding the save() method:
class UserForm(forms.ModelForm):
password=forms.CharField(widget=forms.PasswordInput())
def save(self, commit=True):
user = super().save(commit=False)
user.set_password(self.cleaned_data['password'])
if commit:
user.save()
return user
class Meta:
model = User
fields = ('username' , 'email' , 'password')

Related

Django 3.0.5 ModelForm has no model class specified. Error

forms.py
from django import forms
from django.contrib.auth.models import User
from basic_app.models import UserProfileInfo
class UserForm(forms.ModelForm):
password = forms.CharField(widget=forms.PasswordInput())
class Meta:
models = User
feilds = ['username','email','password']
class UserProfileInfoForm(forms.ModelForm):
class Meta:
models = UserProfileInfo
feilds = ('portfolio_site','profile_pic')
models.py
from django.db import models
from django.contrib.auth.models import User
class UserProfileInfo(models.Model):
user = models.OneToOneField(User,on_delete=models.CASCADE)
portfolio_site = models.URLField(blank=True)
profile_pic = models.ImageField(upload_to='profile_pics',blank=True)
def __str__(self):
return self.user.username
Error screenshot
As you can see in the above error image, my code is not functioning properly. I have added my forms.py and models.py code. Please help me resolve the issue.
It should be model instead of models inside the Meta class of UserForm.
class UserForm(forms.ModelForm):
password = forms.CharField(widget=forms.PasswordInput())
class Meta:
model = User # model instead of models here
feilds = ['username','email','password']

Hiding Email Address On UserUpdateForm

I want the user to be able to update their username but not email address. In example 3 despite the fact I do not include the field email in my code, the field still appears when I run the site. Admittedly the text box is blank whereas in example 1 and 2 it is populated.
How can I stop the email text box appearing? Or can I lock it so the user cannot enter a new value?
Example 1
class UserUpdateForm(forms.ModelForm):
email = forms.EmailField()
class Meta:
model = User
fields = ['username', 'email']
Example 2
class UserUpdateForm(forms.ModelForm):
email = forms.EmailField()
class Meta:
model = User
fields = ['email']
Example 3
class UserUpdateForm(forms.ModelForm):
email = forms.EmailField()
class Meta:
model = User
fields = ['username']
from django.forms import ModelForm
class UserUpdateForm(ModelForm):
#email = forms.EmailField()
class Meta:
model = User
fields = ['username']
Since you need all other fields except email (or at least you mentioned only email as the one you'd like to hide), perhaps you can use then exclude attribute:
from django.forms import ModelForm
class UserUpdateForm(ModelForm):
class Meta:
model = User
exclude = ('email',)
For more details you can read in this doc.
Also, alternative way to go is to disable field (not checked):
class UserUpdateForm(forms.ModelForm):
email = forms.EmailField(disabled=True)
class Meta:
model = User
fields = ['username', 'email']
Passing disabled=True should still render the field but unable it edition by user.

Make User email unique django

How can I make a Django User email unique when a user is signing up?
forms.py
class SignUpForm(UserCreationForm):
email = forms.EmailField(required=True)
class Meta:
model = User
fields = ("username", "email", "password1", "password2")
def save(self, commit=True):
user = super(SignUpForm, self).save(commit=False)
user.email = self.cleaned_data["email"]
if commit:
user.save()
return user
I'm using the from django.contrib.auth.models User.
Do I need to override the User in the model. Currently the model doesn't make a reference to User.
views.py
class SignUp(generic.CreateView):
form_class = SignUpForm
success_url = reverse_lazy('login')
template_name = 'signup.html'
The best answer is to use CustomUser by subclassing the AbstractUser and put the unique email address there. For example:
from django.contrib.auth.models import AbstractUser
class CustomUser(AbstractUser):
email = models.EmailField(unique=True)
and update the settings with AUTH_USER_MODEL="app.CustomUser".
But if its not necessary for you to store the unique email in Database or maybe not use it as username field, then you can update the form's clean method to put a validation. For example:
from django.core.exceptions import ValidationError
class YourForm(UserCreationForm):
def clean(self):
email = self.cleaned_data.get('email')
if User.objects.filter(email=email).exists():
raise ValidationError("Email exists")
return self.cleaned_data
Update
If you are in mid project, then you can follow the documentation on how to change migration, in short which is to:
Backup you DB
Create a custom user model identical to auth.User, call it User (so many-to-many tables keep the same name) and set db_table='auth_user' (so it uses the same table)
Delete all Migrations File(except for __init__.py)
Delete all entry from table django_migrations
Create all migrations file using python manage.py makemigrations
Run fake migrations by python manage.py migrate --fake
Unset db_table, make other changes to the custom model, generate migrations, apply them
But if you are just starting, then delete the DB and migrations files in migration directory except for __init__.py. Then create a new DB, create new set of migrations by python manage.py makemigrations and apply migrations by python manage.py migrate.
And for references in other models, you can reference them to settings.AUTH_USER_MODEL to avoid any future problems. For example:
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.DO_NOTHING)
It will automatically reference to the current User Model.
Here is a working code
Use the below code snippets in any of your models.py
models.py
from django.contrib.auth.models import User
User._meta.get_field('email')._unique = True
django version : 3.0.2
Reference : Django auth.user with unique email
Working Code for Django 3.1
models.py
from django.contrib.auth.models import User
User._meta.get_field('email')._unique = True
SETTINGS.PY
AUTHENTICATION_BACKENDS = [
'django.contrib.auth.backends.ModelBackend'
]
There is a great example of this in Django's docs - https://docs.djangoproject.com/en/2.1/topics/auth/customizing/#a-full-example.
You have to declare the email field in your AbstractBaseUser model as unique=True.
class MyUser(AbstractBaseUser):
email = models.EmailField(
verbose_name='email address',
max_length=255,
unique=True,
)
date_of_birth = models.DateField()
is_active = models.BooleanField(default=True)
is_admin = models.BooleanField(default=False)
Easy way:
you can user signal
Example
from django.db.models.signals import post_save, pre_save
from django.dispatch import receiver
from django.contrib.auth.models import User
from django.forms import ValidationError
#receiver(pre_save, sender=User)
def check_email(sender, instance, **kwargs):
email = instance.email
if sender.objects.filter(email=email).exclude(username=instance.username).exists():
raise ValidationError('Email Already Exists')
You might be interested in:
django-user-unique-email
Reusable User model with required unique email field and mid-project support.
It defines custom User model reusing of the original table (auth_user) if exists. If needed (when added to existing project), it recreates history of applied migrations in the correct order.
I'll appreciate any feedback.
A better way of doing then using AbstractBaseUser
#forms.py
from django.core.exceptions import ValidationError
from django.contrib.auth.models import User
from django.contrib.auth.form import UserCreationForm
from some_app.validators import validate_email
def validate_email(value):
if User.objects.filter(email = value).exists():
raise ValidationError((f"{value} is taken."),params = {'value':value})
class UserRegistrationForm(UserCreationForm):
email = forms.EmailField(validators = [validate_email])
class Meta:
model = User
fields = ['username', 'email', 'password1', 'password2']
In case of use CustomUser model inherit from AbstractBaseUser you can override the full_clean() method to validate unique constraints on the model fields you specified unique=True. This is safer than form (i.e. FormClass) validation.
Example:
from django.contrib.auth.models import AbstractBaseUser
from django.db import models
class CustomUser(AbstractBaseUser):
email = models.EmailField(unique=True)
# ...
def full_clean(self, **kwargs):
"""
Call clean_fields(), clean(), and validate_unique() on the model.
Raise a ValidationError for any errors that occur.
"""
super().full_clean()
Note: Tested on Django 3.1
Improvement for solution with form validation
Instead of raising a ValidationError, it would be better to use the add_error method so that all errors of the forms are sent, and not only the one raised by ValidationError.
class SignUpForm(UserCreationForm):
email = forms.EmailField(max_length=254, help_text='Required. Inform a valid email address.')
class Meta:
model = User
fields = ('username', 'email', 'password1', 'password2', )
def clean(self):
cleaned_data = super().clean()
email = cleaned_data.get('email')
if User.objects.filter(email=email).exists():
msg = 'A user with that email already exists.'
self.add_error('email', msg)
return self.cleaned_data
You can edit model in meta as follow
Note: This will not update the original model
class SignUpForm(UserCreationForm):
email = forms.EmailField(required=True)
class Meta:
model = User
model._meta.get_field('email')._unique = True
fields = ("username", "email", "password1", "password2")

User Model Serializer required Validation Error

I want to create a registration app for my project.
Here is my serializer:
from rest_framework import serializers
from rest_framework.validators import UniqueValidator
from django.contrib.auth.models import User
from rest_framework import serializers
from django.contrib.auth import get_user_model # If used custom user model
UserModel = get_user_model()
class UserSerializer(serializers.ModelSerializer):
password = serializers.CharField(write_only=True)
def create(self, validated_data):
user = UserModel.objects.create(
username=validated_data['username'],
email=validated_data['email'],
first_name=validated_data['first_name'],
last_name=validated_data['last_name']
)
user.set_password(validated_data['password'])
return user
class Meta:
model = User
fields = ('id', 'username', 'password','email','first_name','last_name')
write_only_fields = ('password',)
read_only_fields = ('id',)
As you see, I use UserModel which is one of the default models of rest_framework. I want to make first_name field required for my registration serializer.
Waiting for your help.
You need to specify required=True attrubute for first_name field:
class UserSerializer(serializers.ModelSerializer):
first_name = serializers.CharField(required=True)
password = serializers.CharField(write_only=True)

Delete related models with Django Rest Framework API

When I use the Django Rest Framework to delete a User, the associated UserProfile object also gets deleted. I would like for the reverse relationship to also be true. How can I do this?
I have a Django model that represents a User's profile.
class UserProfile(models.Model):
user = models.OneToOneField(User, related_name='profile')
preferred_name = models.CharField(max_lengths=100)
#other fields here
Here are my Views:
class UserDetail(generics.RetrieveUpdateDestroyAPIView):
"""
API endpoint that represents a single user.
"""
model = User
serializer_class = UserSerializer
class UserProfileDetail(generics.RetrieveUpdateDestroyAPIView):
"""
API endpoint that represents a single UserProfile
"""
model = UserProfile
serializer_class = UserProfileSerializer
And the serializers:
class UserSerializer(serializers.HyperlinkedModelSerializer):
profile = serializers.HyperlinkedRelatedField(view_name = 'userprofile-detail')
class Meta:
model = User
fields = ('url', 'username', 'email', 'profile')
class UserProfileSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = UserProfile
fields = ('url', 'preferred_name', 'user')
You could overwrite the delete method at your UserProfile class, like this:
class UserProfile(models.Model):
user = models.OneToOneField(User, related_name='profile')
preferred_name = models.CharField(max_lengths=100)
#other fields here
def delete(self, *args, **kwargs):
self.user.delete()
super(UserProfile, self).delete(*args, **kwargs)

Categories

Resources