Hi StackOverFlow buddies,
I have created a custom User model for my project and would like to register the Users with Restframework.
I want Custom User model to have 2 unique fields together, for which I followed "Official doc" for unique_together property. It seems to be only taking only 1 field (ie email for my case), as a unique one.
Relevant piece my code until this point looks like this:
PS: Let me know if more info is required.
models.py
class MasterUser(AbstractBaseUser):
email = models.EmailField(verbose_name='email address',max_length=255,unique=True,)
firstname = models.CharField(max_length=100,blank=True)
contact = models.CharField(max_length=100, blank=True)
objects = MyUserManager()
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['firstname']
class Meta:
unique_together = (('email', 'contact'),)
serializer.py
class RegisterUserSerializer(serializers.ModelSerializer):
password2 = serializers.CharField(style={'input_type': 'password'}, write_only= True)
class Meta:
model = MasterUser
fields = ('firstname', 'password', 'password2', 'email','contact')
extra_kwargs = {
'password': {'write_only': True},
}
def save(self):
account = MasterUser(
email = self.validated_data['email'],
firstname = self.validated_data['firstname'],
contact = self.validated_data['contact'],
)
password = self.validated_data['password']
password2 = self.validated_data['password2']
if password != password2:
raise serializers.ValidationError({'password': 'Password doesnt matches'})
account.set_password(password)
account.save()
return account
views.py
#api_view(['POST'])
def registration_view(request):
if request.method == "POST":
serializer = RegisterUserSerializer(data= request.data)
data = {}
if serializer.is_valid():
account = serializer.save()
data['response'] = "Successfully registered new user!"
else:
data = serializer.errors
return Response(data)
Where am I missing to implement thing?
unique_together means the set of fields you defined should be unique for all Users. If you also want contact to be a unique field when stored in db, you should pass the unique parameter like so:
class MasterUser(AbstractBaseUser):
...
contact = models.CharField(max_length=100, blank=True, unique=True)
...
Related
I have a custom User model and a function in views.py to handle the registration. when I register a user with Postman for example all the user data is stored in the database correctly but the password -field remains empty. I think that causes the problem when I try to use a simple login -page - it never accepts my credentials (because there is no password in the database). Any ideas why the password isn't saved and how to fix it?
I have a function like this in views.py when registering a new user:
def register(response):
if response.method == 'POST':
form = RegisterForm(response.POST)
if form.is_valid():
user = form.save()
user.refresh_from_db()
user.id = form.cleaned_data.get('id')
user.save()
username = form.cleaned_data.get('email')
password = form.cleaned_data.get('password')
user = authenticate(username=email, password=password)
return HttpResponse(status=201)
else:
form = RegisterForm()
return HttpResponse(status=400)
And this is my custom user model:
class UserManager(BaseUserManager):
def create_user(self, email, password):
"""
Creates and saves a User with the given email and password.
"""
if not email:
raise ValueError('A user must have a email.')
user = self.model(
email=self.normalize_email(email),
)
user.set_password(password)
user.save(using=self._db)
return user
Edit. Here is my RegisterForm:
User = get_user_model()
class RegisterForm(forms.ModelForm):
password = forms.CharField(widget=forms.PasswordInput)
password_2 = forms.CharField(label='Confirm Password', widget=forms.PasswordInput)
class Meta:
model = User
fields = ['email', 'id', 'gender', 'height', 'weight']
def clean_email(self):
'''
Verify email is available.
'''
email = self.cleaned_data.get('email')
qs = User.objects.filter(email=email)
if qs.exists():
raise forms.ValidationError("email is taken")
return email
def clean_id(self):
'''
Verify id is available.
'''
id = self.cleaned_data.get('id')
qs = User.objects.filter(id=id)
if qs.exists():
raise forms.ValidationError("id is taken")
return id
def clean(self):
'''
Verify both passwords match.
'''
cleaned_data = super().clean()
password = cleaned_data.get("password")
password_2 = cleaned_data.get("password_2")
if password is not None and password != password_2:
self.add_error("password_2", "Your passwords must match")
return cleaned_data
And here is the model:
class CustomUser(AbstractBaseUser):
is_active = models.BooleanField(default=True)
staff = models.BooleanField(default=False) # a admin user; non super-user
admin = models.BooleanField(default=False) # a superuser
gender = models.CharField(null=True, blank=True, max_length=20)
height = models.CharField(null=True, blank=True, max_length=3)
id = models.CharField(primary_key=True, unique=True, blank=False, max_length=100)
email= models.EmailField(max_length=100, unique=True, null=False, blank=False)
weight = models.CharField(max_length=3, null=True, blank=True)
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = [] # Email & Password are required by default.
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 __str__(self):
return self.email
def has_perm(self, perm, obj=None):
"Does the user have a specific permission?"
# Simplest possible answer: Yes, always
return True
def has_module_perms(self, app_label):
"Does the user have permissions to view the app `app_label`?"
# Simplest possible answer: Yes, always
return True
#property
def is_staff(self):
"Is the user a member of staff?"
return self.staff
#property
def is_admin(self):
"Is the user a admin member?"
return self.admin
objects = UserManager()
And soon after editing my question I noticed I was missing the save function from the RegisterForm class. Adding this solved the problem:
def save(self, commit=True):
user = super().save(commit=False)
user.set_password(self.cleaned_data["password"])
if commit:
user.save()
return user
I have this Serializer:
class PersonSerializer(serializers.ModelSerializer):
class Meta:
model = Person
fields = '__all__'
extra_kwargs = {'password': {'write_only': True}}
def create(self, validated_data):
name = validated_data['name'],
id = validated_data['id'],
user = Person.objects.create(name=name,
password=validated_data['password'],
id=id)
return user
I thought that passwords are automatically saved as hashs in the database but with this serializer the password is saved in a raw format. How could I modify this to save the hash, not the raw version?
Edit. Here is my Person model and my UserManager:
class UserManager(BaseUserManager):
def create_user(self, name, password):
user.set_password(password)
user.save(using=self._db)
return user
class Person(AbstractBaseUser):
id = models.CharField(primary_key=True, unique=True, blank=False, max_length=100)
name = models.CharField(max_length=100, unique=True, null=False, blank=False)
USERNAME_FIELD = 'name'
REQUIRED_FIELDS = []
objects = UserManager()
As you can see, the name is here used as the username. Where do you define the password hashing, in serializer or in my models.py?
You should set the password like this.
user = Person(name=name, id=id)
user.set_password(validated_data['password'])
user.save()
set_password method takes care of hashing.
I create a model but I need a non-editable model. When I visit the model it's just displaying text, not giving any option of update the record in admin panel.
model.py
class Contact(models.Model):
name = models.CharField(max_length=160)
email = models.EmailField()
subject = models.CharField(max_length=255)
message = models.TextField()
def __str__(self):
return self.subject
While searching I get information about readonly but I do not still understand how to use it.
There are two ways to do this. One, make editable=False for all the fields, then it will not be edible anywhere(modelform and adminsite):
class Contact(models.Model):
name = models.CharField(max_length=160, editable=False)
email = models.EmailField(editable=False)
subject = models.CharField(max_length=255, editable=False)
message = models.TextField(editable=False)
Two, in adminsite, use readonly_fields:
class ContactAdmin(admin.ModelAdmin):
readonly_fields = ('name', 'email', 'subject', 'message')
admin.site.register(Contact, ContactAdmin)
I am trying to list users and at the same time with the groups added to each one of them.
How can I do this?
Below is an example I'm trying to do.
Thank you.
Serializer
class serializerUser(serializers.Serializer):
id = serializers.PrimaryKeyRelatedField(read_only=True)
first_name = serializers.CharField()
last_name = serializers.CharField()
groups = serializers.CharField()
password = serializers.CharField(write_only=True)
username = serializers.CharField(write_only=True)
views
class UserList(ListAPIView):
pagination_class = LimitOffsetPagination
serializer_class = serializerUser
queryset = User.objects.all()
def get_queryset(self):
users = User.objects.select_related('group').all()
return users
Error:
django.core.exceptions.FieldError: Invalid field name(s) given in
select_related: 'gsroup'. Choices are: (none)
You already got them in user.groups. If you want to show them along with the user, add the groups field to the Meta class fields list, for example:
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = (
"username",
"first_name",
"last_name",
"email",
"groups",
)
I am developing a simple LMS system where I have 3 stakeholders Administrator, Faculty Members and Students. Administrator can create, edit, delete, block and list user accounts, with my developement so far I am able to create and list all users.
Now I am stuck at editing user profile, my requirement is when I click on any listed users it should open my extended/customized user profile in a form and I should have the ability to edit any opened user profile.
Below are my code snippets:
MODELS.PY:
from django.contrib.auth.models import User
class UserInformation(models.Model):
user = models.ForeignKey(User, unique=True)
degree = models.ForeignKey(Degree, null=True, blank=True)
stakeholder = models.ForeignKey(Stakeholder)
cell_number = models.CharField(max_length=32, null=True, blank=True)
def __str__(self):
return self.user.username
VIEWS.PY (to create user):
def ad_create_user(request):
if request.method == 'POST':
firstname = request.POST['firstname']
lastname = request.POST['lastname']
username = request.POST['username']
password = request.POST['password']
email = request.POST['email']
group = request.POST['group']
degree = request.POST['degree']
cell_no = request.POST['cell_no']
new_user = User.objects.create_user(username, email, password)
new_user.first_name = firstname
new_user.last_name = lastname
new_user.save()
if group == 'option_one':
set_group = 3
new_user.groups.add(3)
userinfo = UserInformation(user=User.objects.get(username=username), degree=Degree.objects.get(pk=degree),
stakeholder=Stakeholder.objects.get(pk=set_group), cell_number=cell_no)
userinfo.save()
if group == 'option_two':
set_group = 2
new_user.groups.add(2)
userinfo = UserInformation(user=User.objects.get(username=username),
stakeholder=Stakeholder.objects.get(pk=set_group), cell_number=cell_no)
userinfo.save()
if group == 'option_three':
set_group = 1
new_user.groups.add(1)
userinfo = UserInformation(user=User.objects.get(username=username),
stakeholder=Stakeholder.objects.get(pk=set_group), cell_number=cell_no)
userinfo.save()
return HttpResponseRedirect('/administrator/user_management/')
return render(request, 'MobiApp/create_user.html', {'form': CreateUserForm()})
FORMS.PY (to create user):
class CreateUserForm(forms.Form):
firstname = forms.CharField(max_length=64)
lastname = forms.CharField(max_length=64)
username = forms.CharField(max_length=16)
password = forms.CharField(
widget=forms.PasswordInput(),
)
email = forms.EmailField()
group = forms.ChoiceField(
choices=(
('option_one', "Student"),
('option_two', "Faculty Member"),
('option_three', "Administrator"),
),
widget = forms.RadioSelect,
initial = 'option_one',
)
degree = forms.ModelChoiceField(queryset=Degree.objects.all())
cell_no = forms.CharField()
helper = FormHelper()
helper.form_class = 'form-horizontal'
helper.layout = Layout(
Field('firstname', css_class='input-xlarge'),
Field('lastname', css_class='input-xlarge'),
Field('username', css_class='input-xlarge'),
Field('password', css_class='input-xlarge'),
Field('email', css_class='input-xlarge'),
'group',
'degree',
Field('cell_no', css_class='input-xlarge'),
FormActions(
Submit('create', 'Create!', css_class="btn-primary"),
)
)
I found many questions similar to this but none of them helped me and my requirement is also little different as user is not editing his/her profile but administrator is editing profile of any user.
Just for your information:
I am using Django-Crispy-Forms
When I click on any user of edit profile, its URL will be something like this http://myapp.com/administrator/edit_user/11
where 11 is the user id
Thanks in advance, let me know if you need any further information.
EDIT:Here's my final proposed solution (charlesliam also made a reference to this in his comment)
We will subclass AbstractUser to add extra fields to the user. Obviously, there's other ways of doing this, but using AbstractUser should suffice your requirements. You'd have to syncdb for model changes to propagate.
SETTINGS.PY
AUTH_USER_MODEL = 'app.UserInformation' # Points to our custom User model which we will define in models.py
MODELS.PY
from django.contrib.auth.models import AbstractUser
class UserInformation(AbstractUser):
# user = models.ForeignKey(User, unique=True) <---- Remove this field
degree = models.ForeignKey(Degree, null=True, blank=True)
stakeholder = models.ForeignKey(Stakeholder)
cell_number = models.CharField(max_length=32, null=True, blank=True)
FORMS.PY
class EditUserForm(forms.ModelForm): #fixed typo. It's forms.ModelForm, not models.ModelForm
class Meta:
model = UserInformation
VIEWS.PY
from forms.py import EditUserForm
def edit_user (request, id):
user = User.objects.get(id=11)
if request.method == 'POST': #If form has been submitted
form = EditUserForm(request.POST, instance=user)
if form.is_valid(): #All good. Validation passed
form.save()
return HttpResponseRedirect('/your-view/') # Redirect after POST
else:
form = EditUserForm(instance=user) # Unbound form
return render(request, 'MobiApp/edit_user.html', {'form': form})
edit_user.html
{% load crispy_forms_tags %}
{% crispy form %}
will give you a pre-populated form with the user instance, which you can edit and POST back.
Now with regards to user authentication, you can check if the currently logged in user is superuser (administrator), and proceed with the form editing logic.
So in your VIEWS.PY:
def edit_user (request, id):
current_user = request.user
if current_user.is_superuser:
...
Alternatively, you can assign the currently logged-in user to any group and then check if the user is member of that group, then proceed with form editing.
I hope that helps.
REFERENCES:
https://docs.djangoproject.com/en/1.6/topics/forms/modelforms/
https://github.com/django/django/blob/master/django/contrib/auth/models.py#L353
https://docs.djangoproject.com/en/dev/topics/auth/customizing/
When to use the Custom User Model in Django 1.5