Django Form not raising error - django

I'm trying to create a custom send email application using the User-model.
I am able to get the application functioning by sending email but it does not raise an error on the template , If the user doesn't exist.
So I tried to test whether it will raise any error at all if the number of characters is breach and no error raise . Can someone help me
class Thread(models.Model):
subject = models.CharField(max_length=100, blank=True)
user = models.ForeignKey(User)
class Message(models.Model):
user = models.ForeignKey(User, related_name='sender')
recipient = models.ForeignKey(User, related_name='recipient')
created = models.DateTimeField(auto_now_add=True)
body = models.CharField(max_length=1000)
read = models.BooleanField(default=False)
trash = models.BooleanField(default=False)
sentmessage = models.BooleanField(default=False)
thread = models.ForeignKey(Thread)
def __unicode__(self):
return self.body
views.py
#login_required
def Create(request):
form = NewMessageForm()
if request.method =='POST':
form = NewMessageForm(request.POST)
if form.is_valid():
recipient = form.cleaned_data['recipient']
subject = form.cleaned_data['subject']
message = form.cleaned_data['message']
thread = Thread.objects.create(subject=subject,user=request.user)
recipient = User.objects.get(username=recipient)
message = Message.objects.create(user=request.user,recipient=recipient,body=message,thread=thread)
return HttpResponseRedirect(reverse('world:message'))
else:
return HttpResponseRedirect(reverse('world:Create'))
return render(request,'create.html',{'messages':messages,'form':form})
forms
class NewMessageForm(forms.Form):
recipient = forms.CharField(required=True,max_length=1)
subject = forms.CharField(required=True,max_length=1)
message = forms.CharField(widget=forms.Textarea,required=True,max_length=1)
def clean_recipient(self):
recipient = self.cleaned_data['recipient']
try:
recipient = User.objects.get(username=recipient)
except User.DoesNotExist:
raise forms.ValidationError("This username does not exist")
return recipient
template
<form method="POST" >{% csrf_token %}
{{form.recipient}}
{{form.subject}}
{{form.message}}
<input type = "submit" value= "send" class="save" id="send"/>
</form>
{{form.recipient.errors}}
{{form.subject.errors}}
{{form.message.errors}}

Your pattern is slightly wrong:
if request.method =='POST':
form = NewMessageForm(request.POST)
if form.is_valid():
recipient = form.cleaned_data['recipient']
subject = form.cleaned_data['subject']
message = form.cleaned_data['message']
thread = Thread.objects.create(subject=subject,user=request.user)
recipient = User.objects.get(username=recipient)
message = Message.objects.create(user=request.user,recipient=recipient,body=message,thread=thread)
return HttpResponseRedirect(reverse('world:message'))
else:
form = NewMessageForm()
Idea is that you first check if it's a POST, ok - is it working? Great- return correct flow - otherwise fall-through and pass form with errors attached by is_valid() to context. In case it's a new one - create it as a last resort since it doesn't hold any information yet.
Also don't forget form.non_field_errors since it will contain errors that are not specific to any field.

Related

Django queryset how to get last n records?

I have an app where I want to display in template last 5 comments for every message. How can I get last n comments that matches a specific message? I've tried: comments_all = Comments.objects.all().order_by('-id')[:5] but it just returns 5 last comments regardless of message.
models
class Message(models.Model):
host = models.ForeignKey(NewUser, on_delete=models.CASCADE)
body = models.TextField(max_length=1000)
created = models.DateTimeField(auto_now_add=True)
class Comments(models.Model):
message = models.ForeignKey(Message, on_delete=models.CASCADE)
publisher = models.ForeignKey(NewUser, on_delete=models.CASCADE)
body = models.TextField(max_length=300)
created = models.DateTimeField(auto_now_add=True)
views
def home(request):
messages_all = Message.objects.all()
comments_all = Comments.objects.all()
form = AddComments()
if request.method == "POST":
form = AddComments(request.POST)
if form.is_valid():
comment = form.save(commit=False)
messageid = request.POST.get('message_id')
comment.message_id = messageid
comment.publisher = request.user
comment.save()
return redirect('home')
last_five = Message.objects.all().order_by('-id')[:10]
context = {
'messages_all':messages_all,
'comments_all':comments_all,
'form':form,
'last_five':last_five
}
return render(request,'base/home.html', context)
def home(request):
messages_all = Message.objects.all()
...
# construct a dictionary where each message.id is the key
# and the value is the queryset for last 5 comments for the
# matching message
message_last_five = {
msg.id: Comments.objects.filter(message=msg).order_by('-created')[:5]
for msg in messages_all
}
# add message_last_five to your context
context = {
'messages_all': messages_all,
'comments_all': comments_all,
'message_last_five': message_last_five,
'form': form,
'last_five': last_five
}
return render(request,'base/home.html', context)
Although this perhaps can get you what you need - know that this is not an optimal solution because this is quite an expensive query to run.
And you can also achieve relatively the same thing with django templating instead too.

Auto populate hidden form fields in modelform

So my model, form, and view are working mostly. View works and sending the email works. The "message" is saved but I cannot get the message_to and message_from to save. It is supposed to save the usernames. I can get everything to save, but cannot get the message saved to the database WITH the to and from usernames. I am trying to only have 1 field in the message. "Content". The to and from should be hidden and auto-populated. I appreciate any other set of eyes on this. Thank you.
'models.py'
class Message(models.Model):
message_content = models.TextField()
message_to = models.ForeignKey(User, on_delete=models.CASCADE, related_name='message_to')
message_from = models.ForeignKey(User, on_delete=models.CASCADE, related_name='message_from')
date_created = models.DateTimeField(default=timezone.now)
unread = models.BooleanField(default=True)
'forms.py'
class MessageSellerForm(forms.ModelForm):
class Meta:
model = Message
'views.py'
def ad_detail(request, *args, **kwargs):
template_name = 'x_ads/ad_detail.html'
ad = get_object_or_404(Ad, pk=kwargs['pk'])
ad.increment_view_count()
if request.method == 'POST':
message_form = MessageSellerForm(data=request.POST)
message_form.message_from = request.user.username
message_form.message_to = ad.creator.username
if message_form.is_valid():
subject = 'Message about your ad. ' + ad.title
from_email = request.user.email
to_email = ad.creator.email
message = 'You have a message about one of your ads waiting for you!'
send_mail(subject=subject, message=message, from_email=from_email,
recipient_list=[to_email], fail_silently=False)
messages.success(request, your message has been sent.')
message_form.save()
return HttpResponseRedirect(request.path_info)
else:
message_form = MessageSellerForm()
return render(request, template_name, {'ad': ad, 'message_form': message_form})
I think I see what you're trying to do there, but there are other ways that I think will be a bit easier.
https://docs.djangoproject.com/en/3.0/topics/forms/modelforms/#the-save-method
You could instead:
# create the django object in memory, but don't save to the database
message = message_form.save(commit=False)
message.message_from = request.user.username
message.message_to = ad.creator.username
# now save it to the database
message.save()
If you do that you won't need the assignments to the message form further up:
message_form.message_from = request.user.username
message_form.message_to = ad.creator.username
EDIT
You might also need to modify your MessageSellerForm to not include the message_from and message_to fields so that validation will work. That's OK because you know that you'll be assigning the right values to those fields after form validation but before saving to the database.

Message notification in django

i have simple message model in my study project.
class Message(models.Model):
sender = models.ForeignKey(CustomUser, related_name='sender')
reciever = models.ForeignKey(CustomUser, related_name='reciever')
text = models.TextField()
created_at = models.DateTimeField(auto_now_add=True)
How can I send notification to request.user about new message.
i.e. I need send notification to request.user if I have new Message object with request.user in reciever field
UPD my view:
def dialog(request, user_pk):
sent = Message.objects.filter(reciever_id=user_pk, sender_id=request.user.pk)
recieved = Message.objects.filter(reciever_id=request.user.pk, sender_id=user_pk)
mate = CustomUser.objects.get(pk=user_pk)
dialog_list = sorted(chain(sent, recieved), key=lambda a:a.created_at)
if request.POST:
form = MessageForm(request.POST)
if form.is_valid():
f = form.save(commit=False)
f.sender = CustomUser.objects.get(pk=request.user.pk)
f.reciever = CustomUser.objects.get(pk=user_pk)
form.save()
else:
form=MessageForm()
return render(request, 'dialog.html', {'sent':sent,
'recieved':recieved, 'form':form, 'mate':mate, 'dialog_list':dialog_list})
look's like this:
I solved this problem by making some ugly logic
Update my model with boolean field:
class Message(models.Model):
sender = models.ForeignKey(CustomUser, related_name='sender')
reciever = models.ForeignKey(CustomUser, related_name='reciever')
text = models.TextField(verbose_name='')
created_at = models.DateTimeField(auto_now_add=True)
is_readed = models.BooleanField(default=False)
In view I create set for my message and add users here, who have messages with is_readed == False
It become True when we load dialog page with this user
I never working in real project, but I think this solution unacceptable here.
Anyway in my first study project it's worked, maybe it helped some newbies, like meemphasized text
def dialog(request, user_pk):
sent = Message.objects.filter(reciever_id=user_pk, sender_id=request.user.pk)
recieved = Message.objects.filter(reciever_id=request.user.pk, sender_id=user_pk)
not_readed = set()
for message in recieved:
message.is_readed = True
message.save()
for message in Message.objects.filter(reciever_id=request.user.pk):
if message.is_readed ==False:
not_readed.add(CustomUser.objects.get(pk=message.sender_id))
dialog_list = sorted(chain(sent, recieved), key=lambda a:a.created_at)
if request.POST:
form = MessageForm(request.POST)
if form.is_valid():
f = form.save(commit=False)
f.sender = CustomUser.objects.get(pk=request.user.pk)
f.reciever = CustomUser.objects.get(pk=user_pk)
form.save()
else:
form=MessageForm()
return render(request, 'dialog.html', {'sent':sent,
'recieved':recieved, 'form':form, 'mate':mate,
'dialog_list':dialog_list, 'not_readed':not_readed})

Django raising no error issue

I'm building a simple messaging application where users can send message to each other .
I'm working a function which allows to users to send messages they previously saved as draft .
The problem is , It doesn't raise any error when no input is submitted or username doesn't exist . I think something is blocking it which mean , it wouldn't create a message to send to other user
this is my model
class Thread(models.Model):
subject = models.CharField(max_length=100, blank=True)
user = models.ForeignKey(User)
class Message(models.Model):
user = models.ForeignKey(User, related_name='sender')
recipient = models.ForeignKey(User, related_name='recipient')
created = models.DateTimeField(auto_now_add=True)
body = models.CharField(max_length=1000)
read = models.BooleanField(default=False)
trash = models.BooleanField(default=False)
sentmessage = models.BooleanField(default=False)
thread = models.ForeignKey(Thread,blank=True,null=True)
def __unicode__(self):
return self.body
because you require a subject , recipient and a body to send a message and they are each in different models and this is when the user has already created the message and saved it as a draft . I have created 2 forms each with different models and populated each form with their objects.
class DraftForm(forms.ModelForm):
recipient = forms.CharField(required=True,max_length=2)
body = forms.CharField(widget=forms.Textarea,required=True,max_length=1)
hidden_field = forms.CharField(widget=forms.HiddenInput())
class Meta:
model = Message
fields = ('body',)
def clean_recipient(self):
recipient = self.cleaned_data['recipient']
try:
recipient = User.objects.get(username=recipient)
except User.DoesNotExist:
raise forms.ValidationError("This username does not exist")
return recipient
class ThreadForm(forms.ModelForm):
class Meta:
model = Thread
fields = ('subject',)
views
#login_required
def ReadDraft(request,id):
try:
messages = Message.objects.get(pk=id,recipient=request.user,trash=True)
except Message.DoesNotExist:
return HttpResponseRedirect(reverse('world:Display'))
thread = Thread.objects.get(message=messages)
initial = {}
initial.update({'hidden_field': messages.id})
draft = DraftForm(instance=messages,initial=initial)
thread = ThreadForm(instance=thread)
person = Person.objects.get(user=request.user)
if request.method =='POST':
id = request.POST.get('hidden_field', False)
form = ThreadForm(request.POST)
forms = DraftForm(request.POST)
if form.is_valid and forms.is_valid():
m = Message.objects.get(pk=id)
recipient = form.cleaned_data['recipient']
subject = form.cleaned_data['subject']
body = form.cleaned_data['body']
message = Message.objects.create(user=request.user,recipient=recipient,body=message,thread=m.thread)
return render(request,'create.html',{'DraftForm':draft,'ThreadForm':thread,'person':person})
forms
{% if DraftForm and ThreadForm %}
<form method="POST" >{% csrf_token %}
{{DraftForm.recipient}}
{{ThreadForm.subject}}
{{DraftForm.body}}
{{DraftForm.hidden_field}}
<input type = "submit" value= "send" class="save" id="send" />
</form>
{% endif %}
{{ThreadForm.subject.errors}}
{{DraftForm.recipient.errors}}
{{DraftForm.body.errors}}
You do not include your forms in your template context when you doing a POST request and validation fails. So there is no error message to your form.
You should do something like:
if request.method =='POST':
# some more code here ...
thread = ThreadForm(request.POST)
draft = DraftForm(request.POST)
Hope this helps.
EDIT: Also look at Burhans answer, you forgot the braces if forms.is_valid()!
Its not working because here:
if form.is_valid and forms.is_valid()
One is a property and one is a method call, and only one of them is actually doing anything (the other is returning True).
Even if there were any errors, you don't have a condition to check if the forms fail and return the template (there is no else clause for your if check).
Putting all this together, you should have something like this:
if request.method =='POST':
id = request.POST.get('hidden_field') # get will return None as default value
form = ThreadForm(request.POST)
forms = DraftForm(request.POST)
if form.is_valid() and forms.is_valid():
# do stuff
return redirect(reverse('some-url'))
else:
ctx = {'DraftForm': form, 'ThreadForm': forms}
return render(request, 'forms.html', ctx)
else:
return redirect(reverse('some-url'))
In the forms.html, you should have:
{{ DraftForm.errors }}
{{ ThreadForm.errors }}

Simple form not validating

I have found here on stackoverflow a method to extend django's built-in authentication using signals. My base User is defined by 'email' and passwords (so no username there). So I'm trying to modify it to my needs, but I'm geting a validation error for my form. Strange thing is that error is connected to the User.email field and I'm getting 'already in use' even though I'm just registering at the moment. Is it trying to save it 2 times or what ? I've discovered it when I was sending dictionary with data to form's contstructor in shell: form = MyForm(data={}). After this form was still invalid, but changing email to different value finally gave me True.
The user_created function connected to registration signal :
def user_created(sender, user, request, **kwargs):
form = CustomRegistrationForm(request.POST, request.FILES)
if form.is_valid():
data = UserProfile(user=user)
data.is_active = False
data.first_name = form.cleaned_data['first_name']
data.last_name = form.cleaned_data['last_name']
data.street = form.cleaned_data['street']
data.city = form.cleaned_data['city']
data.save()
else:
return render_to_response('user/data_operations/error.html', {'errors': form._errors}, context_instance=RequestContext(request))
user_registered.connect(user_created)
My form :
class CustomRegistrationForm(RegistrationForm):
first_name = forms.CharField(widget=forms.TextInput(attrs=attrs_dict), max_length=50)
last_name = forms.CharField(widget=forms.TextInput(attrs=attrs_dict), max_length=50)
street = forms.CharField(widget=forms.TextInput(attrs=attrs_dict), max_length=50)
city = forms.CharField(widget=forms.TextInput(attrs=attrs_dict), max_length=50)
My model :
class UserProfile(models.Model):
first_name = models.CharField(_("Name"), max_length=50, blank=False,)
last_name = models.CharField(_("Last name"), max_length=50, blank=False,)
street = models.CharField(_("Street"), max_length=50, blank=False,)
city = models.CharField(_("City"), max_length=50, blank=False,)
user = models.ForeignKey(User, unique=True, related_name='profile',)
Registration form :
class RegistrationForm(forms.Form):
email = forms.EmailField(widget=forms.TextInput(attrs=dict(attrs_dict,
maxlength=75)),
label=_("Adres email"))
password1 = forms.CharField(widget=forms.PasswordInput(attrs=attrs_dict, render_value=False),
label=_("Haslo"))
password2 = forms.CharField(widget=forms.PasswordInput(attrs=attrs_dict, render_value=False),
label=_("Haslo powtorzone"))
def clean_email(self):
email = self.cleaned_data.get("email")
if email and User.objects.filter(email=email).count() > 0:
raise forms.ValidationError(
_(u"Already in use."))
return email
your 'user_registered' signal is sent after the User is saved. So it already has an 'email' field defined.
UPDATE
Using restless thinking :
form = CustomRegistrationForm(request.POST, request.FILES, notvalidateemail=True)
and in form :
def __init__(self, *args, **kwargs):
self.notvalidateemail = kwargs.pop('notvalidateemail',False)
super(CustomRegistrationForm, self).__init__(*args, **kwargs)
def clean_email(self):
if self.notvalidateemail:
return
else:
#your cleaning here
return email
Problem:
Your form is first saved by django-registration. Then you save it again in user_created.
Solutions:
Use a different form in user_created. One that won't have already saved fields (these from User model like email). You just want to save additional data in user_created, right?
Add some parameters to the form like:
in user_created:
form = CustomRegistrationForm(dontvalidateemail=True, request.POST, request.FILES)
and in form's init;
self.dontvalidateemail = dontvalidateemail
then just check it in clean_email.