Gmail sending with attachment in Django - html

I am trying to send Gmail with attachments also in Django.
here my views.py:
def index(request):
if request.method != 'POST':
form = EmailForm()
context = {'email_form': form}
return render(request, 'app/index.html', context)
form = EmailForm(request.POST, request.FILES)
if form.is_valid():
subject = form.cleaned_data['subject']
message = form.cleaned_data['message']
email = form.cleaned_data['email']
attach = request.FILES['attach']
try:
mail = EmailMessage(subject, message, settings.EMAIL_HOST_USER, [email])
mail.attach_file(attach.name, attach.read(), attach.content_type)
mail.send()
context = {'message': 'email sended'}
print("email sended")
return render(request, 'app/email_template.html', context)
except:
context = {'message': 'Either the attachment is too big or corrupt'}
print("Either the attachment is too big or corrupt")
return render(request, 'app/index.html', context)
context = {'message': 'Unable to send email. Please try again later'}
return render(request, 'app/index.html', context)
forms.py:
class EmailForm(forms.Form):
email = forms.EmailField()
subject = forms.CharField(max_length=100)
attach = forms.Field(widget = forms.FileInput)
message = forms.CharField(widget = forms.Textarea)
template:
<div class="container-fluid">
<div class="col-xl-4">
<h1>Welcome in django's world</h1>
{{message}}
<form method="POST" action ="." enctype="multipart/form-data">
{% csrf_token %}
<br></br>
{{email_form.as_p}}
<label> </label><label> </label><label> </label>
<input type ="submit" name = "send" value = "Send"/>
</form>
</div>
</div>
Now each and every time I tried to sent mail, it shows Either the attachment is too big or corrupt, which means it goes by the except: block, instead of the try block.I just print the exception msg, it says
__init__() takes from 1 to 2 positional arguments but 5 were given
Now how can I solve this problem such that Gmail will be sent successfully?

Related

Contact form not saving in the Database , is it the views or the HTML?

I have an issue in Django where the contact form after being filled out doesn't save in the database unless I just filled out the message text area :
My views :
def CV_page(request):
if request.method == 'POST':
form = ContactForm(request.POST)
if form.is_valid():
# Save form data to Django models
contact = Contact(name= form.cleaned_data['name'],
email= form.cleaned_data['email'],
phone= form.cleaned_data['phone'],
message= form.cleaned_data['message'])
contact.save()
# Send email to admin
send_mail('CV-Website-contact',
form.cleaned_data['message'],
form.cleaned_data['email'],
['xxxx#xxxx.com', form.cleaned_data['email']])
return HttpResponse('success') #HttpResponseRedirect('success/') # create a success page
else:
form = ContactForm()
else:
form = ContactForm()
context = {'Abouts': latest_description, 'form': form, 'categories': categories, 'portfolios': portfolios, 'pdf': final_pdf_link}
return render(request, 'cv_page.html', context)
models.py
class Contact(models.Model):
id = models.UUIDField(default=uuid.uuid4, unique=True, primary_key=True, editable=False)
name = models.CharField(max_length=100, null=True, blank=True)
email = models.EmailField(max_length=254, null=False, blank=True)
phone = PhoneNumberField(null=True, blank=True, unique=False)
message = models.CharField(max_length=2500, null=True, blank=True)
created_on = models.DateTimeField(auto_now_add=datetime.datetime.now)
updated_on = models.DateTimeField(auto_now=datetime.datetime.now)
forms.py
class ContactForm(forms.ModelForm):
class Meta:
model = Contact
fields = ['name', 'email', 'phone', 'message']
my HTML:
<!-- Contact Section-->
<section class="page-section" id="contact">
<div class="container">
<!-- Contact Section Heading-->
<h2 class="page-section-heading text-center text-uppercase text-secondary mb-0">Contact Me</h2>
<!-- Icon Divider-->
<!-- Contact Section Form-->
<div class="row justify-content-center">
<div class="col-lg-8 col-xl-7">
<form method="post">
{% csrf_token %}
{{form.as_p}}
<div class="form-actions">
<button type="submit">Send</button>
</div>
</form>
</div>
</div>
</div>
</section>
I don't understand why it would only save if there is only the message text area filled out ?
Thank you very much
As you've defined the model already in the forms.py you can simply save the form by:
form = ContactForm(request.POST)
if form.is_valid():
# Save form data to Django models
form.save()
# Send email to admin
# ... your additional code here
also add action="" in your HTML-form tag:
<form method="post" action="">
The action argument defines the url for the post request. An empty string will load the same page again. You can also define a custom url.
Everything else looks fine.
Edit 1: (comment 1)
The issue with the reload is because your form.is_valid() fails and then your page reloads:
if form.is_valid():
# ... your code
else:
form = ContactForm()
Remove the last two lines of the code above. If your form fails youre page gets reloaded like before but you'll see the errors in your form.
Here's the working snippet:
if request.method == 'POST':
form = ContactForm(request.POST)
if form.is_valid():
# Save form data to Django models
form.save()
# Send email to admin
send_mail('CV-Website-contact',
form.cleaned_data['message'],
form.cleaned_data['email'],
['xxxx#xxxx.com', form.cleaned_data['email']])
return HttpResponse('success')
else:
form = ContactForm()

405 Error: The method is not allowed for the requested URL

I've been trying to code the reset password for my website. However, I keep on running into the 405 error. Google says it's probable that it is a client side error, but I think my html is fine. I'm not sure what else can be causing the error.
I've tried testing the url without the content, but that has not been working either.
routes.py
def send_reset_email(user):
token = user.get_reset_token()
msg = Message('Password Reset Request', sender='noreply#clubsapp.com', recipients=[user.email])
msg.body = f'''To reset your password, visit the following link:
{url_for('reset_token', token=token, _external=True)}
If you did not make this request then simply ignore this email and no change will be made.
'''
mail.send(msg)
#users.route('/reset_request', methods=['GET, POST'])
def reset_request():
if current_user.is_authenticated:
return redirect(url_for('main.home'))
form = RequestResetForm()
if form.validate_on_submit():
user = User.query.filter_by(email=form.email.data).first()
send_reset_email(user)
flash('An email has been sent with instructions to reset your password', 'info')
return redirect(url_for('users.login'))
return render_template('reset_request.html', title='Reset Password', form=form)
#users.route('/reset_password/<token>', methods=['GET, POST'])
def reset_token(token):
if current_user.is_authenticated:
return redirect(url_for('main.home'))
user = User.verify_reset_token(token)
if user is None:
flash('That is an invalid or expired token', 'warning')
return redirect(url_for('reset_request'))
form = ResetPasswordForm()
if form.validate_on_submit():
hashed_password = bcrypt.generate_password_hash(form.password.data).decode('utf-8')
user.password = hashed_password
db.session.commit()
flash(f'Your password has been updated, you are now able to log in!', 'success')
return redirect(url_for('users.login'))
return render_template('reset_token.html', title='Reset Password', form=form)
forms.py
class RequestResetForm(FlaskForm):
email = StringField('Email',
validators=[DataRequired(), Email()],
render_kw={"placeholder":"Enter Email"})
submit = SubmitField('Request Password Reset')
def validate_email(self, email):
user = User.query.filter_by(email=email.data).first()
if user is None:
raise ValidationError('There is no account with that email. You must register first.')
class ResetPasswordForm(FlaskForm):
password = PasswordField('Password',
validators=[DataRequired(), EqualTo('confirm_password', message='Passwords Must Match')],
render_kw={"placeholder":"Create Password"})
confirm_password = PasswordField('Confirm Password',
validators=[DataRequired(), EqualTo('password', message='Passwords Must Match')],
render_kw={"placeholder":"Confirm Password"})
submit = SubmitField('Reset Password')
models.py
class User(db.Model, UserMixin):
id = db.Column(db.Integer, primary_key=True)
firstname = db.Column(db.String(15), nullable=False)
lastname = db.Column(db.String(15), nullable=False)
email = db.Column(db.String(60), unique=True, nullable=False)
password = db.Column(db.String(60), nullable=False)
role = db.Column(db.Integer(), nullable=False, default=ROLES['student'])
clubs = db.relationship('Club', secondary=user_club_assoc_table)
def get_reset_token(self, expires_sec=1800):
s = Serializer(app.config['SECRET_KEY'], expires_sec)
return s.dumps({'user_id': self.id})
#staticmethod
def verify_reset_token(token):
s = Serializer(app.config['SECRET_KEY'])
try:
user_id = s.loads(token)['user_id']
except:
return None
return User.query.get(user_id)
def __repr__(self):
return f'{self.firstname} {self.lastname}'
login.html
{% extends "layout.html" %}
{% block content %}
<h1>Login Page</h1>
<div class="content-section">
<form method="POST" action="">
{{ form.hidden_tag() }}
<fieldset class="form-group">
<legend class="border-bottom mb-4">Login</legend>
...(irrelevant content)
<div class="border-top pt-3">
<small class="text-muted">
<a class="ml-2" href="{{ url_for('users.register') }}">Don't Have An Account?</a>
</small>
<small class="text-muted ml-2">
<a class="ml-2" href="{{ url_for('users.reset_request') }}">Forgot Password?</a>
</small>
</div>
{% endblock content %}
It is a simple error. It is not methods=['GET, POST'].
Change to
methods=['GET', 'POST']
Docs
HTTP Methods
Web applications use different HTTP methods when accessing URLs. You
should familiarize yourself with the HTTP methods as you work with
Flask. By default, a route only answers to GET requests. You can use
the methods argument of the route() decorator to handle different HTTP
methods.
from flask import request
#app.route('/login', methods=['GET', 'POST'])
def login():
if request.method == 'POST':
return do_the_login()
else:
return show_the_login_form()

full_clean() missing 1 required positional argument: 'self'

I'm currently using django version 2.2.4 and trying to create an edit button that will update my models. When trying to save the updated value an TypeError occured which it stated that "full_clean() missing 1 required positional argument: 'self'". I can't seem to detect any error from my codes. Thanks in advance for helping me.
my views.py file
def lab_edit(request, pk, template_name='webapp/lab_edit.html'):
lab= get_object_or_404(Labs, pk=pk)
form = LabForm(request.POST or None, instance=Labs)
if request.method == "POST":
if form.is_valid():
form.save()
return redirect('lab')
return render(request, template_name, {'form':form})
my lab_edit.html file
<div class='container'>
<h2>EDIT LAB</h2>
<form method="post">{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Submit" />
</form>
</div>
my LabForm
class LabForm(forms.ModelForm):
class Meta:
model = Labs
fields = ('labcode', 'name','administrator')
Your LabForm gets as instance= the model class, not a model object. You should fix that by passing lab instead:
def lab_edit(request, pk, template_name='webapp/lab_edit.html'):
lab = get_object_or_404(Labs, pk=pk)
if request.method == 'POST':
form = LabForm(request.POST, instance=lab)
if form.is_valid():
form.save()
return redirect('lab')
else:
form = LabForm(instance=lab)
return render(request, template_name, {'form':form})
By passing a reference to the class , you have basically called full_clean on the class, hence the error.
Note that you should not use request.POST or None since an empty POST request can still be a valid POST request.

Is it possible to loop a custom model form in django?

Is it possible to create multiple objects for a model in django by looping the same form in a for loop. I am using a custom model form.
My template is:
{% for query in queryset %}
<form method="POST" action="{% url 'academics' %}" style=" padding: 5%">
{% csrf_token %}
<input type="text" name="Student" class="form-control" id="id_Student"
value="{{query}}">
<input type="text" name="Subject" class="form-control" required id="id_Subject">
<input type="checkbox" name="Presence" id="id_Presence">
<button type="Submit" id="submit">Submit</button>
{% endfor %}
<button type="Submit" id="submit">Submit</button>
</form>
My models.py is:
class Attendance(models.Model):
Student = models.CharField(max_length=100, blank=False)
Hour = models.CharField(max_length=1, blank=False)
Subject = models.CharField(max_length=8, blank=False)
Date = models.DateTimeField(default=timezone.now)
Presence = models.BooleanField(default=False, blank=False)
def __str__(self):
return f'{self.Student}'
My views.py is:
def academics(request):
if request.user.is_staff:
form = forms.AttendanceForm()
context = {
'form': form,
'queryset': User.objects.filter(profile__Year='SY',profile__Department='CSE')
}
if request.method == "POST" :
form = forms.AttendanceForm(request.POST)
if form.is_valid():
student = request.POST.get('Student')
hour = request.POST.get('Hour')
subject = request.POST.get('Subject')
boolean = request.POST.get('Presence')
def bool(boolean):
if boolean == 'on':
return 'True'
else:
return 'False'
form = Attendance(Student=student,Hour=hour,Subject=subject,Presence=bool(boolean))
form.save()
return render(request, 'console/academics.html',context)
Currently i can create multiple objects, but with the same values of the last form. ie, the object is created with the values of last form. Here i have looped the form so that n number of forms will be generated for n queries with the name filled automatically in the first field. I know explaining this is little complex. Anyone can help?
I'm not entirely clear what you mean by "looping a form", but if you want the user to be able to enter a list of arbitrary length of similar sets of data, then what you want is a Formset or a ModelFormset. When it comes back, you validate all the data that he has submitted, and if it's all good then you iterate through it, usually creating or modifying multiple objects.
Due to reputation I'm unable to comment but I believe this is how you achieve your desired result. by using WHILE LOOP.
I myself have not much knowledge of python & Django but I guess this is the logic. Please correct me if I am wrong instead of down voting.
var = 0
n = 5
if request.method == "POST":
form = forms.AttendanceForm(request.POST)
if form.is_valid():
while var < n:
student = request.POST.get('Student')
hour = request.POST.get('Hour')
subject = request.POST.get('Subject')
boolean = request.POST.get('Presence')
def bool(boolean):
if boolean == 'on':
return 'True'
else:
return 'False'
form = Attendance(Student=student, Hour=hour,Subject=subject,Presence=bool(boolean))
form.save()
var += 1
return render(request, 'console/academics.html', context)

Django - How to make the topics, that you create public? Learning Log Project

The Problem
I'm trying to make a project, where you can make topics, that can be private or public to unauthenticated users. In every topic, you can then make several entries, applying to that topic. Now I'm trying to make a checkbox in my new_topic.html, where if you check it, it evaluates to True, if not, to False. But I'm having trouble with making the checkbox evaluate to True, when I check it. And for some reason, there is two checkbuttons in the page that applies to new_topic.html; one with a label, and one with just a box.
What I've tried
I've tried recreating the db.sqlite3 database. But that didn't work.
I've tried using request.POST.get('public', False) in my new_topic() function, when saving the new_topic variable.ยจ
The Code
My learning_logs/models.py looks like this:
from django.db import models
from django.contrib.auth.models import User
class Topic(models.Model):
"""A topic the user is learning about."""
text = models.CharField(max_length=200)
date_added = models.DateTimeField(auto_now_add=True)
public = models.BooleanField(default=False)
owner = models.ForeignKey(User, on_delete=models.CASCADE)
def __str__(self):
"""Return a string representation of the model."""
return self.text
class Entry(models.Model):
"""Something specific learned about a topic."""
topic = models.ForeignKey(Topic, on_delete=models.CASCADE)
text = models.TextField()
date_added = models.DateTimeField(auto_now_add=True)
class Meta:
verbose_name_plural = 'entries'
def __str__(self):
"""Return a string representation of the model."""
# Add an ellipsis ONLY if the entry,
# is more than 50 characters long.
if self.text > self.text[:50]:
return self.text[:50] + "..."
elif self.text <= self.text[:50]:
return self.text[:50]
My learning_logs\views.py looks like this:
from django.shortcuts import render, get_object_or_404
from django.http import HttpResponseRedirect, Http404
from django.urls import reverse
from django.contrib.auth.decorators import login_required
from .models import Topic, Entry
from .forms import TopicForm, EntryForm
def index(request):
"""The Home Page for Learning Log."""
return render(request, 'learning_logs/index.html')
def check_topic_owner(request, topic):
"""Checks if the topic requested, is requested by the owner.
Else return Http404.
"""
if topic.owner != request.user:
raise Http404
#login_required
def topics(request):
"""Show all topics."""
topics = Topic.objects.filter(owner=request.user).order_by('date_added')
context = {'topics': topics}
return render(request, 'learning_logs/topics.html', context)
#login_required
def topic(request, topic_id):
"""Show a single topic and all its entries."""
topic = get_object_or_404(Topic, id=topic_id)
# Make sure the Topic belongs to the current user.
check_topic_owner(request, topic)
entries = topic.entry_set.order_by('-date_added')
context = {'topic': topic, 'entries': entries}
return render(request, 'learning_logs/topic.html', context)
#login_required
def new_topic(request):
"""Add a new topic."""
if request.method != 'POST':
# No data submitted; create a blank form.
form = TopicForm()
else:
# POST data submitted; process data.
form = TopicForm(data=request.POST)
if form.is_valid():
new_topic = form.save(commit=False)
new_topic.owner = request.user
new_topic.save()
return HttpResponseRedirect(reverse('learning_logs:topics'))
context = {'form': form}
return render(request, 'learning_logs/new_topic.html', context)
#login_required
def new_entry(request, topic_id):
"""Add a new entry for the particular topic."""
topic = get_object_or_404(Topic, id=topic_id)
check_topic_owner(request, topic)
if request.method != 'POST':
# No data submitted; create a blank form.
form = EntryForm()
else:
# POST data submitted; process data.
form = EntryForm(data=request.POST)
if form.is_valid():
new_entry = form.save(commit=False)
new_entry.topic = topic
if new_entry.topic.owner == request.user:
new_entry.save()
else:
return Http404
return HttpResponseRedirect(reverse('learning_logs:topic',
args=[topic_id]))
context = {'topic': topic, 'form': form}
return render(request, 'learning_logs/new_entry.html', context)
#login_required
def edit_entry(request, entry_id):
"""Edit an existing entry."""
entry = get_object_or_404(Entry, id=entry_id)
topic = entry.topic
check_topic_owner(request, topic)
if request.method != 'POST':
# Initial request; pre-fill form with the current entry.
form = EntryForm(instance=entry)
else:
# POST data submitted; process data.
form = EntryForm(instance=entry, data=request.POST)
if form.is_valid():
form.save()
return HttpResponseRedirect(reverse('learning_logs:topic',
args=[topic.id]))
context = {'entry': entry, 'topic': topic, 'form': form}
return render(request, 'learning_logs/edit_entry.html', context)
My learning_logs\forms.py looks like this:
from django import forms
from .models import Topic, Entry
class TopicForm(forms.ModelForm):
class Meta:
model = Topic
fields = ['text']
labels = {'text': ''}
class EntryForm(forms.ModelForm):
class Meta:
model = Entry
fields = ['text']
labels = {'text': ''}
widgets = {'text': forms.Textarea(attrs={'cols': 80})}
My learning_logs\templates\learning_logs\new_topic.html looks like this:
{% extends "learning_logs/base.html" %}
{% load bootstrap3 %}
{% block header %}
<h2>New topic:</h2>
{% endblock header %}
{% block content %}
<h1>Add a new topic:</h1>
<form action="{% url 'learning_logs:new_topic' %}" method='post'
class="form">
{% csrf_token %}
{% bootstrap_form form %}
<div class="form-check">
<input class="form-check-input" type="checkbox" value=True id="public">
<label class="form-check-label" for="public">
Make it public?
</label>
</input>
</div>
{% buttons %}
<button name="submit" class="btn btn-primary">Add Topic</button>
{% endbuttons %}
</form>
{% endblock content %}
I just can't seem to fix this error. Thanks in advance! Any help is appreciated.
You have:
<input class="form-check-input" type="checkbox" value=True id="public">
<label class="form-check-label" for="public">
Make it public?
</label>
</input> # <<< this closing tag is wrong
correct input tag :
<input class="form-check-input" type="checkbox" value=True id="public" />
So you'll have:
<label class="form-check-label" for="public">
Make it public?
</label>
<input class="form-check-input" type="checkbox" value=True id="public" />