django allauth custom signup form to assign different groups - django

I have two types of users in the system, I want to assign the appropriate group at the time of signing up. Referring to How to customize user profile when using django-allauth, I thought I can override the Signup form and do something like:
class CustomSignupForm(forms.Form):
login_widget = forms.TextInput(attrs={'type': 'email',
'placeholder': _('email'),
'autofocus': 'autofocus',
'class': 'form-control'
})
email = forms.EmailField(label='Email', widget=login_widget)
password = PasswordField(label='Password', widget=forms.PasswordInput(attrs={'class': 'form-control'}))
password2 = PasswordField(label='Re-type Password', widget=forms.PasswordInput(attrs={'class': 'form-control'}))
def save(self, request, user):
role = request.GET.get('type')
print(role)
group = role or "group1"
g = Group.objects.get(name=group)
user.groups.add(g)
user.save()
But I keep getting the error below:
save() missing 1 required positional argument: 'user'
Also, I have configured allauth to use email to login.
Thanks for your help.

signup is the method to override not save.
class LocalSignupForm(forms.Form):
pass
def signup(self, request, user):
role = request.session.get('user_type')
group = role or "Default"
g = Group.objects.get(name=group)
user.groups.add(g)
user.save()
Also the settings has to be
ACCOUNT_SIGNUP_FORM_CLASS = 'useraccount.forms.LocalSignupForm'

Related

Removing username from UserCreationForm and auto generating username for user based on email, first name and last name

I am trying to auto generate username for users as they sign up, however, I do not know where to set the username. I do not want to user signals, so therefore I want to set the username for users when the user signs up.
In my UserCreationForm I have remove the username by:
users = None
and inside the SingUpForm(UserCreationForm) method I have:
class SignUpForm(UserCreationForm):
class Meta:
fields = (
"first_name",
"last_name",
"email",
)
However, I do not know how to set the username for the user, should I set the username for user inside the signup view?
user = form.save(commit=False)
user.username = generate_username(user.first_name, user.last_name, user.email)
user.save()
Or should I have the generate_username() method inside my custom creation form?
Or should I have the generate_username() method inside my custom creation form?
That is probably more appropriate, since the form normally should be responsible to create a valid Userobject, you thus an override the def save method:
class SignUpForm(UserCreationForm):
class Meta:
fields = (
'first_name',
'last_name',
'email',
)
def save(self, *args, **kwargs):
user = self.cleaned_data
self.instance.username = generate_username(
user['first_name'],
user['last_name'],
user['email']
)
return super().save(*args, **kwargs)
then in the view, it is simply:
user = form.save()

Test UpdateView for useraccounts application

Test doesn't give status_code 302 in user profile UpdateView and so there's no updates occurs on the object
the model code
class User(AbstractBaseUser, PermissionsMixin):
'''
This a replaced user profile instead of the default django one
'''
language_choices=[('en',_('English')),('se',_('Swedish'))]
email=models.CharField(verbose_name=_('Email'), max_length=128, blank=False, unique=True)
first_name=models.CharField(verbose_name=_('First Name'), max_length=128)
last_name=models.CharField(verbose_name=_('Last Name'), max_length=128)
joined_at=models.DateField(
verbose_name=_('Joined at'),
auto_now_add=True,
blank=False
)
language=models.CharField(
verbose_name=_('Language'),
max_length=2,
choices=language_choices,
default=language_choices[0][0]
)
active=models.BooleanField(verbose_name=_('Active'), default=False)
is_superuser=models.BooleanField(verbose_name=_('Is Superuser'), default=False)
is_active=models.BooleanField(verbose_name=_('Is Active'), default=True)
is_staff=models.BooleanField(verbose_name=_('Is Staff'), default=False)
The form code
class EditUserForm(UserChangeForm):
'''
Profile form to update existing user information
'''
# error message for email matches
error_messages = {
'email_mismatch': _("The two email fields didn't match."),
}
# create field for email
email1 = forms.EmailField(
label=_("Email"),
widget=forms.EmailInput,
help_text=_("If you change your email your account will be inactive untill your reactivate by email link."),
)
# get the email from confirmed email field
email2 = forms.EmailField(
label=_("Confirm Email"),
widget=forms.EmailInput,
help_text=_("Enter the same email as before, for verification."),
)
# hide password field
password = ReadOnlyPasswordHashField(label="Password")
class Meta:
'''
Initial fields and model for the form
'''
model = models.User
fields = ('first_name','last_name','email1','email2', 'language')
def clean_email2(self):
'''
Method for if email and confirmed email are the same
This method works when confirmed email cleared
'''
# get the email from email field
email1 = self.cleaned_data.get("email1")
# get the email from confirmed email field
email2 = self.cleaned_data.get("email2")
# check if both emails are equal
if email1 and email2 and BaseUserManager.normalize_email(email1) != BaseUserManager.normalize_email(email2):
# give an error message if emails not matches
raise forms.ValidationError(
self.error_messages['email_mismatch'],
code='email_mismatch')
# return the confirmed email
return BaseUserManager.normalize_email(email2)
def save(self, commit=True):
'''
Method tosave the edited user data
'''
# get the initial method
user = super().save(commit=False)
# set the email on the model field
user.email = self.cleaned_data["email1"]
# save edited user data
if commit:
user.save()
return user
def __init__(self, *args, **kwargs):
'''
Method for initial values and functions for the SignUp form class
'''
# get user data from User model
user = get_user_model().objects.get(email=kwargs['instance'])
# get the initial form class values
super(EditUserForm, self).__init__(*args, **kwargs)
# Add the current email as the inital email
self.fields['email1'].initial = user.email
# Add the current email as the intial confirmed email
self.fields['email2'].initial = user.email
# Add help text in the password field for change
self.fields['password'].help_text=(
_("Raw passwords are not stored, so there is no way to see "
"this user's password, but you can change the password "
"using this form.")
.format(reverse(
'core:ChangePassword',
kwargs={'pk':user.pk})))
and the view code
class EditUser(UserPassesTestMixin, UpdateView):
'''
Class view to update user details
'''
# used template
template_name = 'core/edit.html'
# View model
model = models.User
# View form
form_class = forms.EditUserForm
def test_func(self):
return self.request.user == get_user_model().objects.get(pk=self.kwargs['pk'])
def get_success_url(self):
'''
Metho to redirect after a valid form
'''
# check if the email is verified
if self.request.user.active:
# get the user key
pk=self.request.user.pk
# redirect to profile details
return reverse_lazy('core:details', kwargs={'pk':pk})
else:
# send a verification email
return SendActivationEmail(self.request, self.request.user)
the test code
self.viewuser_url = reverse('core:details', kwargs={'pk':self.user.pk})
self.edituser_url = reverse('core:edit', kwargs={'pk':self.user.pk})
def test_edit_user_post(self):
first_name = 'Osama'
response = self.client.post(self.edituser_url,
data={
'first_name': first_name,
'last_name': self.last_name,
'email': self.email,
})
self.assertRedirects(response, self.viewuser_url)
self.user.refresh_from_db()
self.assertEqual(self.user.first_name, first_name)
I tried to get assertEqual for the status code and it gitves me 200 instead of 302
also I tried to enter the form details instead of model details and it gives me an error
The get test works fine and also permission test works great.. all the models, forms and urls test works perfect.
I don't know how I can test this..
If the form isn't valid, then the form will be re-rendered with errors and you'll get a 200 response.
To debug the problem, check response.context['form'].errors in your test to see what the problem is.
response = self.client.post(self.edituser_url,
data={
'first_name': first_name,
'last_name': self.last_name,
'email': self.email,
})
print(response.context['form'].errors
Your view uses EditUserForm, but you are not posting any values for email1 or email2, so there is probably something in the errors about missing data.

How Django-allauth signal/adapter flow works ?

I have implemented Django-allauth using Facebook as a social account provider as it gives lots of information about its users.
Below is my custom signup form:
ACCOUNT_SIGNUP_FORM_CLASS = 'profiles.forms.MySignupForm'
class MySignupForm(forms.ModelForm):
class Meta:
model = get_user_model()
fields = ['email', 'first_name', 'last_name']
def __init__(self, *args, **kwargs):
super(MySignupForm, self).__init__(*args, **kwargs)
self.helper = FormHelper()
self.fields["email"].widget.input_type = "email" # ugly hack
self.helper.form_method = "POST"
self.helper.form_action = "account_signup"
self.helper.form_id = "signup_form"
self.helper.form_class = "signup"
self.helper.layout = Layout(
Field('email', placeholder="Enter Email", autofocus=""),
Field('first_name', placeholder="Enter First Name"),
Field('last_name', placeholder="Enter Last Name"),
Field('password1', placeholder="Enter Password"),
Field('password2', placeholder="Re-enter Password"),
Submit('sign_up', 'Sign up', css_class="btn-warning"),
)
def signup(self, request, user):
user.first_name = self.cleaned_data['first_name']
user.last_name = self.cleaned_data['last_name']
user.email = self.cleaned_data['email']
user.save()
*What else can be added here in signup form ??? or to do the processing on received input i need to write adapter ??*
I have below signal receiver implemented.
#receiver(user_signed_up)
def set_initial_user_names(request, user, sociallogin=None, **kwargs):
if sociallogin:
***grab the data***
email_verified = sociallogin.account.extra_data['verified']
profile = models.Profile(user=user, avatar_url=picture_url, email_verified=email_verified)
profile.save() <--- *saving custom user profile here*
from allauth.account.models import EmailAddress
emails = EmailAddress.objects.filter(user=user, email=user.email)
for email in emails:
email.verified = email_verified
email.save() <--- *saving allauth Email Address instance.*
user.guess_display_name()
user.save() <----- *saving custom user model based on email address.*
Now if you see i am verifying if email is verified by social account(Facebook) if so i am updating Email Address instance of allauth.
But it happens twice.
allauth already updates the email address instance (account_emailaddress) during the login/sign up process. So database is getting hit twice for account_emailaddress table.
I want to control this scenario myself so that it only does it once..
Answer is Adapter, but if i write adapter, what happens to the signal receiver ??
Can someone tell me the adapter/signal flow ?? how it should be implemented. ??
I have gone through the documentation but still it would be nice if i get some direction on the flow like at which stage what i can control !!
Don't use a ModelForm for your custom signup form, as allauth needs to be in charge of constructing a User instance and saving it. Make it a plain form, simply deriving from django.forms.Form. Only use it in cases when you need to add additional inputs during signup. Looking at your form it seems you are not adding any additional fields, so you probably don't need a custom form at all.
Use the following form to ask users for their first/last name:
class SignupForm(forms.Form):
first_name = forms.CharField(max_length=30)
last_name = forms.CharField(max_length=30)
def signup(self, request, user):
user.first_name = self.cleaned_data['first_name']
user.last_name = self.cleaned_data['last_name']
user.save()

How to add additional user information while the user is logged in?

I have created a login in page and now want to redirect the user to a page to add additional user information. How do I ensure that the user is "logged in" and that the information is added to that user's profiles? I know I need to use something like user=User.objects.get(email=email) to identify the user but I don't want to have the user enter their email on every single page to identify themselves. Is there something I can pass to keep the user Logged-in?
Here is the view:
def add_user_profile(request):
# if this is a POST request we need to process the form data
if request.method == 'POST':
# create a form instance and populate it with data from the request:
form = AddUserProfileForm(request.POST)
# check whether it's valid:
if form.is_valid():
# process the data in form.cleaned_data as required
# ...
# redirect to a new URL:
return HttpResponseRedirect('/summary/')
# if a GET (or any other method) we'll create a blank form
else:
form = AddUserProfileForm()
return render(request, 'registration/add_user_profile.html', {'form': form})
Here is the form:
class AddUserProfileForm(forms.ModelForm):
this_year = datetime.date.today().year
Years = range(this_year-70, this_year+1)
required_css_class = 'required'
first_name = forms.CharField(widget=forms.TextInput(attrs={'placeholder': 'First Name'}), label="")
middle_name = forms.CharField(widget=forms.TextInput(attrs={'placeholder': 'Middle Name'}), label="")
last_name = forms.CharField(widget=forms.TextInput(attrs={'placeholder': 'Last Name'}), label="")
date_of_birth = forms.DateField(widget=forms.extras.widgets.SelectDateWidget(years=Years))
Primary_address = forms.CharField(widget=forms.TextInput(attrs={'placeholder': 'Primary address'}), label="")
Primary_address_zipcode = forms.CharField(widget=forms.TextInput(attrs={'placeholder': 'Zip Code'}), label="")
class Meta:
model = User
fields = ('first_name', 'middle_name', 'last_name', 'date_of_birth', 'Primary_address', 'Primary_address_zipcode')
Here is the url.py
url(r'^add_user_profile/$',
views.add_user_profile,
name='add_user_profile'),
You just need to create a view which will deal with request.user. If your update_profile functionality is rather simple you may take a look at UpdateView. You just need to overwrite get_object() to look like this:
def get_object(self, queryset=None):
return self.request.user
After this you just need to set up you UpdateView and create a proper template. You can read about this in the docs.

Django permissions outside of the admin and in a custom form

I have an app with 2 user levels; Superuser, and Staff. Superuser will login to this app and create a new user and assign the user either Superuser or Staff permissions, but they are not logging in through the default Django admin, they will be logging into a custom admin. How do I display the permission checkboxes in my form and how do I save them for that new user being created? Is it merely just a normal checkbox or is there something I need to override to be able to accomplish this?
Here is my CreateUserForm as it is now.
class CreateUserForm(forms.Form):
username = forms.EmailField(max_length=50)
email = forms.EmailField()
first_name = forms.CharField(max_length=150)
last_name = forms.CharField(max_length=150)
password1 = forms.CharField(max_length=30, widget=forms.PasswordInput(render_value=False), label='Password')
password2 = forms.CharField(max_length=30, widget=forms.PasswordInput(render_value=False), label='Password Confirmation')
address_1 = forms.CharField(max_length=50)
address_2 = forms.CharField(max_length=50)
city = forms.CharField(max_length=50)
province = forms.CharField(max_length=2)
country = forms.CharField(max_length=50)
postal_code = forms.CharField(max_length=10)
work_phone = forms.CharField(max_length=20)
mobile_phone = forms.CharField(max_length=20)
fax = forms.CharField(max_length=20)
url = forms.CharField()
comments = forms.CharField(widget=forms.Textarea)
def clean_username(self):
try:
User.objects.get(username=self.cleaned_data['username'])
except User.DoesNotExist:
return self.cleaned_data['username']
raise forms.ValidationError("Sorry, this username has already been taken. Please choose another.")
def clean_email(self):
try:
User.objects.get(email=self.cleaned_data['email'])
except User.DoesNotExist:
return self.cleaned_data['email']
raise forms.ValidationError("Sorry, this email has already been taken. Please choose another.")
def clean(self):
if 'password1' in self.cleaned_data and 'password2' in self.cleaned_data:
if self.cleaned_data['password1'] != self.cleaned_data['password2']:
raise forms.ValidationError("You must type the same password each time.")
if ' ' in self.cleaned_data['username']:
raise forms.ValidationError("username must not contain spaces")
return self.cleaned_data
def save(self):
new_user = User.objects.create_user(
username = self.cleaned_data['username'],
email = self.cleaned_data['email']
)
new_user.first_name = self.cleaned_data['first_name']
new_user.last_name = self.cleaned_data['last_name']
new_user.set_password(self.cleaned_data['password'])
new_user.is_active = True
new_user.save()
new_profile = UserProfile(
# TODO:
)
Thanks
If you mean the permission checkboxes for is_staff and is_superuser, then you're probably best off using a ModelForm w/ the User model. This will automatically take care of everything for you.
class UserForm(forms.ModelForm):
class Meta:
model = User
exclude = ('last_login', 'date_joined')
EDIT per OP update: You can just add 2 forms.BooleanField() fields to your form for is_superadmin and is_staff (or name them differently), and much like you're already doing in the save method you can do new_user.is_staff = self.cleaned_data['is_staff']. You may consider using a choice field instead, w/ a dropdown w/ 3 entries, "Normal User", "Staff User", "Admin User", and then set is_staff and is_superadmin according to the user's selection.
It's also still possible to use a ModelForm on the User model and just add the necessary extra fields in addition. It may or may not be worth it to you, depending how custom you're getting.
Another note about your form - when it comes to editing existing users, this won't be able to be used for that w/ the current clean methods on username/email. But if you tweak those to exclude the current instance (if set) from the lookup, you will be able to use this form for editing existing users (though since it's not a model form you'd need to populate the initial data manually).