Django - cannot assign - must be an instance - django

I tried phrasing this question recently and got totally confused. I've extended the default user model with this in my models.py:
class Biography(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
biography = models.TextField(max_length=500, blank=True,default='Details')
I've included this in the forms.py:
class EditProfileForm(forms.Form):
first_name = forms.CharField(label='First Name')
last_name = forms.CharField(label='Last Name')
biography = forms.CharField(label='Biography', widget=Textarea(attrs={'rows': 5}))
I have a view to edit the profile and want to add "biography" to it, but have absolutely no clue where to start. Here's the view:
def edit_profile(request):
user = request.user
products = Product.objects.filter(user=user)
form = EditProfileForm(request.POST or None, initial={'first_name':user.first_name, 'last_name':user.last_name})
if request.method == 'POST':
if form.is_valid():
user.first_name = request.POST['first_name']
user.last_name = request.POST['last_name']
user.save()
return render(request, 'profile.html', {'user':user, 'products':products})
context = {"form": form}
return render(request, "edit_profile.html", context)
I tried to replicate what is already there with this:
def edit_profile(request):
user = request.user
products = Product.objects.filter(user=user)
biography = Biography(user=user)
form = EditProfileForm(request.POST or None, initial={'first_name':user.first_name, 'last_name':user.last_name, 'biography':user.biography})
if request.method == 'POST':
if form.is_valid():
user.first_name = request.POST['first_name']
user.last_name = request.POST['last_name']
user.biography = request.POST['biography']
user.save()
return render(request, 'profile.html', {'user':user, 'products':products})
context = {"form": form}
return render(request, "edit_profile.html", context)
I definitely missed the point somehow. The last time I asked this question I was somewhat chastised for not knowing how to solve it. In all honesty I'm really new to Django and am amazed I got this far, but I'm stuck. I 'think' I need to create an instance but am not sure how.

What you should do differently:
Create Biography instance if it does not exist, or get from db.
Instantiate separate forms for different request methods
Use cleaned_data as input validation is one of the main purposes of forms
Always redirect after POST
Save User and Biography instances separately
And you don't need related products in form view. If only you are not going to somehow update them here.
For example:
def edit_profile(request):
user = request.user
biography, created = Biography.objects.get_or_create(user=user)
form = EditProfileForm(initial={
'first_name': user.first_name,
'last_name': user.last_name,
'biography': biography.biography
})
if request.method == 'POST':
form = EditProfileForm(data=request.POST)
if form.is_valid():
user.first_name = form.cleaned_data['first_name'] # use cleaned_data
user.last_name = form.cleaned_data['last_name']
biography.biography = form.cleaned_data['biography']
biography.save() # save Biography object
user.save() # save User object
return redirect(biography) # always redirect after successful POST. In this case Biography must have get_absolute_url() method
context = {'form': form}
return render(request, 'edit_profile.html', context)
Read more in documentation.

user.biography is an instance of Biography model, so what you should do here is get that instance and edit its attributes, like this:
bio = user.biography
bio.biography = request.POST['biography']
bio.save()

Related

ChoiceFields to give two different forms or different fields in html template and django

I am trying to make a registration form for a dentist and a student, where I have a choice field for a dentist and a student. What I want to happen is, when dentist is picked, I should be able to see the specialties field in the html as well as Django to pick that form, and for student, to pick student_email and institution. I am confused to how to write its code in the template and I looked almost everywhere and couldn't find anything that could help with what I want. I also included an image with what the registration image looks like. I know I can use select and option in html template but still a bit confused about them as well. If you could show me a better way to apply my idea, please let me know.
form.py
from django import forms
from django_countries.fields import CountryField
class RegistrationForm(forms.Form):
Specialties = [
('pediatric','Pediatric'),
('oral surgeon','Oral Surgeon'),
('periodontist','Periodontist (Restorative; Esthetic)'),
('orthodontist','Orthodonsit'),
('endodontist','Endodontist'),
('prosthodontist','Prosthodontist'),
('oral pathologist','Oral Pathologist'),
('oral radiologist','Oral Radiologist'),
('public health dentist','Public Health Dentist'),
('research and academics','Research and Academics'),
]
username = forms.CharField(widget=forms.TextInput(attrs={'class':'form-control'}), required=True, unique=True)
email = forms.EmailField(widget=forms.EmailInput(attrs={'class':'form-control'}), required=True, unique=True)
student_email = forms.EmailField(widget=forms.EmailInput(attrs={'class':'form-control'}), required=True, unique=True)
password = forms.CharField(widget=forms.PasswordInput(attrs={'class':'form-control'}), required=True)
password_repeat = forms.CharField(widget=forms.PasswordInput(attrs={'class':'form-control'}), required=True)
first_name = forms.CharField(widget=forms.TextInput(attrs={'class':'form-control'}), required=True)
last_name = forms.CharField(widget=forms.TextInput(attrs={'class':'form-control'}), required=True)
date_of_birth = forms.DateField(label = "Date of Birth", widget=forms.SelectDateWidget([x for x in range(1920,2021)]), required=True)
country = CountryField().formfield(required=True)
gender = forms.ChoiceField(widget=forms.RadioSelect, choices=[('male','Male'),('female','Female')], required=True)
specialty = forms.CharField(widget=forms.Select(choices= Specialties), required=True)
institution = forms.CharField(widget=forms.TextInput(attrs={'class':'form-control'}), required=True)
dentist_or_student = forms.ChoiceField(widget=forms.RadioSelect, choices=[('dentist','Dentsit'),('student','Student')], required=True)
def clean_student_email(self):
data = self.cleaned_data['student_email']
if "#edu" not in data: #Check if the student's email is educational or not
raise forms.ValidationError("Email must be #edu")
return data
views.py
def user_register(request):
template_name = 'accounts/signup.html'
if request.method == 'POST':
form = RegistrationForm(request.POST)
# Check for validity
if form.is_valid():
if form.cleaned_data['dentist_or_student'] == 'dentist':
if User.objects.filter(username=form.cleaned_data['username']).exists():
return render(request, template_name, {
'form': form,
'error_message': 'Username already exists'
})
elif User.objects.filter(email=form.cleaned_data['email']).exists():
return render(request, template_name, {
'form': form,
'error_message': 'Email already exists'
})
elif form.cleaned_data['password'] != form.cleaned_data['password_repeat']:
return render(request,template_name, {
'form': form,
'error_message': 'Passwords do not match'
})
else:
# Create the user
user = User.objects.create_user(
form.cleaned_data['username'],
form.cleaned_data['email'],
form.cleaned_data['password']
)
user.first_name = form.cleaned_data['first_name']
user.last_name = form.cleaned_data['first_name']
user.dentist_or_student = form.cleaned_data['dentist']
user.date_of_birth = form.cleaned_data['date_of_birth']
user.country = form.cleaned_data['country']
user.gender = form.cleaned_data['gender']
user.save()
# Login the user
login(request, user)
# redirect to Homepage
return HttpResponseRedirect('home')
elif form.cleaned_data['dentist_or_student'] == 'student':
if User.objects.filter(username=form.cleaned_data['username']).exists():
return render(request, template_name, {
'form': form,
'error_message': 'Username already exists'
})
elif User.objects.filter(email=form.cleaned_data['student_email']).exists():
return render(request, template_name, {
'form': form,
'error_message': 'Email already exists'
})
elif form.cleaned_data['password'] != form.cleaned_data['password_repeat']:
return render(request,template_name, {
'form': form,
'error_message': 'Passwords do not match'
})
else:
# Create the user
user = User.objects.create_user(
form.cleaned_data['username'],
form.cleaned_data['student_email'],
form.cleaned_data['password']
)
user.first_name = form.cleaned_data['first_name']
user.last_name = form.cleaned_data['first_name']
user.dentist_or_student = form.cleaned_data['student']
user.date_of_birth = form.cleaned_data['date_of_birth']
user.country = form.cleaned_data['country']
user.gender = form.cleaned_data['gender']
user.save()
# Login the user
login(request, user)
# redirect to Homepage
return HttpResponseRedirect('home')
else:
messages.error(request, 'Please pick if either you are a Dentist or a Student before continuing the form')
return redirect('register')
# No post data available, just show the webpage
else:
form = RegistrationForm()
return render(request, template_name, {'form': form})
enter image description here
You are using a CharField (see docs) for a field that has options. Try using a ChoiceField (see docs) instead.
Something like:
specialty = forms.ChoiceField(choices= Specialties, required=True)
You should add both into your html, and make them hidden.
Then add an alert into your Select, and OnChange you can make hidden=false of the selected form.
YOURSELECT.addEventListener("change", document.getElementById(YOURSELECT.value).removeAttribute("hidden"););

Form after registration is not submitting Django

To complete registration, I want users to complete secondary form. However secondary form is not submitting. I think user is not getting authenticated in the registration and then the secondary form is not submitting. The login() seems to not work.
# the form in this view that's not submitting
def agreements(request):
if request.method == "POST":
form = AgreementsForm(request.POST)
if form.is_valid():
user = request.user
agree = form.save(commit=False)
agree.save()
else:
raise ValidationError("Form is not valid. Try Again.")
else:
form = AgreementsForm()
return render(request, 'agree.html', {'form': form})
Here is the forms.py for the agreements:
class AgreementsForm(forms.ModelForm):
non_ent=forms.BooleanField(label='kdmkl kdldsk')
agreement1=forms.BooleanField(label='dmklsd. lkdfmld')
class Meta:
model = Agreements
fields = ('non_ent', 'agreement1')
def save(self, commit=True):
agree = super(AgreementsForm, self).save(commit=False)
agree.non_ent = self.cleaned_data['non_ent']
agree.agreement1 = self.cleaned_data['agreement1']
if commit:
agree.save()
return agree
Here is the initial registration view:
# register view which submits, but I think it's not authenticating the user
def registration(request):
if request.method == "POST":
form = CustomUserCreationForm(request.POST)
if form.is_valid():
user = form.save(commit=False)
user.is_active = True
user.save()
login(request, user, backend='django.contrib.auth.backends.ModelBackend')
return redirect('agreements_page')
else:
raise ValidationError("Form is not valid. Try Again.")
else:
form = CustomUserCreationForm()
return render(request, 'register.html', {'form': form})
Agreements Model:
class Agreements(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE,blank=True, null=True)
non_ent = models.BooleanField(default=False, blank=True, null=True)
agreement1 = models.BooleanField(default=False, blank=True, null=True)
date = models.DateTimeField(default=datetime.now, blank=True, null=True)
def __str__(self):
return f'{self.user} ({self.date})'
You need to authenticate user first to login :
def registration(request):
if request.method == "POST":
form = CustomUserCreationForm(request.POST)
if form.is_valid():
user = form.save(commit=False)
user.is_active = True
user.save()
# authenticate user first
user = authenticate(request,username=form.cleaned_data['username'],password=form.cleaned_data['password'])
if user:
login(request, user)
return redirect('agreements_page')
Then in your secondary form you can save request.user like this .
I hope you have a OneToOne relation with user in your Agreement model.
form = AgreementsForm(request.POST)
if form.is_valid():
agree = form.save(commit=False)
agree.user = request.user
agree.save()

Django - Cannot assign "'Biography test'": "User.biography" must be a "Biography" instance

I have extended the user model with an extra field - biography. It appears in the admin panel as a new section. Here's a picture:
Here's the new model:
class Biography(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
biography = models.TextField(max_length=500, blank=True)
Here's the profile view:
def profile(request, username):
user = get_object_or_404(User, username=username)
products = Product.objects.filter(user=user)
if not request.user == user:
return render(request, 'no.html')
else:
return render(request, 'profile.html', {'user':user,'products': products})
I'm using a form to edit the profile - here's the view:
def edit_profile(request):
user = request.user
products = Product.objects.filter(user=user)
form = EditProfileForm(request.POST or None, initial={'first_name':user.first_name, 'last_name':user.last_name, 'biography':user.biography})
if request.method == 'POST':
if form.is_valid():
user.first_name = request.POST['first_name']
user.last_name = request.POST['last_name']
user.biography = request.POST['biography']
user.save()
return render(request, 'profile.html', {'user':user, 'products':products})
context = {"form": form}
return render(request, "edit_profile.html", context)
...and here's the form:
class EditProfileForm(forms.Form):
first_name = forms.CharField(label='First Name')
last_name = forms.CharField(label='Last Name')
biography = forms.CharField(label='Biography', widget=Textarea(attrs={'rows': 5}))
Here's a screenshot of the error message:
I'm mixing something up but I can't figure out what. Doesn't help that I'm new to this ...still trying!
As the error message says:
"User.Biography" must be a "Biography" instance.
In your edit_profile definition, you have the following assignment:
user.biography = request.POST['biography']
request.POST['biography'] is not a valid instance of Biography. So, you have to create a valid Biography instance, according to your Biography model, with the request.POST['biography'].
After that, you can assign your valid instance to user.biography.
I hope it had been useful for you.

Queryset filtering on Foreignkey in Modelform

I have a strange issue when saving a model form. I have a form, which consists of two model forms and I am trying to save them at the same time. For clarity, below is my code
Views.py
def create_user(request):
if request.method == 'POST':
user_form = UserForm(request.POST)
my_user_form = MyUsersForm(request.POST)
if user_form.is_valid() and my_user_form.is_valid():
us = user_form.save()
my_us = my_user_form.save(commit=False)
my_us.user = us
my_us.save()
return HttpResponse('You have successfully created a user')
else:
return HttpResponse(' My_user_form is not validated')
else:
user_form = UserForm()
my_user_form = MyUsersForm(user=request.user)
return render(request, 'create_user.html', {'user_form': user_form, 'my_user_form': my_user_form})
my_user_form is not validated when I override the init method of MyUsersForm to filter the queryset of the foreign key(created_by) but when I don"t filter the queryset, my_user_form is validated and the form is saved.
What I don't understand is when I don"t filter the query set how come my_user_form is validated?
The data which is sent via the request.post to my_user_form is somehow lost (when I filter the queryset). any clue in the right direction is highly appreciated. Thank you for your valuable inputs.
Forms.py
class MyUsersForm(ModelForm):
class Meta:
model = MyUsers
fields = ['created_by', ]
def __init__(self, user=None, **kwargs):
super(MyUsersForm, self).__init__(**kwargs)
if user is not None:
self.fields['created_by'].queryset = User.objects.filter(username=user)
Models.py
class MyUsers(models.Model):
user = models.OneToOneField(User, blank=True, null=True)
created_by = models.ForeignKey(User, related_name="created_by", blank=True, null=True)
def create_user(request):
if request.method == 'POST':
user_form = UserForm(request.POST)
my_user_form = MyUsersForm(request.POST)
if user_form.is_valid() and my_user_form.is_valid() and User.objects.filter(username=request.POST.get('user')).exists():
us = user_form.save()
my_us = my_user_form.save(commit=False)
my_us.user = us
my_us.save()
return HttpResponse('You have successfully created a user')
else:
return HttpResponse(' My_user_form is not validated')
else:
user_form = UserForm()
my_user_form = MyUsersForm(user=request.user)
return render(request, 'create_user.html', {'user_form': user_form, 'my_user_form': my_user_form})
why my_user_form is not validated and saved. instead of modifying the queryset in the init i did it in the view itself using the statement
my_user_form.fields['created_by'] = forms.ModelChoiceField(User.objects.filter(username=request.user))
and this solves my problem. but i still don't know why it didn't work in the init method of the MyUsersForm?.

Cannot create profile automatically with django-registration

I'm new to Django and try to create a userprofile along with any user that registers through django-registration module. Here is my UserProfile module:
class UserProfile(models.Model):
username = models.OneToOneField(User)
bio = models.CharField(max_length=500)
location = models.CharField(max_length=20)
User.profile = property(lambda u:UserProfile.objects.get_or_create(username=u)[0])
def __unicode__(self):
return self.user.username
I have also this in settings.py
AUTH_PROFILE_MODULE = 'userprofile.UserProfile'
When users are created they are refered to this view to fill their profile form:
def add_profile(request):
if request.method == 'POST':
form = UserProfileForm(request.POST,request.FILES)
if form.is_valid():
f = form.save(commit = False)
f.username_id= request.user.id
form.save()
return render_to_response('userprofile/ok.html',
context_instance=RequestContext(request))
else:
print form.errors
else:
form = UserProfileForm()
return render_to_response('userprofile/add_profile.html', {'form': form},
context_instance=RequestContext(request))
but when they submit the form, they get this error:
TypeError at /profile/add/sara/
add_profile() got an unexpected keyword argument 'username'
I know that there are many similar questions on SO and I tried many of the suggestions but none work for me. So appreciate your hints