Django Contact Form using ModelForm - python

I am trying to create a contact form that will both email and store the message. I think I got the model.py, forms.py and admin.py right, and I am able to create and store (not email) a message from the admin. But I am struggling with the view.py that has to both email and store the message.
model.py:
from django.db import models
from django.contrib import admin
class Contact(models.Model):
name = models.CharField(max_length=100)
email = models.EmailField()
message = models.TextField(max_length=10)
date_created = models.DateField(verbose_name="Created on date", auto_now_add="True")
class ContactAdmin(admin.ModelAdmin):
list_display = ('name', 'email', 'message', 'date_created')
forms.py:
from django import forms
from .models import Contact
class ContactForm(forms.Form):
name = forms.CharField()
email = forms.EmailField()
When it comes to the views.py, I need some guidance in order to put together the code for the def contact(request):. I think I will have to include these modules:
from django.conf import settings
from django.shortcuts import render, HttpResponseRedirect, HttpResponse, Http404
from django.core.mail import send_mail
from .forms import ContactForm
from .models import Contact
When it comes to the template part, I am not sure on how to use the template tags to render the contact form in html.
So, I need help to figure out the correct view and template code.. I am of course open to suggestions for the rest of the code as well - As you might guessed, this is my first real Django app.
Thank you!

Something like this:
I would use a django ModelForm to generate the form:
class ContactForm(forms.ModelForm):
class Meta:
model = Contact
exclude = ('date_created', )
Docs: https://docs.djangoproject.com/en/1.7/topics/forms/modelforms/#modelform
And FormView for the actual view:
from django.conf import settings
from django.core.mail import send_mail
from django.views.generic import FormView
from .forms import ContactForm
class ContactFormView(FormView):
form_class = ContactForm
template_name = "email_form.html"
success_url = '/email-sent/'
def form_valid(self, form):
message = "{name} / {email} said: ".format(
name=form.cleaned_data.get('name'),
email=form.cleaned_data.get('email'))
message += "\n\n{0}".format(form.cleaned_data.get('message'))
send_mail(
subject=form.cleaned_data.get('subject').strip(),
message=message,
from_email='contact-form#myapp.com',
recipient_list=[settings.LIST_OF_EMAIL_RECIPIENTS],
)
form.save()
return super(ContactFormView, self).form_valid(form)
Docs: https://docs.djangoproject.com/en/1.7/ref/class-based-views/generic-editing/#formview
And your template:
{% extends 'base.html' %}
{% block title %}Send an email{% endblock %}
{% block content %}
<div class="row">
<div class="span6">
<h1>Send an email</h1>
<form action="." method="post">
{% csrf_token %}
{{ form }}
<input type="submit" value="Submit" />
</form>
</div>
</div>
{% endblock %}
{% block extrajs %}
<script src="{{ STATIC_URL }}js/jquery-1.7.1.min.js"></script>
<script type="text/javascript">
$(function() {
$('#id_name').focus()
});
</script>
{% endblock %}
Docs: https://docs.djangoproject.com/en/1.7/topics/forms/#the-template

Related

Save user who submitted form(django)

So I'm creating a reporting app for my organization I want to be able to save the user that submits the report. Problem is if I use the models.ForeignKey(User,on_delete=models.PROTECT) method on my model I get a drop down for all the users which is not what I want.
models.py
class Report(models.Model):
id = models.UUIDField(primary_key=True,default=uuid.uuid1,editable=False)
department = models.ForeignKey(Company,null=True,on_delete=models.SET_NULL)
user= models.ForeignKey(User,on_delete=models.PROTECT)
submission_date= models.DateField(auto_now=True) #invisible to user
submission_time = models.TimeField(auto_now=True) #invisible to ,user
date = models.DateField(default=now,blank=False)
time = models.TimeField(default=now,blank=False,help_text="hh:mm:ss")
location = PlainLocationField()
building = models.ForeignKey(bld,null=True,on_delete=models.SET_NULL)
size = models.PositiveIntegerField()
notes = models.TextField(blank=True)
def __str__(self):
return f'{self.date} {self.time} ({self.company})
form.py
from django.forms import ModelForm, fields
from django.shortcuts import redirect
from django.urls.conf import include
from .models import Report
from django import forms
from location_field.forms.plain import PlainLocationField
class ReportForm(forms.ModelForm):
class Meta:
model = Report
fields = '__all__'
location = PlainLocationField()
def redirect():
return redirect("Report")
views.py
from django.forms import fields
from django.forms.forms import Form
from django.http import request
from django.http.request import HttpRequest, validate_host
from django.http.response import HttpResponse, HttpResponseRedirect
from django.shortcuts import render,redirect
from django.urls.base import reverse
from django.views.generic.base import TemplateView
from django.contrib.auth import authenticate, login
from django.contrib.auth.mixins import LoginRequiredMixin
# Create your views here.
from .forms import ReportForm
from .models import Report
from django.views.generic.edit import CreateView, UpdateView, DeleteView
class ReportCreate(LoginRequiredMixin,CreateView):
Template = "templates\reports\report.html"
model = Report
fields = '__all__'
def form_valid(self, form):
return super().form_valid(form)
def get_success_url(self):
return reverse('Report')
def home(request):
return render(request,"users/home.html")
EDIT:
templates/report_form.html
{% extends "base_generic.html" %}
<head>
</head>
<body>
{% block content %}
<form action="" method="post" onsubmit="return True">
{% csrf_token %}
<table>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<input type="hidden" name="user" value= "{{ request.user }}">
{{ form.as_table }}
{{ form.media }}
</table>
<input type="submit" value="Submit">
</form>
{% endblock %}
</body>
There's a few of ways to go about this.
You can add blank=True to user model field and assign the user when overriding the form_valid method:
def form_valid(self, form):
form.instance.user = self.request.user
form.instance.save()
return super(ReportCreate, self).form_valid(form)
Or, render the user form field as a hidden field and pre-populate it:
<input type="hidden" name="user" value="{{ request.user }}

django's form is not saved in store

I have a quiz form and when I want to save the answer the model does not store its question. I want to validate the form and save it when if form.is_valid (): I remove it and instead of AnswerForm I use the direct Answer model then it saves but not so. How to change it so that I can validate it and save it with the help of the form?
my code ->
models.py
from django.db import models
# Create your models here.
from django.core.exceptions import ValidationError
class Question(models.Model):
question=models.CharField(max_length=100)
answer_question=models.CharField(max_length=100, default=None)
def __str__(self):
return self.question
class Answer(models.Model):
questin=models.ForeignKey(Question, on_delete=models.CASCADE, related_name="questions")
answer=models.CharField(max_length=100,blank=True)
def __str__(self):
return str(self.questin)
forms.py
from django import forms
from django.contrib.auth.models import User
from django.core.exceptions import ValidationError
from django.forms import ModelForm
from .models import Question,Answer
class QuestionForm(forms.ModelForm):
class Meta:
model=Question
fields="__all__"
class AnswerForm(forms.ModelForm):
class Meta:
model=Answer
fields="__all__"
views.py
from django.shortcuts import render
from django.shortcuts import render, HttpResponse
from django.http import HttpResponseRedirect
from django.shortcuts import redirect
from .forms import QuestionForm,AnswerForm
from .models import Question,Answer
import random
from django.forms import modelformset_factory
def home(request):
form=QuestionForm
if request.method=='POST':
form=QuestionForm(request.POST)
if form.is_valid():
form.save()
return render(request, "question/base.html", {"form":form})
def ans(request):
questions=Question.objects.all()
form=AnswerForm()
if request.method=="POST":
if form.is_valid():
qu=request.POST["i_id"]
question_id=int(qu)
answ=AnswerForm(questin=question_id,answer=request.POST["answer"])
answ.save()
return render(request, "question/ans.html", {"form":form, "questions":questions})
ans.html
<!DOCTYPE html>
<html>
<head>
<title>question</title>
</head>
<body>
{% for i in questions %}
<form method="POST" required>
{% csrf_token %}
{{i}}
<input type="hidden" name="i_id" value="{{ i.id }}"/>
{% for a in form.answer %}
{{a}}
{% endfor %}
<input type="submit" name="sub">
</form>
{% endfor %}
</body>
</html>

ValueError at /create_entry/

Can someone help me to solve this error?
ValueError at /create_entry/
Cannot assign "<SimpleLazyObject: <django.contrib.auth.models.AnonymousUser object at 0x000000B7BBF1BFC8>>": "Entry.entry_author" must be a "User" instance.
urls.py
from django.urls import path
from .views import HomeView, EntryView, CreateEntryView
urlpatterns = [
path('', HomeView.as_view(), name = 'blog-home'),
path('entry/<int:pk>/', EntryView.as_view(), name = 'entry-detail'),
path('create_entry/', CreateEntryView.as_view(success_url='/'), name = 'create_entry')
]
views.py
from django.shortcuts import render
from django.views.generic import ListView, DetailView, CreateView
from .models import Entry
class HomeView(ListView):
model = Entry
template_name = 'entries/index.html'
context_object_name = "blog_entries"
class EntryView(DetailView):
model = Entry
template_name = 'entries/entry_detail.html'
class CreateEntryView(CreateView):
model = Entry
template_name = 'entries/create_entry.html'
fields = ['entry_title', 'entry_text']
def form_valid(self,form):
form.instance.entry_author = self.request.user
return super().form_valid(form)
models.py
from django.db import models
from django.contrib.auth.models import User
class Entry(models.Model):
entry_title=models.CharField(max_length=50)
entry_text=models.TextField()
entry_date=models.DateTimeField(auto_now_add=True)
entry_author=models.ForeignKey(User, on_delete=models.CASCADE)
class Meta:
verbose_name_plural = "entries"
def __str__(self):
return f'{self.entry_title}'
create_entry.html
{% extends "entries/base.html" %}
{% block content %}
<div class="col-md-8"><br><br>
<!-- Blog Post -->
<div class="card mb-4">
<div class="card-header">
Create Blog Post
</div>
<div class="card-body">
<form class="form-conrol" action="" method="post">
{% csrf_token %}
{{form.as_p}}
<button type="submit" class="btn btn-primary">Post Entry</button>
</form>
</div>
</div>
</div>
{% endblock %}
I need your help for this
small project.
You are not logged in, so self.request.user is not a real user. You can use the LoginRequiredMixin [Django-doc] to restrict access to a view such that you can only post (and retrieve) the view when the user has logged in:
from django.contrib.auth.mixins import LoginRequiredMixin
class CreateEntryView(LoginRequiredMixin, CreateView):
model = Entry
template_name = 'entries/create_entry.html'
fields = ['entry_title', 'entry_text']
def form_valid(self,form):
form.instance.entry_author = self.request.user
return super().form_valid(form)

Django form not passing is_valid() test

In my Django app in a Createview class it never enters the is_valid(): statement and I can not seem to find any errors:
models.py
from django.db import models
from django.core.urlresolvers import reverse
from django.contrib.auth.models import User
from django.conf import settings
from .validators import validate_file_extension
import zipfile
class Post(models.Model):
title = models.CharField(max_length=140)
body = models.TextField(max_length=250)
date = models.DateTimeField(auto_now=True, auto_now_add=False)
album_image = models.FileField(validators=[validate_file_extension])
user = models.ForeignKey(User, default=1)
face = models.IntegerField(default=1)
def get_absolute_url(self):
return reverse('photos:detail',kwargs={'pk':self.pk})
def __str__(self):
return self.title
views.py
This is my view folder that contains a list view a detailed view and create view. Although the form doesnt pass the valid test, it still gets uploaded and is viewable by the user
from django.http import Http404
from django.http import HttpResponse
from django.shortcuts import render, get_object_or_404
from django.core.urlresolvers import reverse
from .forms import PostForm
from .models import Post
from django.contrib.auth.models import User
from django.template import loader
from django.views import generic
from django.views.generic.edit import CreateView
import cognitive_face as CF
import json
class IndexView(generic.ListView):
template_name='photos/post.html'
def get_queryset(self):
return Post.objects.filter(user=self.request.user)
class DetailView(generic.DetailView):
model = Post
template_name = 'photos/detail.html'
class PostCreate(generic.CreateView):
form = PostForm()
model = Post
if form.is_valid():
print('valid')
instance = form.save(commit=False)
username = form.cleaned_data['username']
album_image = form.cleaned_data['album_image']
instance.save()
if not form.is_valid():
print('not')
post_form.html
<html>
<body>
{% if request.user.is_authenticated%}
<h3>Hello {{request.user.username}}, please upload your image as either a .JPEG, .GIF or .PNG</h3>
{% endif %}
<div class="container-fluid">
<div class="row">
<div class="col-sm-12 col-md-7">
<div class="panel panel-default">
<div class="panel-body">
{% if request.user.is_authenticated %}
<form class="form-horizontal" role="form" action="" method="post" enctype="multipart/form-data">
{% csrf_token %}
{{form.as_p}}
{{ form.errors }}
{{ form.non_field_errors }}
<div class="col-sm-offset-2 col-sm-10">
<button type="submit" class="btn btn-success">Submit</button>
</div>
</div>
</form>
{% else %}
<p>You must be logged in to upload a file</p>
{% endif %}
</div>
</div>
</div>
</body>
</html>
urls.py
from django.conf.urls import url, include
from django.views.generic import ListView, DetailView
from photos.models import Post
from . import views
app_name = 'photos'
urlpatterns = [
url(r'^$',views.IndexView.as_view(), name='index'),
url(r'^(?P<pk>[0-9]+)/$',views.DetailView.as_view(), name='detail'),
url(r'post/add/$', views.PostCreate.as_view(), name='post-add'),
]
You are writing function based view code inside a class based view, which is incorrect.
You shouldn't need to instantiate the form, or manually check whether it is valid. Just set form_class for the view, then override form_valid or form_invalid if you need to change the behaviour when the form is valid or invalid. Since you have {{ form.errors }} in your template, it should show any errors when you submit the form.
class PostCreate(generic.CreateView):
form_class = PostForm
model = Post
See the docs on form handling with class based views for more information. You might find it easier to write a function based view to begin with, since the flow of the code is easier to follow.

Include template form to django admin

Is it possible to include model form template in django admin as follows?
models.py
class Customer(models.Model):
name = models.CharField(max_length=20)
designation = models.CharField(max_length=20)
gender = models.BooleanField()
forms.py
class CustomerForm(forms.ModelForm)
gender = forms.TypedChoiceField(
choices=GENDER_CHOICES, widget=forms.RadioSelect(renderer=HorizontalRadioRenderer), coerce=int, )
class Meta:
model = Customer
template.html
<form action="{% url 'verinc.views.customerView' %}" method="POST">{% csrf_token %}
{{ form.as_p }}
<input id="submit" type="button" value="Click" /></form>
views.py
def customerView(request):
if request.method == 'POST':
form = CustomerForm(request.POST)
else:
form = CustomerForm()
return render_to_response('myapp/template.html', {'form' : form,})
admin.py
class CustomerInline(admin.StackedInline)
model= Customer
form = CustomerForm
template = 'myapp/template.html
When I view the form in url (localhost/myapp/customer) it displays all the fields correctly. But when I view it in admin page it displays only the submit button in the block. My requirement is to view the form using templates in admin page, so that i could use some AJAX script for further process. Any help is most appreciated.
Well , Its not possible . But you can implement like this :
You should create file called admin_views.py in your app customer.
Then add url in your urls.py like
(r'^admin/customer/customerView/$', 'myapp.customer.admin_views.customerView'),
Write your view implementation inside admin_views.py like :
from myapp.customer.models import Customer
from django.template import RequestContext
from django.shortcuts import render_to_response
from django.contrib.admin.views.decorators import staff_member_required
def customerView(request):
return render_to_response(
"admin/customer/template.html",
{'custom_context' : {}},
RequestContext(request, {}),
)
customerView = staff_member_required(customerView)
And inside your admin template extend base_site.html like this one :
{% extends "admin/base_site.html" %}
{% block title %}Custmer admin view{% endblock %}
{% block content %}
<div id="content-main">
your content from context or static / dynamic...
</div>
{% endblock %}
Thats it . hit that new url after admin login . Note Not Tested :) .

Categories