Django: combining a DetailView and a form on the same view - python

Is there a way I can combine a detailview and a form into the same view? I'm making a rental system where when a user clicks on a particular house, he will be directed to a detailview page of that house. And after viewing the details of the house he intends to rent, he will fill a form while still on the detailview page then the form will be saved in the database.
Here's what I have so far
views.py
class HouseDetailView(DetailView):
model = House
template_name = 'Home/detail.html'
def get_context_data(self, **kwargs):
context = super(HouseDetailView, self).get_context_data(**kwargs)
context['house_list'] = House.objects.all()
return context
def Callback (request):
form = CallbackForm()
if request.method == 'POST':
form = CallbackForm(request.POST)
if form.is_valid():
form.save()
messages.success(request, 'Callback Submission Successful.')
return redirect('Home')
context = {'form':form}
return render(request, 'Home/callback.html', context)
urls.py
from django.urls import path
from .views import (
HouseView,
HouseDetailView,
)
from . import views
urlpatterns = [
path('Home/', views.Home, name='Home'),
path('SignUp/', views.SignUp, name='SignUp'),
path('Login/', views.Login, name='Login'),
path('house/', HouseView.as_view(), name='house'),
path('callback/', views.Callback, name='callback'),
path('Logout/', views.Login, name='Logout'),
path('<slug:slug>/', HouseDetailView.as_view(), name='detail'),
]
forms.py
from django.forms import ModelForm
from django.contrib.auth.forms import UserCreationForm
from django.contrib.auth.models import User
from django import forms
from .models import House, Lease, Agent, Customer, Callback
class CreateUserForm(UserCreationForm):
class Meta:
model = User
fields = ['username', 'email', 'password1', 'password2']
class CallbackForm(ModelForm):
class Meta:
model = Callback
fields = ['Username', 'Phone', 'email', 'HouseId']
Templates
lease.html
{% extends "Home/Base.html" %}
{% load static %}
{% block content %}
<div class="row">
<div class="col-lg-6">
<div class="box-element1">
<form method="POST" action="">
<div id="user-info">
<div class="d-flex justify-content-center">
<h3 id="form-title">Personal Details</h3>
</div>
{% csrf_token %}
<div class="input-group mb-2">
<span class="input-group-text"><i class="fas fa-user"></i></span>
<input type="text" class="form-control" name="Username" >
</div>
<div class="input-group mb-2">
<div class="input-group-append">
<span class="input-group-text"><i class="fas fa-phone-square"></i></span>
</div>
<input type="text" class="form-control" name="Phone" >
</div>
<div class="input-group mb-2">
<div class="input-group-append">
<span class="input-group-text"><i class="fas fa-envelope-square"></i>
</span>
</div>
<input type="email" class="form-control" name="email" >
</div>
<div class="input-group mb-2">
<div class="input-group-append">
<span class="input-group-text"><i class="fas fa-home"></i></span>
</div>
<input type="HouseId" class="form-control" name="HouseId" >
</div>
<div class="d-flex justify-content-center mt-3">
<input id="form-button" class="btn btn-success btn-block" type="submit"
name="Lease" value="Get a Call Back">
</div>
</div>
</form>
{{form.errors}}
<script>
var form_fields = document.getElementsByTagName('input')
form_fields[1].placeholder='Username..';
form_fields[2].placeholder='Phone..';
form_fields[3].placeholder='email..';
form_fields[4].placeholder='HouseId..';
for (var field in form_fields){
form_fields[field].className += ' form-control'
}
</script>
{% if messages %}
<ul class="messages">
{% for message in messages %}
<li{% if message.tags %} class="{{ message.tags }}" {% endif %}>{{ message }}</li>
{% endfor %}
</ul>
{% endif %}
</div>
<br>
<div class="box-element hidden" id="payment-info">
<small>Paypal Options</small>
</div>
</div>
<div class="col-lg-5">
<div class="card mb-4 shadow-sm">
<img class="thumbnail" src="{{house.image.url}}">
<div class="box-element-house">
<h6><strong> {{house.HouseId}}</strong></h6>
<h6><strong>Type: {{house.HouseType}}</strong></h6>
<h6><strong>Location: {{house.Location}}</strong></h6>
<h6><strong>Status: {{house.Status}}</strong></h6>
<h6><strong>Rent: Ksh.{{house.Rent|floatformat:2}}</strong></h6>
<a btn-outline-success ></a>
</div>
</div>
</div>
</div>
{% endblock content %}
The image below is how the page is supposed to look like
https://drive.google.com/file/d/1gTy2awJU2GCZYhCHauhOgB5DIPQaTLYw/view?usp=sharing
Thanks in advance.

To add any additional context (like form) to your view just override get_context_data method on DetailView and add it to your template's context
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['callback_form'] = CallBackForm(initial={'HouseId': self.object.id})
return context
then you should of course display it somehow on your page in a
<form method='post' action={% url 'your-callback-from-handling-url' %}>
{{ callback_form.as_p }}
<button type='submit'...>...</button>
</form>
and from then you can use another django's built-in View - CreateView which will under the 'your-callback-from-handling-url' url
class CallBackCreateView(CreateView):
model = CallBack
fields = ['Username', 'Phone', 'email', 'HouseId']
success_url = 'url-to-redirect-after-creating-the-CallBack'
This will automatically create your CallBack instance - of course this needs some polishing like error handling and so on. Please read the docs here

Related

How can I display Error Messages for the logging funtion in Django?

I am a new Django Developer, and I have some trouble with the login function. My problem is that my system does not show error messages even though it is invalid. I try to enter the wrong username and password, and I expect there are some error messages displayed. However, it does not work. Here is my code:
login_register.html
<div class="login-form">
<form action="" method="POST">
{% csrf_token %}
{% if form.errors %}
{% for field in form %}
{% for error in field.errors %}
<p> {{ error }} </p>
{% endfor %}
{% endfor %}
{% endif %}
<h2 class="text-center">Sign in</h2>
<div class="form-group">
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text">
<span class="fa fa-user"></span>
</span>
</div>
<input
type="text"
class="form-control"
name="username"
placeholder="Username"
required="required"
/>
</div>
</div>
<div class="form-group">
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text">
<i class="fa fa-lock"></i>
</span>
</div>
<input
type="password"
class="form-control"
name="password"
placeholder="Password"
required="required"
/>
</div>
</div>
<div class="form-group">
<button type="submit" class="btn btn-primary login-btn btn-block">
Sign in
</button>
</div>
<div class="clearfix">
<a href="{% url 'reset_password' %}" class="text-center"
>Forgot Password?</a
>
</div>
</form>
<p class="text-center text-muted small">
Don't have an account? Register here!
</p>
</div>
view.py
def loginUser(request):
page = 'login'
if request.user.is_authenticated:
return redirect('starting-page')
if request.method == 'POST':
username = request.POST['username'].lower()
password = request.POST['password']
try:
user = User.objects.get(username=username)
except:
messages.error(request, 'Username does not exist')
user = authenticate(request, username=username, password=password)
if user is not None:
login(request, user)
return redirect(request.GET['next'] if 'next' in request.GET else 'starting-page')
# return redirect('starting-page')
else:
messages.error(request, 'Username OR password is incorrect')
return render(request, 'users/login_register.html')
form.py
from django.forms import ModelForm
from django.contrib.auth.forms import UserCreationForm
from django.contrib.auth.models import User
from .models import Profile
class CustomUserCreationForm(UserCreationForm):
class Meta:
model = User
fields = ['first_name', 'email', 'username', 'password1', 'password2']
labels = {
'first_name': 'Name',
}
def __init__(self, *args, **kwargs):
super(CustomUserCreationForm, self).__init__(*args, **kwargs)
for name, field in self.fields.items():
field.widget.attrs.update({'class': 'input'})
urls.py
from django.urls import path
from . import views
urlpatterns = [
path('', views.loginUser, name="login"),
path('login/', views.loginUser, name="login"),
path('logout/', views.logoutUser, name="logout"),
path('register/', views.registerUser, name="register"),
]
Try implementing the below code in your template. If there are any messages that are added in view, will be displayed.
{% for message in messages %}
<div class="alert alert-info">{{message}}</div>
{% endfor %}

Error: NoReverseMatch: Reverse for 'index' not found. 'index' is not a valid view function or pattern name

I'm developing websystem in Django, when I write a login function gives me this error:
NoReverseMatch at /login/ Reverse for 'index' not found. 'index' is not a valid view function or pattern name.
I checked the code a hundred times but there is something I'm missing and can't find the solution. I've been stuck on this for a while, can't seem to fix this error.
The other parts from the website are working fine.
views.py
#login_required
def index(request):
return render(request, 'dashboard.html')
def loginPage(request):
form = AuthenticationForm()
if request.method == 'POST':
username = request.POST.get('username')
password = request.POST.get('password')
user = authenticate(username=username, password=password)
if user is not None:
login(request, user)
if request.GET.get('next'):
return redirect(request.GET.get('next'))
else:
return redirect('index')
return render(request, 'login.html', {'form': form})
urls.py:
urlpatterns = [
path('login/', views.loginPage, name="login"),
path('logout/', views.logoutUser, name="logout"),
path('', views.index, name="index"),
]
login.html
<body>
<div class="container h-100">
<div class="d-flex justify-content-center h-100">
<div class="user_card">
<div class="d-flex justify-content-center">
<h3 id="form-title">Login</h3>
</div>
<div class="d-flex justify-content-center form_container">
<form method="POST" action="">
{% csrf_token %}
{{form.as_p}}
<div class="d-flex justify-content-center mt-3 login_container">
<input class="btn login_btn" type="submit" value="Login">
</div>
</form>
</div>
</body>
dashboard.html
{% extends 'main.html' %}
{% block content %}
<br>
<div class="row">
<div class="col-md-10">
<h5>Patients:</h5>
<hr>
<a class="btn btn-sm" href="">+ Create Patient</a>
<div class="card card-body">
<table class="table table-sm">
<tr>
<th></th>
<th>Patient</th>
<th>E-mail</th>
<th>ID</th>
<th>Language</th>
<th>Comment</th>
<th>Remove</th>
</tr>
</table>
</div>
</div>
</div>
{% endblock %}
Can anybody see what I'm missing?
i guess you should change this line
return redirect('index')
to
return redirect('APPNAME:index')
refer to
https://docs.djangoproject.com/en/2.2/topics/http/shortcuts/#redirect

How can I use two different functions in one HTML page Django

I wanted to create a website where I can List all created forms and also create forms in the same page. But I could'n figure it out. Firstly I tried it with two classes which linked to the same HTML file but then I read that this is wrong then I tried to write the two classes in one with the get post and get_queryset functions. However now I can only create forms and if I am deleting the get function the I can list the created forms.
Thank You very much and here are my views.py and HTML.
views.py
from django.shortcuts import render,redirect
from django.contrib.auth.decorators import login_required
from django.views import generic
from .models import PostModel
from .forms import PostForm
# Create your views here.
class PostList(generic.ListView):
template_name = 'home.html'
form_class=PostForm
def get_queryset(self):
return PostModel.objects.order_by('-created_on')
def get(self, request, *args, **kwargs):
form = self.form_class()
return render(request, self.template_name, {'form': form})
def post(self,request,*args, **kwargs):
form=self.form_class(request.POST)
if form.is_valid():
form.save()
return redirect('home')
return render(request,self.template_name,{'form':form})
class PostDetail(generic.DetailView):
model = PostModel
template_name = 'post_detail.html'
home.html
{% extends "base.html" %}
{%block content%}
<div class="container">
<div class="row">
<!-- Blog Entries Column -->
<div class="col-md-6 mt-3 left mx-auto">
{% for post in postmodel_list %}
<div class="card mb-4 block">
<a class="overlay" href="{% url 'post_detail' post.slug %}"style="text-decoration:none"> </a>
<div class="card-body inner">
<p style="text-align:right;float:right;margin-top:10px;" class="card-text text-muted h6"><a style="text-decoration:none" href="https://google.com">#{{ post.author }}</a> </p>
<h2 class="card-title">{{ post.title }}</h2>
</div>
</div>
{% endfor %}
</div>
</div>
</div>
<div class="col-md-4 float-right ">
<button style= "position: fixed; bottom:50px;" type="button" class="btn btn-primary" data-toggle="modal" data-target="#exampleModal" data-whatever="#mdo">Open modal for #mdo</button>
</div>
<div class="modal fade" id="exampleModal" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel" aria-hidden="true">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="exampleModalLabel">New message</h5>
</div>
<div class="modal-body">
<form method="post" style="margin-top: 1.3em;">
{% csrf_token %}
{{ form }}
<div class="modal-footer">
<button type="submit" class="btn btn-primary">Submit</button>
<button type="submit" class="btn btn-secondary" data-dismiss="modal">Close</button>
</div>
</form>
</div>
</div>
</div>
</div>
<style>
.card{
box-shadow: 0 16px 48px #E3E7EB;
}
</style>
{%endblock content%}
If I understand the question properly you can use CreateView instead of ListView and return the post lists in the context from get_context_data.
class PostList(generic.CreateView):
template_name = 'home.html'
form_class=PostForm
model = Post
def get_context_data(self, **kwargs)
context = super().get_context_data(**kwargs)
context ['postmodel_list'] = PostModel.objects.order_by('-created_on')
return context

How to you put a checkbox in a django.contrib.auth.forms UserCreationForm

I have multiple fields in my Django UserCreationForm including the already included username, password 1, and password 2. I have also added and email field. I am unable to add a checkbox.
from django import forms
from django.contrib.auth.models import User
from django.contrib.auth.forms import UserCreationForm
class UserRegisterForm(UserCreationForm):
email = forms.EmailField(required=True)
is_teacher = forms.BooleanField(required=True)
class Meta:
model = User
fields = ['is_teacher', 'username', 'email', 'password1', 'password2']
I want there to be a checkbox to declare if the user wants to register as a teacher or as a student.
{% extends 'base.html' %}
{% block content %}
<div class="container row">
<form method="POST" class='input-field col s6 offset-s3' >
{% csrf_token %}
<fieldset>
<legend>
Sign Up:
</legend>
{{form.as_p}}
</fieldset>
<br>
<button class="btn waves-effect waves-light green accent-2" type="submit" name="action">Submit
<i class="material-icons right">send</i>
</button>
<br><br>
<div>
<small>
Already have an account? <a href='{% url "login"%}'>Sign In</a>
</small>
</div>
</form>
</div>
{% endblock %}
from django.shortcuts import render, redirect
from .forms import UserRegisterForm
from django.contrib import messages
from django.contrib.auth.decorators import login_required
# Create your views here.
def register(request):
if request.method == "POST":
form = UserRegisterForm(request.POST)
if form.is_valid():
form.save()
username = form.cleaned_data.get('username')
messages.success(request, f'Account created for {username}!')
return redirect('/')
else:
form = UserRegisterForm()
return render(request, 'users/register.html', {'form': form})
#login_required
def profile(request):
return render(request, 'users/profile.html')
You already added the BooleanField in django form here:
class UserRegisterForm(UserCreationForm):
email = forms.EmailField(required=True)
is_teacher = forms.BooleanField(required=True)
class Meta:
model = User
fields = ['is_teacher', 'username', 'email', 'password1', 'password2']
looks good you only need to add the it to your html
{% extends 'base.html' %}
{% block content %} <div class="container row">
<form method="POST" class='input-field col s6 offset-s3' >
{% csrf_token %}
<fieldset>
<legend>
Sign Up:
</legend>
{{form.as_p}}
</fieldset>
<br>
<button class="btn waves-effect waves-light green accent-2" type="submit" name="action">Submit
<i class="material-icons right">send</i>
</button>
<br><br>
<div>
<small>
Already have an account? <a href='{% url "login"%}'>Sign In</a>
</small>
</div>
<div name="new is teacher checkbox">
{{form.is_teacher}}
</div>
</form>
</div> {% endblock %}
You can access the value of is_teacher in views too by forms.cleaned_data.get('is_teacher')

Typeerror for file deletion from database in django

I'm trying to get together code that uploads and deletes photos from a django/bootstrap carousel, as well as a database. However, I can't seem to get past this error:
TypeError at /alzheimers/delete
delete() takes exactly 2 arguments (1 given)
Can anyone help me? I'm kind of a noob at django, and writing this code is like pulling teeth, so any help would be greatly appreciated.
My code:
Carousel.html:
{% load staticfiles %}
{% load filename %}
<div class="container">
<div class="row">
<div class="col-md-12">
<div id="myCarousel" class="carousel slide" data-ride="carousel">
<div class="carousel-inner" role="listbox">
{% for document in documents %}
<div class="item {% if forloop.first %} active {% endif %}">
<div class="row">
<div class="col">
<li>{{document.docfile.name}}</li>
<img src = "{{STATIC_URL}}img/{{document|filename}}" >
<p align="center"><form style="text-align:center" action="{% url 'webportal:delete' %}" method="post" enctype="multipart/form-data">
{% csrf_token %}
<p>{{ form.non_field_errors }}</p>
<p>{{ form.Document.label_tag }} {{ form.Document.help_text }}</p>
<p>
{{ form.Document.errors }}
{{ form.Document.docfile }}
</p>
<p><input type="submit" value="Delete" /></p>
</form></p>
</div>
</div>
</div>
{% endfor %}
</div>
<a class="left carousel-control" href="#myCarousel" role="button" data-slide="prev">
<span class="glyphicon glyphicon-chevron-left"></span>
<span class="sr-only">Previous</span>
</a>
<a class="right carousel-control" href="#myCarousel" role="button" data-slide="next">
<span class="glyphicon glyphicon-chevron-right"></span>
<span class="sr-only">Next</span>
</a>
</div>
<!-- /.carousel -->
</div>
</div>
<form action="{% url 'webportal:carousel' %}" method="post" enctype="multipart/form-data">
{% csrf_token %}
<p>{{ form.non_field_errors }}</p>
<p>{{ form.docfile.label_tag }} {{ form.docfile.help_text }}</p>
<p>
{{ form.docfile.errors }}
{{ form.docfile }}
</p>
<p><input type="submit" value="Upload" /></p>
</form>
</div>
Views.py:
from django.shortcuts import render
from django.shortcuts import render, redirect, get_object_or_404
from django.contrib.auth import authenticate, login
from webportal.views.authentication import LoginForm
from django.shortcuts import render_to_response
from django.template import RequestContext
from django.http import HttpResponseRedirect
from django.http import HttpResponse
from django.core.urlresolvers import reverse
from django.conf import settings
from webportal.forms.forms import DocumentForm
from webportal.models import Document, DeleteForm
is_server = True
def delete(request, my_id):
Deleted=get_object_or_404(Document, docfile=my_id)
if request.method=='POST':
form=DeleteForm(request.POST, instance=Deleted)
if form.is_valid():
Deleted.delete()
return HttpResponseRedirect('http://127.0.0.1:8000/alzheimers/')
else:
form=DeleteForm(instance=Deleted)
return render_to_response(
'webportal/index.html',
{'documents': documents, 'form': form,},
context_instance=RequestContext(request)
)
# Redirect to the document list after POST
def carousel(request):
# Handle file upload
if request.method == 'POST':
form = DocumentForm(request.POST, request.FILES)
if form.is_valid():
newdoc = Document(docfile = request.FILES['docfile'])
newdoc.save()
# Redirect to the document list after POST
return HttpResponseRedirect('http://127.0.0.1:8000/alzheimers/')
else:
form = DocumentForm() # A empty, unbound form
# Load documents for the list page
documents = Document.objects.all()
#documents=DocumentForm().
# Render list page with the documents and the form
return render_to_response(
'webportal/index.html',
{'documents': documents, 'form': form,},
context_instance=RequestContext(request)
)
Models.py:
class Document(models.Model):
docfile = models.ImageField(upload_to='webportal/static/img/')
class DeleteForm(ModelForm):
class Meta:
model=Document
fields=[]
Forms.py:
class DocumentForm(forms.Form):
docfile = forms.ImageField(label='Select a file', help_text='max. 42 megabytes')
urls.py:
url(r'^delete', views.delete, name="delete"),
You are POSTing your object id through a form while your delete view expects it as an argument (my_id). Change it to:
def delete(request):
if request.method=='POST':
my_id = request.POST.get('id')
Deleted=get_object_or_404(Document, id=my_id)
...
As a side note, the Python convention is to use lowercase names for objects. Consider renaming Deleted to deleted.
Update: You also seem to have omitted to include the id of the object to be deleted in your form. Put the following line somewhere between your <form> and </form> tags:
<input type="hidden" name="id" value="{{ document.id }}" />

Categories