I am having trouble having my form validate in Django and I really dont know what the problem is. It is a messaging application and I am trying to have the user sending the message be automatically populated with the user that is logged in. I have tried a lot of things but none of them have worked. Any help would be appreciated. Here is my models.py: (anything commented out is previous attempts at a solution)
class Message(models.Model):
#readonly_fields = 'sender'
sender = models.ForeignKey(User, related_name="send", default=User)
recipient = models.ForeignKey(User, related_name="receive", default=None)
message_subject = models.CharField("Subject", max_length=25)
message_text = models.TextField("Message")
message_time_created = models.DateTimeField(auto_now_add=True)
urgent = models.CharField(max_length=3, choices=URGENCY)
#field to make message urgent or not
def __str__(self):
return self.message_subject
here is the views.py for creating a new message: (anything commented out is previous attempts at a solution)(also includes#login_required tag that wouldnt show up when pasting)
def newMessage(request, user):
user = get_object_or_404(User, pk=user)
#form = MessageForm(request.POST or None, initial={'sender':request.POST.get('username')})
if request.method == 'POST':
#instance = Message(sender=request.user)
#form = MessageForm(instance=instance)
#form = MessageForm(initial={'sender':request.user})
form = MessageForm(request.POST)
if form.is_valid():
new_instance = form.save()
temp = Event(activity='\n' + new_instance.sender.get_full_name() + " sent a message to " + new_instance.recipient.get_full_name())
temp.save()
try:
target = Patient.objects.get(user=user)
return HttpResponseRedirect('/patients/%s/' % target.id)
except Exception as e:
try:
target = Nurse.objects.get(user=user)
return HttpResponseRedirect('/nurses/%s/' % target.id)
except Exception as e:
try:
target = Doctor.objects.get(user=user)
return HttpResponseRedirect('/doctors/%s/' % target.id)
except Exception as e:
try:
target = HospitalAdmin.objects.get(user=user)
return HttpResponseRedirect('/hospitalAdmins/%s/' % target.id)
except Exception as e:
return render(request, "main_site/newMessagePrompt.html",
{ "form" : form, })
elif request.method == 'GET':
form = MessageForm()
return render(request, "main_site/newMessagePrompt.html",
{ "form" : form, })
Here is the form:(anything commented out is previous attempts at a solution)
class MessageForm(ModelForm):
#readonly_fields = 'sender'
urgent = forms.ChoiceField(choices=URGENCY, required=True)
class Meta:
model = Message
#widgets = {'sender': forms.HiddenInput()}
exclude =['message_time_created']
Currently, with giving the sender a default=User in the model, which does auto-populate with the user who is logged in, and if you try to send the message it will work. However the field can still be edited, which is not intentional. If i try to make the field editable=False in the model.py, and then try to send the message, it will give an error. If i try other ways to hide this, such as HiddenInput in the form, the form will not validate. Similar results have occurred when trying to use an instance variable. I have also tried to make the sender field be a read only field but have not succesffully been able to implement it to see if it would work. I really cannot figure out how to fix this problem. Can anyone help me out with this?
Related
Lets say I have the following form rendered in a template :
# Form
class CandidatTourForm(models.ModelForm):
class Meta:
model = Candidat
fields = [
"email", #charField
"first_name", #charField
"last_name", #charField
"dossier", #foreignKey
]
# parent Model
class Candidat(models.Model):
email = models.EmailField()
first_name = models.CharField(max_length=64, blank=True, null=True)
last_name = models.CharField(max_length=64, blank=True, null=True)
dossier = models.ForeignKey(Dossier, on_delete=models.CASCADE)
It is a classical form, nothing fancy. the dossier field is a hidden input.
def candidat_form_tour(request, dossier_uuid, candidat_id):
dossier = get_object_or_404(Dossier, uuid=dossier_uuid)
candidat = get_object_or_404(Candidat, id=candidat_id, dossier__uuid=dossier_uuid)
if request.method == "GET":
form = CandidatTourForm(instance=candidat,)
elif request.method == "POST":
form = CandidatTourForm(request.POST)
if form.is_valid() and assertDataIsLegit(form._meta.model): # see below
candidat = form.save()
return redirect(
"main:document_tour", dossier_uuid=dossier_uuid, candidat_id=candidat_id
)
return render(
request,
"candidature/candidat_form_tour.html",
{
"form": form,
"candidat": candidat,
"dossier_uuid": dossier_uuid,
},
)
When I test this view with POSTMAN and put a dossier_id that has nothing to do with the one received in the form on the GET request, the candidat is saved to the a wrong dossier.
I though about creating a helper function like so
def assertDataIsLegit(_object, _id, _id2):
_type = type(_object)
instance = _type.objects.get(id=_id)
return instance.id == _id2
and calling it to verify that the dossier id sent back by the post request match the original dossier:
if form.is_valid() and assertDataIsLegit(form._meta.model, dossier.id, form.cleaned_data['dossier']):
#do stuff save and all
But I'm pretty sure a better method exists.
I've looked into the form's clean method, but since I need to verify related data im not sure it is the good way.
How would you prevent this ? I know you are not supposed to trust the data sent and verify it.
Would you verify it in the view function ? In a form method ?
Any lead is welcomed !
I have 2 user types, teacher and student. I have built the view to be able to edit a student profile. But I also needed a different one for teacher. I didn't want 2 views, because that would be pointless. Now, for teacher it works as intended, but for some reason for teacher, the same form as for the student is displayed... a teacher has different attributes so it has a different form I need to show.
class TeacherEditForm(forms.ModelForm):
email = forms.EmailField(required=False)
name = forms.CharField(max_length=30, required=False)
surname = forms.CharField(max_length=50, required=False)
academic_title = forms.CharField(max_length=30, required=False)
bio = forms.Textarea()
website = forms.URLField(required=False)
photo = forms.ImageField(required=False)
phone = forms.CharField(required=False)
class StudentEditForm(forms.ModelForm):
email = forms.EmailField(required=False)
name = forms.CharField(max_length=30)
surname = forms.CharField(max_length=50)
photo = forms.ImageField(required=False)
phone = forms.CharField(max_length=15, required=False)
#login_required
def profile_edit(request):
user = request.user
try:
student = Student.objects.get(user=user)
s = True
except ValueError:
teacher = Teacher.objects.get(user=user)
if not s:
if request.method != 'POST':
form = TeacherEditForm(instance=teacher)
else:
form = TeacherEditForm(request.POST, instance=teacher)
if form.is_valid():
user.email = form.cleaned_data['email']
user.save()
form.save()
return redirect('index')
elif s:
if request.method != 'POST':
form = StudentEditForm(instance=student)
else:
form = StudentEditForm(request.POST, instance=student)
if form.is_valid():
user.email = form.cleaned_data['email']
user.save()
form.save()
return redirect('index')
context = {
"form": form,
}
return render(request, "registration/profile_edit.html", context)
The only reason for your code be always using the StudentEditForm is because the request.user is always associated with a Student instance.
Considering a scenario where the user is a Teacher and has no relationship with the Student model, your code would raise an exception. As abahnihi mentioned, you should catch a ObjectDoesNotExist exception instead of ValueError:
try:
student = Student.objects.get(user=user)
s = True
except Student.DoesNotExist:
teacher = Teacher.objects.get(user=user)
The best way to achieve the desired behaviour is writing unit testing.
In any case, just to make sure that's the problem, do a quick "debug" using print statements on top of your view:
#login_required
def profile_edit(request):
print(request.user.student_id)
print(request.user.teacher_id)
#... rest of your view
If you get two IDs on the console, that means your user is a Teacher and a Student.
If this is not an intended behaviour you have to make sure your application won't let it happen. Otherwise, you will need two separate views (or at least two URLs), one to edit the Student profile and the other to edit the Teacher profile.
In any case, here is how you can improve your view:
from django.db import transaction
#login_required
def profile_edit(request):
user = request.user
if hasattr(user, 'student') and isinstance(user.student, Student):
form_class = StudentEditForm
profile_model = user.student # reverse relationship via OneToOne link
else:
form_class = TeacherEditForm
profile_model = user.teacher
if request.method == 'POST':
form = form_class(request.POST, instance=profile_model)
if form.is_valid():
with transaction.atomic(): # two database operations, wrap in a transaction for consistency
profile_model = form.save()
user.email = profile_model.email
user.save()
return redirect('index')
else:
form = form_class(instance=profile_model)
return render(request, 'registration/profile_edit.html', {'form': form})
The code above is considering the following assumptions:
The Student and Teacher models are as described in your pastebin url;
The user will either be a Student or a Teacher, but never both at the same time.
One of the possible reason is, you need to assign a default False value to variable s at the beginning:
#login_required
def profile_edit(request):
s = False
user = request.user
But in all case, you need to make sure the consistency of the database. I mean you may have a user (request.user) in both student and teacher; be careful.
so i currently have my likes app which deals with friend requests, and it works fine however my notification dont seem to be working. Whenever some likes someone else regardless of weather they are liked by that user or not it only sends the second of the two notify.send.
heres my code:
View.py
def like_user(request, id):
pending_like = get_object_or_404(User, id=id)
user_like, created = UserLike.objects.get_or_create(user=request.user)
user = get_object_or_404(User, username=request.user)
liked_user, like_user_created = UserLike.objects.get_or_create(user=user)
if pending_like in user_like.liked_users.all():
user_like.liked_users.remove(pending_like)
elif request.user in liked_user.liked_users.all():
user_like.liked_users.add(pending_like)
notify.send(request.user,
#action=request.user.profile,
target=request.user.profile,
recipient=pending_like,
verb='sent you a friend request view'),
else:
user_like.liked_users.add(pending_like)
notify.send(request.user,
#action=request.user.profile,
target=request.user.profile,
recipient=pending_like,
verb='accepted your friend request view')
return redirect("profile", username=pending_like.username)
models.py
class UserLikeManager(models.Manager):
def get_all_mutual_likes(self, user, number):
try:
qs = user.liker.liked_users.all().order_by("?")
except:
return []
mutual_users = [][:number]
for other_user in qs:
try:
if other_user.liker.get_mutual_like(user):
mutual_users.append(other_user)
except:
pass
return mutual_users
class UserLike(models.Model):
user = models.OneToOneField(User, related_name='liker')
liked_users = models.ManyToManyField(User, related_name='liked_users', blank=True)
objects = UserLikeManager()
def __unicode__(self):
return self.user.username
def get_mutual_like(self, user_b):
i_like = False
you_like = False
if user_b in self.liked_users.all():
i_like = True
liked_user, created = UserLike.objects.get_or_create(user=user_b)
if self.user in liked_user.liked_users.all():
you_like = True
if you_like and i_like:
return True
else:
return False
as you can see in my views.py i have an if statement with one elif, however it never seems to pick up on that elif and goes direct to the else, so in my notifications i always get the 'accepted your friend request view' message. I cant seem to fix this issue if anyone can see an faults please let me know.
When i use it in my profile app to display a button showing, confirm friend request it seems to work. here is my code for the profile:
view.py
def profile_view(request, username):
user = get_object_or_404(User, username=username)
liked_user, like_user_created = UserLike.objects.get_or_create(user=user)
do_they_like = False
if request.user in liked_user.liked_users.all():
do_they_like = True
context = {
"do_they_like": do_they_like
}
return render(request, "profiles/profile_view.html", context)
Thanks
Problem in your below line
user = get_object_or_404(User, username=request.user)
Update above with below line
user = get_object_or_404(User, pk=request.user.pk)
I am trying to check if user is logged in from my views.py file. As depending if user is logged in it should return me different forms. But request.user.is_authenticated() or request.user.is_authenticated is not working, i always get True value.
My view:
def ContactsView(request):
form_class = ContactForm_logged(request=request)
form_class_nonlogged = ContactForm_nonlogged(request=request)
# new logic!
if request.method == 'POST':
if request.user.is_authenticated():
form = ContactForm_logged(data=request.POST, request = request)
else:
form = ContactForm_nonlogged(data=request.POST)
if form.is_valid():
contact_name = request.POST.get(
'contact_name'
, '')
contact_email = request.POST.get(
'contact_email'
, '')
form_content = request.POST.get('content', '')
subjects = form.cleaned_data['subjects']
subjects = dict(form.fields['subjects'].choices)[subjects]
# Email the profile with the
# contact information
template = get_template('threeD/email/contact_template.txt')
context = Context({
'contact_name': contact_name,
'subjects': subjects,
'contact_email': contact_email,
'form_content': form_content,
})
content = template.render(context)
email = EmailMessage(
"New message from " + contact_name,
content,
"Message - " + subjects + ' ',
['smart.3d.printing.facility#gmail.com'],
headers={'Reply-To': contact_email}
)
email.send()
messages.success(request, "Thank you for your message.")
return redirect('/index/contacts/')
else:
if request.user.is_authenticated():
form = ContactForm_logged(request=request)
else:
form = ContactForm_nonlogged()
if request.user.is_authenticated():
return render(request, 'threeD/contacts.html', {
'form': form_class,
})
else:
return render(request, 'threeD/contacts.html', {
'form': form_class_nonlogged,
})
And two of my forms:
class ContactForm_logged(forms.Form):
contact_name = forms.CharField(required=True)
contact_email = forms.EmailField(required=True)
subjects = forms.ChoiceField(choices=emailsubjects)
content = forms.CharField(
required=True,
widget=forms.Textarea
)
def __init__(self, *args, **kwargs):
self.request = kwargs.pop("request")
super(ContactForm_logged, self).__init__(*args, **kwargs)
self.fields['contact_name'].label = "Your name:"
if (self.request.user.first_name == '' or self.request.user.last_name ==''):
self.fields['contact_name'].initial = 'Type your name here'
self.fields['contact_name'].widget.attrs['readonly'] = False
else:
self.fields['contact_name'].initial = self.request.user.first_name
self.fields['contact_name'].widget.attrs['readonly'] = True
self.fields['contact_email'].label = "Your email:"
if (self.request.user.profile.sdu_email == ''):
if (self.request.user.email == ''):
self.fields['contact_email'].initial = 'Type your email here'
self.fields['contact_email'].widget.attrs['readonly'] = False
else:
self.fields['contact_email'].initial = self.request.user.email
self.fields['contact_email'].widget.attrs['readonly'] = True
else:
self.fields['contact_email'].initial = self.request.user.profile.sdu_email
self.fields['contact_email'].widget.attrs['readonly'] = True
self.fields['content'].label = "What do you want to say?"
self.fields['content'].initial = "Dear, Smart 3D printing facility team, I like this WEB server very much, but ..."
self.fields['subjects'].label = "Please, select the subject of your message"
class ContactForm_nonlogged(forms.Form):
contact_name = forms.CharField(required=True)
contact_email = forms.EmailField(required=True)
subjects = forms.ChoiceField(choices=emailsubjects)
content = forms.CharField(
required=True,
widget=forms.Textarea
)
def __init__(self, *args, **kwargs):
self.request = kwargs.pop("request")
super(ContactForm_nonlogged, self).__init__(*args, **kwargs)
self.fields['contact_name'].label = "Your name:"
self.fields['contact_name'].initial = 'Type your name here'
self.fields['contact_email'].label = "Your email:"
self.fields['contact_email'].initial = 'Type your email here'
self.fields['content'].label = "What do you want to say?"
self.fields['content'].initial = "Dear, Smart 3D printing facility team, I like this WEB server very much, but ..."
self.fields['subjects'].label = "Please, select the subject of your message"
The problem is that, whether i am logged in or am not i always get ContactForm_logged form back. And if i m not logged in than, getting ContactForm_logged form back i get an error, that "'AnonymousUser' object has no attribute 'first_name'".
I read on forums that that could have happened if i call request.user.is_authenticated() wrong, but i have tried both request.user.is_authenticated() and request.user.is_authenticated, both give me the same error :/
Any help would be greatly appreciated!
If you are using Django 1.10+, then you should use the property request.user.is_authenticated.
If you are using Django 1.9 or earlier, then you must use request.user.is_authenticated(). Using request.user.is_authenticated in Django 1.9 or earlier is a mistake which can cause sensitive data to be leaked, because it will always be evaluated as True.
If you are using the correct version and it is returning True, then that suggests you really are logged in.
The problem is in the first line of your view method definition:
def ContactsView(request):
form_class = ContactForm_logged(request=request)
Here you are creating an instance of ContactForm_logged class. This line will be executed every time the view method is called. So an instance of ContactForm_logged class will be created everytime, whether user is logged-in or not. Further, in the __init__ method of ContactForm_logged class you are accessing self.request.user.first_name. So when the ContactForm_logged instance is being initialized for unauthenticated requests it is raising the error: "'AnonymousUser' object has no attribute 'first_name'"
I am trying to achieve a rather simple thing, but got stuck with an error and have no idea where does it come from.
I want to create and save an object in my views. The code is very simple:
models.py:
class Iteration(models.Model):
user = models.ForeignKey(User)
one_two = '1-2 weeks'
two_four = '2-4 weeks'
four_six = '4-6 weeks'
six_eight = '6-8 weeks'
DURATION_CHOICES = (
(one_two, '1-2 weeks'),
(two_four, '2-4 weeks'),
(four_six, '4-6 weeks'),
(six_eight, '6-8 weeks'),
)
duration = models.CharField(max_length=100, choices=DURATION_CHOICES, default=two_four)
project = models.ForeignKey(Project)
def is_upperclass(self):
return self.duration in (self.one_two, self.six_eight)
views.py:
def New_iteration(request, slug):
form = IterationForm()
user = request.user
project = Project.objects.get(user=user, slug=slug)
if request.method == 'POST':
form = IterationForm(request.POST)
errors = form.errors
if form.is_valid:
user = request.user
duration = request.POST['duration']
project = Project.objects.get(user=user, slug=slug)
new_iteration = Iteration(user.id, form.cleaned_data['duration'], project.id)
new_iteration.save()
return HttpResponseRedirect("/dashboard/")
else:
return HttpResponse("not valid")
return render(request, "new_iteration.html", {"form" : form, "project" : project, "user" : user})
I am receiving an error invalid literal for int() with base 10: '2-4 weeks'. I think it comes from
new_iteration = Iteration(user.id, form.cleaned_data['duration'], project.id)
line, but I'm not sure what to do.
You shouldn't create the object as
new_iteration = Iteration(user.id, form.cleaned_data['duration'], project.id)
you need to pass data as keyword parameters as
new_iteration = Iteration(user = user, duration = form.cleaned_data['duration'],
project = project)
However, I believe IterationForm is model form and you want to get project before saving the iteration, more better way is
if form.is_valid(): #note this is function call
user = request.user
project = Project.objects.get(user=user, slug=slug)
new_iteration = form.save(commit=False)
new_iteration.project = project
new_iteration.save()
I have solved the task. I should have added my forms.py for better understanding. I have edited my forms.py file and defined there, that the only "selectable" field should be "duration", and Django should get other stuff (user and project) when initiating the form in views.
The other mistake was that I did not pass data as keyword parameters, thanks Rohan.
So I have added fields = ('duration',) to my ModelForm and re-initiaded the form with keyword arguments now.