How to send email with attachment from form? - django

I've been trying different ways to send an attachment from a submitted form but I'm not able to get it right.
my model:
from django.db import models
class Document(models.Model):
description = models.CharField(max_length=255, blank=True)
document = models.FileField()
uploaded_at = models.DateTimeField(auto_now_add=True)
views.py
from django.shortcuts import render, redirect
from django.core.mail import EmailMessage
from decouple import config
from .forms import ContactForm
def contact(request):
contact_form = ContactForm()
if request.method == "POST":
contact_form = ContactForm(request.POST, request.FILES)
if contact_form.is_valid():
name = request.POST.get("name")
email = request.POST.get("email")
subject = request.POST.get("subject")
message = request.POST.get("message")
upload = request.POST.get("upload")
email_content= EmailMessage("Message from django-structure project",
f"The user with name {name}, with the email {email},"
f" write the following: \n Subject: {subject} \n File{upload} \n\n Message: {message}",
"", [config('EMAIL_TO')], reply_to=[email])
try:
email_content.send()
return redirect("/contact/?valid")
except:
return redirect("/contact/?invalid")
return render(request, "contact.html", {"form": contact_form})
contact.html
<div class="row">
<div class="col">
<div class="form-group">
{% if "valid" in request.GET %}
<p style="color: blue;">Information sent successfully </p>
{% endif %}
<form action="" method="POST" enctype="multipart/form-data" style="text-align: center;">
{% csrf_token %}
<table style="color: dark; margin:20px auto;">{{form.as_table}}</table>
<input type="submit" value="Send" style="width: 150px;">
</form>
</div>
When I submit the form, I receive the message but in the content of the File, just the following string: "FleNone". (I have attached a pdf)

Related

Uploading image through an upload form to Django User profile

I am trying to create a student register page that allows the student to upload a profile photo. I am using Django User model and a StudentProfile model that has a OneToOne relation with User. Here are my codes:
student\models.py:
from django.db import models
from django.contrib.auth.models import User
class StudentProfile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE,)
avatar = models.ImageField(upload_to='student_profile/', null=True, blank=True)
def __str__(self):
return self.user.username
students/form.py:
from django import forms
class ImageUploadForm(forms.Form):
profile_photo = forms.ImageField()
eLearning/views.py:
from django.contrib.auth import authenticate, login, get_user_model
from django.http import HttpResponse
from django.shortcuts import render, redirect
from .forms import LoginForm, RegisterForm
from students.forms import ImageUploadForm
from students.models import StudentProfile
User = get_user_model()
def register_page(request):
register_form = RegisterForm(request.POST or None)
photo_upload_form = ImageUploadForm(request.POST, request.FILES)
context = {
"register_form": register_form,
"photo_upload form": photo_upload_form
}
if register_form.is_valid():
# print(register_form.cleaned_data)
username = register_form.cleaned_data.get("username")
first_name = register_form.cleaned_data.get("first_name")
last_name = register_form.cleaned_data.get("last_name")
email = register_form.cleaned_data.get("email")
password = register_form.cleaned_data.get("password")
new_user = User.objects.create_user(
username, email, password,
first_name=first_name,
last_name=last_name,
)
if photo_upload_form.is_valid():
user = username
avatar = photo_upload_form.cleaned_data.get("profile_photo")
new_user_profile = StudentProfile.objects.create(user, avatar)
print(new_user)
return render(request, "auth/register.html", context)
auth/register.html:
{% extends "base.html" %}
{% load static %}
{% block content %}
{% load crispy_forms_tags %}
<div class="container">
<div class="row my-4">
<div class="col-5">
<form action="" method="post" class="form-control">
{% csrf_token %}
{{ register_form|crispy }}
<input type="submit" class="btn btn-default" value="Submit">
</form>
</div>
<div class="col-5">
<form method="post" enctype="multipart/form-data" class="form-control">
{% csrf_token %}
<input id="id_image" type="file" class="my-2" name="image">
{{ photo_upload_form|crispy }}
</form>
</div>
</div>
</div>
{% endblock %}
I am facing 2 problems:
1) The ImageUploadForm is not rendering on to register.html
2) A StudentProfile is not being created. User is being created fine.
I also tried replacing form with ModelForm for ImageUploadForm but I get a NULL constraint for student_user since Django doesn't what user for StudentProfile is.
I have been looking through Stack Overflow. All solutions are about how to upload a user image to Django admin but I haven't found anything that shows how to associate the uploaded image to User model during registration. Forgive me if this is a repeated question. Thanks.
In your eLearning/views.py:
the context you are passing to the html page the _ is missing
context = {
"register_form": register_form,
"photo_upload form": photo_upload_form
}
This will be the reason for the ImageUploadForm is not rendering on to register.html
It's should be like
context = {
"register_form": register_form,
"photo_upload_form": photo_upload_form
}
So I figured it out. The real issue was with the register.html code. The submit button only worked for the register_form so the photo upload form was not validating, hence student profile entry was not being created. Here's the updated code:
eLearning/views.py:
from students.forms import ImageUploadForm
from students.views import upload_pic
def register_page(request):
register_form = RegisterForm(request.POST or None)
photo_upload_form = ImageUploadForm(request.POST, request.FILES)
context = {
"register_form": register_form,
"photo_upload_form": photo_upload_form
}
if register_form.is_valid():
username = register_form.cleaned_data.get("username")
first_name = register_form.cleaned_data.get("first_name")
last_name = register_form.cleaned_data.get("last_name")
email = register_form.cleaned_data.get("email")
password = register_form.cleaned_data.get("password")
new_user = User.objects.create_user(
username, email, password,
first_name=first_name,
last_name=last_name,
)
upload_pic(request, photo_upload_form, username=username)
return render(request, "auth/register.html", context)
students/views.py:
from django.contrib.auth import get_user_model
from django.http import HttpResponseRedirect
from django.shortcuts import render, redirect, HttpResponse
from .models import StudentProfile
from .forms import ImageUploadForm
def upload_pic(request, form, username):
if request.method == 'POST':
if form.is_valid():
User = get_user_model()
user = User.objects.get(username=username)
avatar = form.cleaned_data.get('profile_photo')
new_user_profile = StudentProfile.objects.create(user=user, avatar=avatar)
new_user_profile.save()
register.html:
{% extends "base.html" %}
{% load static %}
{% block content %}
{% load crispy_forms_tags %}
<div class="container">
<div class="row my-4">
<div class="col-5">
<form action="" method="post" enctype="multipart/form-data" class="form-control">
{% csrf_token %}
{{ register_form|crispy }}
{{ photo_upload_form|crispy }}
<input type="submit" class="btn btn-default" value="Submit">
</form>
</div>
</div>
</div>
{% endblock %}
You have to make sure enctype="multipart/form-data" is inside your tags or the image upload form will not get validated. I would also recommend adding an image validation method to your form.py. Something Sachin pointed out earlier: image form and validation. Hope this helps.

Django 2 make comment filed error

recently I decide to add a comment block to my template in my django app but when I add it to my app , I faced to this error :
add_comment_to_post() got an unexpected keyword argument 'item_id'
my template.html:
{% block content %}
<form action="#" method="post" novalidate="novalidate">
{% csrf_token %}
{{ form.as_p }}
<div class="row">
<div class="col-md-4">
<p><label>Name*</label><input type="text" name="your-name" value=""
size="60" class=""
aria-required="true"
aria-invalid="false"></p>
</div>
<div class="col-md-4">
<p><label>Email*</label><input type="text" name="your-email"
value=""
size="60" class=""
aria-required="true"
aria-invalid="false"></p>
</div>
<div class="col-md-4">
<p><label>Website</label><input type="text" name="your-website"
value=""
size="60" class=""
aria-required="true"
aria-invalid="false"></p>
</div>
<div class="col-md-12">
<p><label>Message</label><textarea name="your-message" cols="60"
rows="3" class=""
aria-invalid="false"></textarea>
</p>
</div>
</div>
<div class="dividewhite2"></div>
<p>
<button type="button" href="{% url 'add_comment_to_post' pk=item.pk %}"
class="btn btn-lg btn-darker">Post Comment
</button>
</p>
</form>
{% endblock %}
my models.py :
from django.db import models
from datetime import date
from django.utils import timezone
# Create your models here.
class Blogs(models.Model):
main_image = models.ImageField(upload_to='Blogs/', help_text='This Image Is Gonna To Be Show At Blogs Page.',
blank=False, default='')
class Comment(models.Model):
post = models.ForeignKey('Blog.Blogs', on_delete=models.CASCADE, related_name='comments')
author = models.CharField(max_length=200)
text = models.TextField()
created_date = models.DateTimeField(default=timezone.now)
approved_comment = models.BooleanField(default=False)
def approve(self):
self.approved_comment = True
self.save()
def __str__(self):
return self.text
my form.py:
from django.forms import ModelForm
from .models import Blogs, Comment
class CommentForm(ModelForm):
class Meta:
model = Comment
fields = ('author', 'text',)
my views.py :
from django.shortcuts import render, get_object_or_404, redirect
from Blog.forms import CommentForm
from .models import Blogs, Comment
def item(request, items_id):
items = get_object_or_404(Blogs, pk=items_id)
return render(request, 'Blog/Items.html', {'item': items, 'comments': Comment})
def add_comment_to_post(request, pk):
post = get_object_or_404(Blogs, pk=pk)
if request.method == "POST":
form = CommentForm(request.POST)
if form.is_valid():
comment = form.save(commit=False)
comment.post = post
comment.save()
return redirect('post_detail', pk=post.pk)
else:
form = CommentForm()
return render(request, 'blog/Items.html', {'form': form})
and my urls.py:
from django.urls import path
from Blog import views
from Blog import models
urlpatterns = [
path('<int:item_id>/', views.add_comment_to_post, name='add_comment_to_post'),
path('<int:items_id>/', views.item, name='Itemz'),
]
I checked my code many times but I can't understand what is my problem.
is any body know to how can I add comment to my app or what is my problem?
In addition, I'm sorry for writing mistakes in my question.
change this
def add_comment_to_post(request, pk):
To:
def add_comment_to_post(request, item_id):
Then change everywhere inside the function you wrote pk to item_id
views.py
def add_comment_to_post(request, item_id):
post = get_object_or_404(Blogs, pk=item_id)
if request.method == "POST":
form = CommentForm(request.POST)
if form.is_valid():
comment = form.save(commit=False)
comment.post = post
comment.save()
return redirect('post_detail', pk=post.pk)
else:
form = CommentForm()
return render(request, 'blog/Items.html', {'form': form, 'item': post})
and in your template:
<button type="button" href="{% url 'add_comment_to_post' item.pk %}"
class="btn btn-lg btn-darker">Post Comment
</button>
Double check your url patterns maybe? Try:
urlpatterns = [
path('<int:pk>/', views.add_comment_to_post, name='add_comment_to_post'),
The variable name in your view method needs to match the variable name in the url. Therefore you need both to be pk or both to be item_id
The Problem happen because in urls.py there were two subject that was pass to one views.
change views to this :
urlpatterns = [
path('<int:pk>/', views.item, name='Itemz'),
]
then change html part to this :
{% if not user.is_authenticated %}
<p><a href="{% url 'login' %}" class="btn btn-gr btn-xs">Login To Send
A Command.</a></p>
{% endif %}
<div class="dividewhite2"></div>
{% if user.is_authenticated %}
<form method="post" novalidate="novalidate">
{% csrf_token %}
{{ form.as_p }}
<div class="dividewhite2"></div>
<p>
<button href="{% url 'Itemz' item.id %}" type="submit"
class="btn btn-lg btn-darker">Post Comment
</button>
</p>
</form>
{% endif %}
now the Django project will run properly.

Django forms validation is not showing up in the template

My problem is, when I validate forms for errors, some text with warning would show up, But id does not. I was searching for some good answers and I tried also everything.
I was trying to implement validation in my register subpage so....
First is my model.py for registration
from django.db import models
class User(models.Model):
name = models.CharField(max_length=30)
email = models.EmailField(max_length=30)
password = models.CharField(max_length=30)
def __str__(self):
return "Login: " + self.name + \
" | e-mail: " + self.email + \
" | password: " + self.password
Second is my inputForms.py with simple validation
from django import forms
from django.contrib.auth.models import User
from django.contrib import messages
from .models import User
class NewUser(forms.Form):
name = forms.CharField(required=True)
email = forms.EmailField(required=True)
password = forms.CharField(widget=forms.PasswordInput, required=True)
pic1 = forms.CharField(required=True)
pic2 = forms.CharField(required=True)
pic3 = forms.CharField(required=True)
pic4 = forms.CharField(required=True)
pic5 = forms.CharField(required=True)
class Meta:
model = User
fields = ('name', 'email', 'password')
def clean_email(self):
email_passed = self.cleaned_data.get('email')
email_req = "yourdomain.com"
if not email_req in email_passed:
raise forms.ValidationError("Not a valid email. Please try again")
return email_passed
Third is my view.py
from django.http import HttpResponseRedirect
from django.shortcuts import render
from definitions import SITE_ROOT
from .models import User
from . import inputForms
from threading import Thread
import base64
import re
import os
def index(request):
if request.method == 'GET':
return render(request, 'register.html', {'NewUser': inputForms.NewUser})
else:
return HttpResponseRedirect('/Kniznica')
def new_user(request):
if request.method == 'POST':
form = inputForms.NewUser(request.POST)
if form.is_valid():
user_name = form.cleaned_data['name']
user_email = form.cleaned_data['email']
user_password = form.cleaned_data['password']
thread = Thread(target=save_user, args=(form, user_name, user_email, user_password, ))
thread.start()
return HttpResponseRedirect('/Kniznica')
else:
return render(request, "register.html", {'NewUser': inputForms.NewUser})
def save_user(form, user_name, user_email, user_password):
base_to_png(form.cleaned_data['pic1'], user_name, "face1.png")
base_to_png(form.cleaned_data['pic2'], user_name, "face2.png")
base_to_png(form.cleaned_data['pic3'], user_name, "face3.png")
base_to_png(form.cleaned_data['pic4'], user_name, "face4.png")
base_to_png(form.cleaned_data['pic5'], user_name, "face5.png")
user_instance = User.objects.create(name=user_name, email=user_email, password=user_password)
user_instance.save()
def base_to_png(base64_string, name, image_name):
string_without_header = re.sub('^data:image/.+;base64,', '', base64_string)
img_data = base64.b64decode(string_without_header)
if not os.path.exists(SITE_ROOT + "/faceId/" + name):
os.makedirs(SITE_ROOT + "/faceId/" + name)
filename = SITE_ROOT + "/faceId/" + name + "/" + image_name
with open(filename, 'wb') as f:
f.write(img_data)
os.chdir(SITE_ROOT + "\static\scripts")
os.system("node face-detection.js " + name + " " + image_name)
And fourth is my template.html for registration
<div>
<form action="newUser" method="post" novalidate>
{% csrf_token %}
{{ form.non_field_errors }}
{{ form.username.errors }}
{% if form.errors %}
{% for field in form %}
{% for error in field.errors %}
<p> {{ errors }} </p>
{% endfor %}
{% endfor %}
{% endif %}
<div style="display: inline-block">
Meno:{{ NewUser.name }}
</div>
<div style="display: inline-block">
Email:{{ NewUser.email }}
</div>
<div style="display: inline-block">
Heslo:{{ NewUser.password }}
</div>
<div hidden>
{{ NewUser.pic1 }}
{{ NewUser.pic2 }}
{{ NewUser.pic3 }}
{{ NewUser.pic4 }}
{{ NewUser.pic5 }}
</div>
<div>
<video id="video" width="300" height="300" autoplay></video>
</div>
<div>
<p id="counter"></p>
<button type="button" id="snap">Snap Photo</button>
<button type="submit" id="save" name="register">Registrácia</button>
</div>
<canvas hidden id="canvas" width="640" height="480"></canvas>
</form>
</div>
You have an unusual view structure here - normally you would use the same view for displaying the initial form as you would for processing the submission and displaying errors.
But nevertheless, you do have a couple of obvious issues. Firstly, in your new_user view, when the form is not valid you don't send that invalid form back, but instead send the form class itself. You should always send an instance of the form anyway, and in this case you should send back the same instance you have already validated, because that's the thing with the errors:
return render(request, "register.html", {'NewUser': form})
Secondly, in your template, you are inconsistent about what you call the form. When you render the fields, you access it via NewUser. But in trying to display the errors, for some reason you call it form. As you have used the name NewUser when you send it from the view, you should use that to display the errors:
{{ NewUser.errors }}

No input fields in Django when making a form

I'm new to django and I'm trying to create forms, but whenever I run this I end up with a submit button with no input fields. If you see any other mistakes, don't hesitate to tell me.
views.py
from django.shortcuts import render
from django.http import HttpResponse
from .forms import *
def form(request):
if request.method == 'POST':
form = forma(request.POST)
if form.is_valid():
return HttpResponseRedirect('index.html')
else:
form = forma()
return render(request, 'action.html', {"form":form})
def action(request):
return render(request, 'action.html')
**forms.py **
from django import forms
class forma(forms.Form):
your_name = forms.CharField(label='Your name', max_length=100)
index.html
<body>
<section id = "sec">
<h1>... </h1>
<h3> ... </h3>
<h4> ... <h4>
<button> ... </button>
<p>{{hola}} alo {{darwich}}</p>
<form action= "action.html" method="post">
{% csrf_token %}
{{ form }}
<input type="submit" value="Submit" />
</form>
</section>
<body>

Validation errors don't show in Django form

When I try to test my form with an email which has already been entered into the database it doesn't give the error message like it should, it redirects back to the homepage.
My views.py file looks like this:
from django.shortcuts import render
from .forms import LotteryForm
from django.http import HttpResponseRedirect
# Create your views here.
def lottery_list(request):
return render(request, 'lottery/lottery.html', {})
def lottery_new(request):
if request.method == 'POST':
form = LotteryForm(request.POST)
if form.is_valid():
form.save()
return HttpResponseRedirect('lottery_submitted')
else:
return render(request, 'lottery/lottery.html' {'form': LotteryForm()})
else:
form = LotteryForm()
return render(request, 'lottery/lottery.html', {'form': LotteryForm()})
My form is:
from django import forms
from django.db import models
from django.forms import ModelForm
from .models import Lottery
from .models import Lottery_user
from django.core.exceptions import ValidationError
class LotteryForm(forms.ModelForm):
lottery_type = forms.ModelChoiceField(queryset = Lottery.objects.all(), empty_label=None)
first_name = forms.CharField(required=True)
last_name = forms.CharField(required=True)
lottery_numbers = forms.CharField(max_length=12, required=True)
email = forms.EmailField(required=True)
telephone = forms.CharField(max_length=18,
error_messages={'invalid':'Enter a valid mobile number',
'required':'Enter a valid mobile number'})
def clean_email(self):
email = self.cleaned_data['email']
if Lottery_user.objects.filter(email=email).exists():
raise ValidationError("Email already exists")
return email
if Lottery_user.objects.filter(email=self.cleaned_data['email']).exists():
raise ValidationError("You've already entered")
return email
class Meta:
model = Lottery_user
fields = ['lottery_numbers', 'lottery_type', 'first_name', 'last_name', 'email', 'telephone',]
Template form:
<form action="{% url 'lottery_new' %}" method="post">
{% csrf_token %}
<div class="fieldWrapper">
<label for="lotterytype">Choose a lottery:</label>
{{ form.lottery_type }}
<p>See <a href=lottery_instructions>instructions and rules</a></p>
</div>
<div class="fieldWrapper">
<label for="lotterynumbers">Lottery Numbers:</label>
{{ form.lottery_numbers }}
</div>
<div class="fieldWrapper">
<label for="firstname">First name:</label>
{{ form.first_name }}
</div>
<div class="fieldWrapper">
<label for="lastname">Last name:</label>
{{ form.last_name }}
</div>
<div class="fieldWrapper">
{{ form.email.errors }}
{{ form.email.non_field_errors }}
<label for="email">Email:</label>
{{ form.email }}
</div>
<div class="fieldWrapper">
<label for="phonenumber">Telephone Number:</label>
{{ form.telephone }}
</div>
<button type="submit" class="btn btn-default">Submit</button>
</form>
I want a user to not be able to enter twice with the same email, so it should error when the user clicks submit with a used email address.
Also, does anyone know how to get the error messages to show before the user submits rather than afterwards?
You return a newly instantiated form when the form is invalid, when you should instead return the invalid form (with the errors). Try:
def lottery_new(request):
if request.method == 'POST':
form = LotteryForm(request.POST)
if form.is_valid():
....
else:
return render(request, 'lottery/lottery.html' {'form': form})