Ok so here is the code, I will explain what I try to do at the bottom of the page. I am just starting out with python and django please keep that in mind.
Models.py (there is more in here but its not important):
STAFF_TYPES = (
('OG', _('Organizer')),
('MO', _('Moderator')),
('SC', _('Scanner'))
)
class StaffMember(models.Model):
"""
Staff member class
"""
user = models.ForeignKey(User, verbose_name=_('user'))
staff_type = models.CharField(
max_length=3,
choices=STAFF_TYPES,
verbose_name=_('staff type')
)
forms.py:
class StaffMemberForm(forms.ModelForm):
user = forms.ModelChoiceField(User.objects.all(), label=_('User'), required=False)
username = forms.CharField(label=_('Username'), required=False)
first_name = forms.CharField(label=_('First name'), required=False)
last_name = forms.CharField(label=_('Last name'), required=False)
email = forms.EmailField(label=_('Email'), required=False)
def passwordRandom(string_length=10):
random = str(uuid.uuid4())
random = random.upper()
random = random.replace("-", "")
return random[0:string_length]
class Meta:
model = StaffMember
fields = ('staff_type',)
def clean(self):
if self.errors:
return self.cleaned_data
super(StaffMemberForm, self).clean()
cleaned_data = self.cleaned_data
username = cleaned_data.get("username")
first_name = cleaned_data.get("first_name")
last_name = cleaned_data.get("last_name")
email = cleaned_data.get("email")
user = cleaned_data.get("user")
staff_type = cleaned_data.get("staff_type")
check = [username, user]
if staff_type:
if any(check) and not all(check):
return cleaned_data
raise forms.ValidationError(_('Choose a user from the dropdown OR make a new user'))
def save(self, commit=True):
if self.cleaned_data.get("user") is None:
User.username = self.cleaned_data['username']
User.first_name = self.cleaned_data['first_name']
User.last_name = self.cleaned_data['last_name']
User.email = self.cleaned_data['email']
User.password = self.passwordRandom(8)
StaffMember.user = User
User.save()
else:
StaffMember.user = self.cleaned_data.get("user")
StaffMember.save()
Ok so essentially what I am trying to do here is create the option to either choose an existing user from the dropdown or create one by using the forms, therefore the fields can't be required because they can both be empty (not at the same time though).
So I want the form to save a new user and use that user to connect to the staffmember when they choose to make a new one. How would I do this because so far I'm only getting an error.
Thanks in advance,
Aaron
PS. Sorry for the messy code, just starting out as I stated before
You need to create a new User object in your save() method
def save(self, commit=True):
# Run the default save method, commit=False stops the
# model saving to the db
staff_member = super(StaffMemberForm, self).save(commit=False)
if self.cleaned_data.get("user") is None:
# Create a new User object
user = User()
user.username = self.cleaned_data['username']
user.first_name = self.cleaned_data['first_name']
user.last_name = self.cleaned_data['last_name']
user.email = self.cleaned_data['email']
user.password = self.passwordRandom(8)
# Save new user
user.save()
# Apply the new user to the staff_member object
staff_member.user = user
else:
staff_member.user = self.cleaned_data.get("user")
# If the form was expecting to save the StaffMember then save
if commit:
staff_member.save()
return staff_member
UPDATE: Modified code to reflect comments
Related
Model.py
class UserProfileInfo(models.Model):
user = models.OneToOneField(User,on_delete=models.CASCADE,default='')
# user = models.OneToOneField(User, on_delete=models.CASCADE)
phone_no = models.CharField(max_length=13,unique=True)
registered = models.BooleanField(default=False)
spam = models.BooleanField(default=False)
def __str__(self):
return self.user.username
Serializer.py
class UserSerializer(serializers.ModelSerializer):
password = serializers.CharField()
class Meta():
model = User
fields = ('username','email','password')
class UserProfileSerializer(serializers.ModelSerializer):
class Meta():
model = UserProfileInfo
fields = ('phone_no',)
views.py
def register(request):
registered = False
if request.method == 'POST':
user_serializer = UserSerializer(data=request.POST)
profile_serializer = UserProfileSerializer(data=request.POST)
if user_serializer.is_valid() and profile_serializer.is_valid():
user = user_serializer.save()
user.set_password(user.password) #saving hash value of password
user.save()
profile = profile_serializer.save(commit=False)
profile.user = user
profile.registered = True
profile.save()
registered = True
else:
print(user_serializer.errors)
else:
user_serializer = UserSerializer
profile_serializer = UserProfileSerializer
return Response(request,'basic_app/registration.html',{
'user_serializer':user_serializer,
'profile_form':profile_form,
'registered':registered
})
def user_login(request):
if request.method == 'POST':
username = request.POST.get('username')
password = request.POST.get('password')
print(username,password)
user = authenticate(username=username, password=password)
print(user)
if user:
if user.is_active:
login(request,user)
return render(request,'basic_app/search.html')
else:
return HttpResponse('Account not active!')
else:
# print(username,password)
return HttpResponse('Login credentials not correct!')
else:
return render(request,'basic_app/login.html')
Now I need to make changes to my views.py such that I can parallely populate the user and the profile model having one to one field. I was using the form which was working well but now I need to convert the code to rest API. Please help how I can keep the same functionality using the above serializer class. My aim is to register and login the user.
First, c method in the serializer like my simple code:
(I used to write this class in my REST project for register student)
class StudentRegistrationSerializer(serializers.ModelSerializer):
password = serializers.CharField(style={'input_type': 'password'}, required=True)
password2 = serializers.CharField(style={'input_type': 'password'}, required=True)
class Meta:
model = Student
fields = ['phone_number', 'password', 'password2', 'name', 'family']
extra_kwargs = {
'password': {'write_only': True}
}
def save(self, **kwargs):
phone_number = self.validated_data.get('phone_number')
name = self.validated_data.get('name')
family = self.validated_data.get('family')
password = self.validated_data.get('password')
password2 = self.validated_data.get('password2')
not_active = User.objects.filter(username=phone_number, is_active=False)
is_active = User.objects.filter(username=phone_number, is_active=True)
if password != password2:
raise serializers.ValidationError({'error': 'password not matched'})
if is_active:
raise serializers.ValidationError({'error': 'username already exist'})
if not_active:
raise serializers.ValidationError({'error': 'username already exist active your account'})
user = User(username=phone_number, password=password, is_active=False)
user.set_password(password)
user.save()
student = Student(phone_number=phone_number, user=user, name=name, family=family)
student.save()
return student
and return the saved object here is student
and I recommend you to use build-in LoginView in the third-party library like "rest_auth" this library handles Token
after installing "rest_auth" using :
pip install django-rest-auth
in the url file just import :
from rest_auth.views import LoginView
and use it without any complexity
I am trying to post a simle form to create a user. but whenever i try to save the form data it always gives me UNIQUE constraint failed error even if i pass the new mobile number that does not exist on database.
ERROR IS: UNIQUE constraint failed: core_user.mobile_no
models.py
Manager Class is:
class UserManager(BaseUserManager):
def create_user(self, username, password=None, **extra_fields):
"""Creates and saves a new user"""
if not password:
raise ValueError("User must have a password")
if not username:
raise ValueError("User must have an username")
user = self.model(username=username, **extra_fields)
user.set_password(password)
user.save(using=self.db)
return user
def create_staff_user(self, username, password=None, **kwargs):
user = self.create_user(username, password, is_staff=True, **kwargs)
return user
def create_super_user(self, username, password=None):
user = self.create_user(self, username=username, password=password, is_staff=True, is_super_user=True)
return user
Model class is:
class User(AbstractBaseUser):
user_types = (
("staff", "Staff"),
("super_user", "Super User"),
)
first_name = models.CharField(max_length=100)
middle_name = models.CharField(max_length=100)
last_name = models.CharField(max_length=100)
username = models.CharField(max_length=100, unique=True)
email = models.EmailField()
mobile_no = models.CharField(max_length=10, unique=True)
is_active = models.BooleanField(default=True) # can login
is_staff = models.BooleanField(default=False) # staff user
is_super_user = models.BooleanField(default=False) # super user
created_date = models.DateTimeField(auto_now_add=True)
USERNAME_FIELD = 'username'
objects = UserManager()
# USERNAME_FIELD and password are required by default
REQUIRED_FIELDS = [] # e.g full_name
def __str__(self):
return self.username
Views.py
class UserCreationView(CreateView):
template_name = "form.html"
form_class = UserCreationForm
success_url = "/"
def form_valid(self, form):
username = form.cleaned_data['username']
password = form.cleaned_data['password']
first_name = form.cleaned_data['first_name']
middle_name = form.cleaned_data['middle_name']
last_name = form.cleaned_data['last_name']
mobile_no = form.cleaned_data['mobile_no']
email = form.cleaned_data['email']
user_type = form.cleaned_data['user_type']
user_data = {
"first_name": first_name,
"middle_name": middle_name,
"last_name": last_name,
"mobile_no": mobile_no,
"email": email
}
if user_type == 'super-user':
user = User.objects.create_super_user(username, password, **user_data)
else:
user = User.objects.create_staff_user(username, password, **user_data)
form.instance.user = user
form.instance.is_active = True
form.save()
return super().form_valid(form)
Questions are:-
As far as i tried to debug, it could be the reason that create_staff_user and create_super_user functions have already created a row in database and now form.save() is also trying to insert the row again. (not sure)
do i need to do form.save() as i found that super().form_valid(form) also have implemented form saving function within it ?
In your view, you should only call save once, but you are calling it twice
form.save() # this line saves it to the database
super().form_valid(form) # and this line does that too
so after calling form.save() return response.
update, your code to
class UserCreationView(CreateView):
template_name = "form.html"
form_class = UserCreationForm
success_url = "/"
def form_valid(self, form):
username = form.cleaned_data['username']
password = form.cleaned_data['password']
first_name = form.cleaned_data['first_name']
middle_name = form.cleaned_data['middle_name']
last_name = form.cleaned_data['last_name']
mobile_no = form.cleaned_data['mobile_no']
email = form.cleaned_data['email']
user_type = form.cleaned_data['user_type']
user_data = {
"first_name": first_name,
"middle_name": middle_name,
"last_name": last_name,
"mobile_no": mobile_no,
"email": email
}
if user_type == 'super-user':
user = User.objects.create_super_user(username, password, **user_data)
else:
user = User.objects.create_staff_user(username, password, **user_data)
form.instance.user = user
form.instance.is_active = True
form.save()
return HttpResponseRedirect(self.get_success_url())
mobile number should be unique for every user
class UserForm(forms.ModelForm):
class Meta:
model = User
exclude = ('is_staff', 'is_superuser',)
def clean_mobile_no(self):
mobile_number = self.cleaned_data.get('mobile_no')
user = User.objects.filter(mobile_no=mobile_number)
if user:
raise forms.ValidationError(
"mobile no is taken"
)
return mobile_number
Well, as for your first question, it could be the problem. I guess before you created a custom User model, you makes migrations and pushed them to your database. So, that is the part where Django also creates its own User model, with all the available columns and attributes. I would suggest to DELETE your currently Users table, and run the makemigrations and migrate again.
As for your second question, the best practice and advice that I could give you is to first add an if clause, to check if the form data are valid, and then save the form and post the data. Although my advice does not really relate to your question, the point is to always validate the form's data and after you retrieve them, save the form (post the data).
In the documentation it says that the FormView class on success will redirect the user and on error, it will redisplay the form. However, the CreateView will only display the errors and save the object, it will not redirect to anything. Although with the CreateView you can automatically save the form and its data, it will not redirect the user. I suggest you using the FormView class that will show if there any errors and will redirect the user on success, but be careful and save the form data at the end of the POST function.
I hope that helps! Please let me know if there is anything else I can help you with.
I have created a Clients model in models.py that is intended to be a Client (user) Profile.
class Clients(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
first_name = models.CharField(max_length=30, verbose_name="Primeiro Nome")
last_name = models.CharField(max_length=30, verbose_name="Apelido")
address = models.CharField(max_length=200, verbose_name="Morada")
nif = models.CharField(max_length=9, verbose_name="NIF", validators=[RegexValidator(r'^\d{1,10}$')], primary_key=True)
mobile = models.CharField(max_length=9, verbose_name="Telemóvel", validators=[RegexValidator(r'^\d{1,10}$')])
email = models.CharField(max_length=200, null=True, verbose_name="Email")
avatar = models.ImageField(null=True)
def __str__(self):
return "%s %s" % (self.first_name, self.last_name)
class Meta:
verbose_name_plural = "Clientes"
#receiver(post_save, sender=User)
def update_user_profile(sender, instance, created, **kwargs):
if created:
Clients.objects.create(user=instance)
instance.profile.save()
This model is connected to the Django Users through a OneToOneField called user.
I created a form that is capable of adding data to the Clients model in forms.py:
from django import forms
from django.contrib.auth.forms import UserCreationForm
from django.contrib.auth.models import User
from django.core.validators import RegexValidator
class SignUpForm(UserCreationForm):
first_name = forms.CharField(max_length=30, label="Primeiro Nome")
last_name = forms.CharField(max_length=30, label="Apelido")
address = forms.CharField(max_length=200, label="Morada")
nif = forms.CharField(max_length=9, label="NIF", validators=[RegexValidator(r'^\d{1,10}$')])
mobile = forms.CharField(max_length=9, label="Telemóvel", validators=[RegexValidator(r'^\d{1,10}$')])
email = forms.CharField(max_length=200, label="Email")
class Meta:
model = User
fields = ('username', 'password1', 'password2', 'first_name', 'last_name', 'address', 'nif', 'mobile', 'email')
How can I, through this single form, add a username and password field so that, through the OneToOneField, it creates an user connected to this profile?
EDIT
The new version of the files above. Now, it creates the user, but all other fields for the Clients get passed empty.
My views.py
def signup(request):
if request.method == 'POST':
form = SignUpForm(request.POST)
if form.is_valid():
user = form.save()
user.refresh_from_db() # load the profile instance created by the signal
user.first_name = form.cleaned_data.get('first_name')
user.last_name = form.cleaned_data.get('last_name')
user.address = form.cleaned_data.get('address')
user.nif = form.cleaned_data.get('nif')
user.mobile = form.cleaned_data.get('mobile')
user.email = form.cleaned_data.get('email')
user.save()
raw_password = form.cleaned_data.get('password1')
user = authenticate(username=user.username, password=raw_password)
login(request, user)
return redirect('clientes')
else:
form = SignUpForm()
return render(request, 'backend/new_client.html', {'form': form})
The fields you added are just regular form fields, django doesn't know anything about where to save these values. So you need to override the form's save() method or save them in your view. Here's how to save them in your view, since this is what you started to do:
if form.is_valid():
user = form.save() # this creates the user with first_name, email and last_name as well!
user.refresh_from_db() # load the profile instance created by the signal
user.clients.address = form.cleaned_data.get('address')
user.clients.nif = form.cleaned_data.get('nif')
user.clients.mobile = form.cleaned_data.get('mobile')
user.clients.save()
login(request, user)
return redirect('clientes')
Note: I don't do anything with first_name, last_name and email in the view, they are fields of the User model so they will already be saved automatically when you do form.save(). You should remove them from your Clients model.
Note 2: Renaming your Clients model to Client would make your code more readable. You should always use singular for your models. This way you can do user.client.address which makes more sense than user.clients.address since it's a one-to-one field.
Alternatively, you can override the form's save() method, which is a method I would prefer as I don't think the view should care about how to save the user's profile:
# in SignupForm(UserCreationForm):
def save(self, commit=True):
user = super().save(commit) # this creates the new user
if commit:
user.refresh_from_db() # not sure if this is needed
user.clients.nib = self.cleaned_data.get('nib')
user.clients.address = self.cleaned_data.get('address')
user.clients.mobile = self.cleaned_data.get('mobile')
user.clients.save()
return user
I'm trying to store the First name and last name straight from the Facebook API to a User Auth model (which is extended with FacebookProfile model, containing webpull, id and year_formed)
Models.py
class FacebookProfile(models.Model):
user = models.OneToOneField(User, on_delete = models.CASCADE)
id = models.PositiveIntegerField(primary_key = True)
#name = models.CharField(max_length = 200, null = True)
year_formed = models.PositiveIntegerField(default = 0)
webpull= models.CharField(max_length =1000, null = True)
Views.py
if request.method == 'GET':
print 'im here'
return render(request, "logV2.html")
if request.method == "POST":
first_name = request.POST.get('first_name')
last_name = request.POST.get('last_name')
print first_name, last_name
facebook_user = FacebookUserForm(data=request.POST)
facebook_profile = FacebookProfileForm()
has_account = authenticate(first_name = first_name, last_name = last_name)
if has_account:
print 'this has account'
login(request, has_account)
return HttpResponseRedirect('/music/home/')
else:
id_ = request.POST.get('id')
birthday = request.POST.get('year_formed')
webpull = request.POST.get('webpull')
if birthday == "undefined":
print 'im emplty'
year_formed = random.randint(1993,1998)
else:
year_formed = re.findall(r"[0-9][0-9][0-9][0-9]$", birthday)[0]
print id_, year_formed, webpull
print facebook_user
user = facebook_user.save()
profile = facebook_profile.save(commit = False)
profile.user = user
profile.webpull = webpull
profile.id = id_
## steal birtday fucntion from log
# move to new database facebook (neeed to change all artists to facebookprofile)
profile.year_formed = year_formed
profile.save()
#authenticate user. then log him in.
#user = authenticate(username = profile.user.username)
now_has_account = authenticate(first_name = first_name, last_name = last_name)
login(request, now_has_account)
#profile.save()
return HttpResponseRedirect('/music/home/')
In the views the code brakes at user = facebook_user.save()
I tried clearing the whole database,
also
What I'm receiving from the html is a form with first_name,last_name,id,year_formed,webpull. The data gets to the backend fine.
Forms.py
class FacebookUserForm(forms.ModelForm):
class Meta:
model = User
fields = ('first_name', 'last_name')
class FacebookProfileForm(forms.ModelForm):
class Meta:
model = FacebookProfile
fields = ('id', 'year_formed', 'webpull',)
what im authenticating
Auth_Backend.py
class OnlynameandsurnameAuth(ModelBackend):
def authenticate(self, first_name = None, last_name = None):
try:
return User.objects.get(first_name = first_name, last_name = last_name)
except:
return None
then
auth.py
admin.site.register(FacebookProfile)
the backend authentification
settings.py
AUTHENTICATION_BACKENDS = (
# ... your other backends
#'music.auth_backend.PasswordlessAuthBackend',
'music.auth_backend.OnlynameandsurnameAuth',
)
any ideas how to save the first_name and last_name without having a UNIQUE error?
Thanks! :D
If you use the default authenticate function not providing a password should always (?) fail, which means that
has_account = authenticate(first_name = first_name, last_name = last_name)
always will be None.
But the main problem is that you do not set a username for the new User, only first_name and last_name. This will work once, but after one User with an empty username was created the next attempt will fail, as Users need an unique username.
So: Add a username!
Besides that, I think that
user = facebook_user.save()
does not assign the User to "user" but the Form.
You should use facebook_user.instance.
Someone wants me to make a registration form with no email, and just first name, last name and date of birth. So I decided to do this:
username = request.POST['first_name'] + '__' + request.POST['last_name']
I know we need to have a unique field in Django, so a person with the same name will cause problems. But this is how he wants it done (not many users will be using it).
So how do I populate Django's AbstractBaseUser with a username as above and a default password?
The idea is to get a Doctor who is already logged in to register a new Patient with just first name, last name, and date of birth.
models.py:
class MyUser(AbstractBaseUser):
USER_TYPE = (('doc', 'Doctor'), ('pat', 'Patient'))
id = models.AutoField(primary_key=True)
first_name = models.CharField(max_length=200)
last_name = models.CharField(max_length=200)
username = models.CharField(max_length=200, unique=True, )
joined = models.DateTimeField(auto_now_add=True)
usertype = models.CharField(max_length=254, choices=USER_TYPE)
first_time = models.NullBooleanField()
is_active = models.BooleanField(default=True)
is_admin = models.BooleanField(default=False)
USERNAME_FIELD = 'username'
def __unicode__(self):
return self.username
forms.py:
class patientForm(forms.ModelForm):
class Meta:
model = MyUser
fields = ('username', 'first_name', 'last_name')
class patientProfileForm(forms.ModelForm):
class Meta:
model = Patients
fields = ('dob', 'condition')
views.py:
#login_required
def new_patient(request):
context = RequestContext(request)
registered = False
if request.method == 'POST':
patient_form = patientForm(data=request.POST)
patient_profile = patientProfileForm(data=request.POST)
if patient_form.is_valid() and patient_profile.is_valid():
username = request.POST['first_name'] + '__' + request.POST['last_name']
user = patient_form.save() #Error here: (1062, “Duplicate entry '' for key 'username'”)
user.set_password(user.password)
user.save()
profile = patient_profile.save(commit=False)
profile.user = user
profile.doctor = request.user
profile.save()
registered = True
else:
print patient_form.errors, patient_profile.errors
else:
patient_form = patientForm()
patient_profile = patientProfileForm()
return render_to_response(
'new-patient.html',
{'patient_form': patient_form, 'patient_profile': patient_profile, 'registered': registered},
context)
How do I add the username in the format mentioned earlier when saving the form? I get the error:
(1062, “Duplicate entry '' for key 'username'”) (see above where this happens)
One way to do this would be to override the clean method to set the username field until found. This might not be the exact code you need, but you get the idea.
class patientForm(forms.ModelForm):
class Meta:
model = MyUser
fields = ('username', 'first_name', 'last_name')
def clean(self):
cd = self.cleaned_data
fullname = "%s-%s" % (cd.get('first_name'), cd.get('last_name'))
username = slugify(fullname)
while True:
try:
user = User.objects.get(username=username)
except:
username = username + '-copy' #Or changes this to a counter.
else:
break
cd['username'] = fullname
return cd