Django related models and UpdateView fields - django

I created a model (UserSettings) to extend django's User model through a OneToOneField (as recommended by the documentation):
class UserSettings(models.Model):
user = models.OneToOneField(User, primary_key=True)
subscribeToMails = models.BooleanField(default=True)
[...]
I wish to offer my users a way to edit some of their profile data, some of which is stored in the User model (the email address), and the rest in the UserSettings model. How may I do that?
I thought of two ways: adding another OneToOneField in the UserSettings model for the email address field; or overriding the UpdateView get_queryset() method (but I'm not sure how). Is there a best or recommended way to do it? So far here's how my view look:
class EditUser(UpdateView):
model = UserSettings
fields = ('emailVisible', 'subscribeToMails', 'mpPopupNotif',
'mpEmailNotif', 'avatar', 'quote', 'website')
template_name = 'user/edit.html'
def get_object(self):
return UserSettings.objects.get(user_id=self.request.user)
def get_success_url(self):
return reverse_lazy('user:edit')

Thanks for the replies! However, since I couldn't figure out how to make this work and thought using two tables eventually resulted in too much clutter to my taste, I finally went with the easier route and subclassed AbstractUser:
# models.py
class ForumUser(AbstractUser):
subscribeToMails = models.BooleanField(default=True)
[...]
# views.py
class EditUser(LoginRequiredMixin, UpdateView):
model = ForumUser
fields = ('email', 'emailVisible', 'subscribeToMails', 'mpPopupNotif',
'mpEmailNotif', 'avatar', 'quote', 'website')
template_name = 'user/edit.html'
success_url = reverse_lazy('forum:welcome')
def get_object(self):
return ForumUser.objects.get(username=self.request.user)
I only had to change my registration form:
# forms.py
class RegisterForm(UserCreationForm):
email = forms.EmailField(required=True)
class Meta:
model = ForumUser
fields = ('username', 'email', 'password1', 'password2')
def clean_email(self):
"Ensure registered emails are unique."
email = self.cleaned_data.get('email')
username = self.cleaned_data.get('username')
if email and ForumUser.objects.filter(email=email).exclude(
username=username).count():
raise forms.ValidationError('Email address already in use.')
return email
def clean_username(self):
"""
UserCreationForm method where mentions of the User model are replaced
by the custom AbstractUser model (here, ForumUser).
https://code.djangoproject.com/ticket/19353#no1
and https://docs.djangoproject.com/en/1.7/_modules/django/contrib/
auth/forms/#UserCreationForm
"""
username = self.cleaned_data["username"]
try:
ForumUser.objects.get(username=username)
except ForumUser.DoesNotExist:
return username
raise forms.ValidationError(
self.error_messages['duplicate_username'],
code='duplicate_username',
)

Use this solution:
mix both User and UserSettings in a form like this:
class EmployeeEditForm(forms.ModelForm):
#fields from User model that you want to edit
first_name = forms.CharField(required=False, label=_('First Name'))
last_name = forms.CharField(required=False, label=_('Last Name'))
class Meta:
model = UserSettings
fields = ('first_name', 'last_name', 'subscribeToMails')
You can access to User and UserSettings object in views.py like this:
user = request.user
usersettings = user.usersettings
Now you can edit User object like this:
user.first_name = request.POST['first_name']
user.last_name = request.POST['last_name']
user.save()
And edit UserSettings like this:
usersettings.subscribeToMails = request.POST['subscribeToMails']
usersettings.save()

Formsets is the best way to go about it.
https://docs.djangoproject.com/en/dev/topics/forms/formsets/

Related

UserCreationForm extension not making fields in the model

I have extended the Usercreationform as follows:
//forms.py
class UserCreationForm(UserCreationForm):
email = EmailField(label=_("Email address"), required=True,
help_text=_("Required."))
city= forms.CharField(label= _("City"),max_length=20, required=True)
state= forms.CharField(label= _("State"),max_length=20, required=True)
class Meta:
model = User
fields = ("username", "email", "password1", "password2","city","state")
def save(self, commit=True):
user = super(UserCreationForm, self).save(commit=False)
user.email = self.cleaned_data["email"]
user.city = self.cleaned_data["city"]
user.state = self.cleaned_data["state"]
if commit:
user.save()
return user
This works fine till the form part. the template shows all these fields but there is a problem here. the fields that i added like city,state,etc are showing on the form but when i query like User.city or anything except the inbuilt, it gives me that User has no attribute city...this means that the fields are not being created in the in-built User model...So how do i do it?
It's a common problem. Ok do you have a complete form to your user, but your model (user) are complete? So wath you need to implement is an extension of your user model (to add your custom fields). How to Extend Django User Model will help you..

Overwrite maxlength/minlength of username by Django User model in the ModelForm

Try to overwrite User models by the following code, but somehow I cannot overwrite the max_length and min_length of username.
More specifically, when I check by python manage.py shell, I do overwrite them. But it seems has no effect on the html which was rendered(username maxlength is still 150).
Don't know which parts get wrong, please help.
from django import forms
from django.contrib.auth.models import User
class RegistrationForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(RegistrationForm, self).__init__(*args, **kwargs)
email = self.fields['email']
username = self.fields['username']
email.required = True
email.label_suffix = ' '
username.label_suffix = ' '
######### this is not work!!!###############
username.min_length = 6
username.max_length = 30
############################################
class Meta:
model = User
fields = ('username', 'email')
labels = {
'username': '帳號',
}
help_texts = {
'username': '',
}
Instead of modifying the form, you should modify/override the model.
I recommend using django-auth-tools for building your own custom user model. It supplies basic models, views and forms which can be easily extended.
If you are trying to override just the model form field, you could do something like this
class RegistrationForm(forms.ModelForm):
username = forms.CharField(required=True, min_length=6, max_length=30)
class Meta:
model = User
fields = ('username', 'email')
or
class RegistrationForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(RegistrationForm, self).__init__(*args, **kwargs)
self.fields['username'] = forms.CharField(required=True, min_length=6, max_length=30)
class Meta:
model = User
fields = ('username', 'email')
But I would recommend creating a Custom User Model inherited from AbstractBaseUser to override the username or email field as documented in https://docs.djangoproject.com/en/1.10/topics/auth/customizing/

Django Forms - Set username field equal to email field

I've got a Django Model Form and I am trying to set the username field equal to the email field.
Here's my form
class UserForm(forms.ModelForm):
email = forms.EmailField()
username = forms.CharField(widget=forms.HiddenInput())
class Meta:
model = User
fields = ('email', 'username')
I am currently using some JavaScript to copy the new email value to the username hidden input before the user submits the form but I would like to do this server side.
I've tried to set it by doing the following UserForm['username'] = email before saving the form and got the following error object does not support item assignment.
Any tips would be appreciated.
I did not test this, I think it would work. If not check out overriding the save method which definitely works. Django Model Field Default Based Off Another Field in Same Model
class UserForm(forms.ModelForm):
email = forms.EmailField()
username = forms.CharField(default=email)
class Meta:
model = User
fields = ('email', 'username')
Maybe it will be useful
from django import forms
from django.contrib.auth.forms import UserCreationForm
from .models import User
more info
enter link description here
class UserForm(UserCreationForm):
class Meta:
model = User
fields = ('email', 'username')
email = forms.EmailInput(attrs={'placeholder': "Email"})
def save(self, commit=True):
user = super(UserForm, self).save(commit=False)
user.username = user.email
if commit:
user.save()
return user

Create new object on ModelForm save, with values from related model

I have a ModelForm to allow the creation of new User objects via a subclass of CreateView, and I also have a UserProfile model with a "client" field, and connected to the User model.
This:
# models.py
class UserProfile(TimeStampedModel):
user = models.OneToOneField(User, unique=True)
client = models.ForeignKey(Client)
# forms.py
class UserForm(ModelForm):
def create_userprofile(self, user, client):
profile = UserProfile()
profile.user = user
profile.client = client
profile.save()
class Meta:
model = User
fields = ('email', 'username', 'password', 'first_name', 'last_name', 'groups')
# views.py
class UserCreate(LoginRequiredMixin, CreateView):
model = User
template_name = 'usermanager/user_form.html'
form_class = UserForm
success_url = reverse_lazy('usermanager:list')
def form_valid(self, form):
### Make sure a newly created user has a UserProfile.
# some pseudo-code thrown in
# First save the user
result = super(UserCreate, self).form_valid(form)
# Now that we have a user, let's create the UserProfile
form.create_userprofile(created_user, current_user.userprofile.client)
# Finally return the result of the parent method.
return result
I want to be able to create a new UserProfile when the form is submitted (and is valid, of course), so I was doing it on the CreateView.form_valid() method, but I need the ID of the just created user, which at that time I don't think I have - do I?
At the same time, I need to assign to the new UserProfile the same client as the current (not the new) user has in his profile.
Any thoughts on how to achieve this?
try checking if
self.object.pk
has what you want after calling
super(UserCreate, self).form_valid(form)
in your form_valid method.

Excluding password when extending UserCreationForm

I want to make a view where the user can change the attributes for his User object: first_name, last_name and email address. Django has a built in UserCreationForm for creating users. I already have an extended version that also includes email, first_name and last_name. Now I am trying to extend that one to not include the password and username.
from django.contrib.auth.forms import UserCreationForm
class ExtendedUserCreationForm(UserCreationForm):
class Meta:
model = User
fields = ('username', 'password1', 'password2',
'email', "first_name", 'last_name' )
class UserEditForm(ExtendedUserCreationForm):
class Meta:
model = User
exclude = ('username', 'password1', 'password2')
The UserEditForm I am looking for will of course not be able to create new instances, but it will only be used to edit existing instances:
form = UserEditForm(instance=request.user)
However, the username field and password fields are still shown. How can I exclude these?
You can just remove the password2 field in the init of the form like so:
class MyUserCreationForm(UserCreationForm):
def __init__(self, *args, **kargs):
super(MyUserCreationForm, self).__init__(*args, **kargs)
del self.fields['password2']
It's not a bad idea to have a look at a class you subclass. password1 and password2 fields are defined in form directly, not in the model. So exclude and fields will have no effect on them. Just make your own ModelForm as #MatthewSchinckel suggests.
think that would give the idea...
class RegistrationForm(UserCreationForm):
email = forms.EmailField(required=True)
#first_name = forms.CharField(required=True)
#last_name = forms.CharField(required=True)
username = forms.CharField (required=True)
class Meta:
model = User
fields = ('first_name','last_name','username','email', 'password1', 'password2')
def __init__ (self, *args, **kwargs):
super(RegistrationForm,self).__init__(*args, **kwargs)
#remove what you like...
self.fields.pop ('first_name')
self.fields.pop ('last_name')
self.fields.pop ('password1')
self.fields.pop ('password2')
Why not just use a ModelForm, and exclude the fields you don't want? That seems like it would be a simpler solution.