Multiple POST request is not working after submit - django

I am trying to use 2 post method in a single page one is for login and other one is for contact us
login is working fine but after submitting contact us the content of login and contact us page is gone
I tried to pass various type of dictionary but still, it's not working
app/views.py
from django.http import HttpResponse, HttpResponseRedirect
from django.shortcuts import render
from homePage.forms import SignInForm,DropUsaMessage
# Create your views here.
def homePage(request):
if request.method == 'POST' and 'SignIn' in request.POST:
sign_in_detail = SignInForm(request.POST)
if sign_in_detail.is_valid():
return render(request, "index2.html",{})
elif request.method == 'POST' and 'SendMessage' in request.POST:
message_detail = DropUsaMessage(request.POST)
if message_detail.is_valid():
return render(request, "index.html",{})
else:
sign_in_detail = SignInForm()
message_detail = DropUsaMessage()
context={
"form":sign_in_detail,
"forms":message_detail
}
return render(request, "index.html",context)
index.html
<div class="container contact-form">
<form method="post">
<h3>Drop Us a Message</h3>
{% csrf_token %}
{{ forms }}<br><br>
<div class="form-group">
<input type="submit" name="SendMessage" class="btnContact" value="Send Message" />
</div>
</form>
</div>
<div class="container-fluid">
<div class="row">
<div class="col-md-8">
<img src="{% static 'img/sampleImage.jpg' %}" width="100%" height="100%" class="d-inline-block align-top" alt="">
</div>
<div class="col-md-4">
<form method="POST">
{% csrf_token %}
{{ form }}
<div class="form-check">
<span class="fpswd">Forgot password?</span>
</div>
<button type="submit" class="btn btn-primary" name="SignIn">Submit</button>
</form>
</div>
</div>
</div>
app/forms.py
from django import forms
from django.core import validators
class SignInForm(forms.Form):
email=forms.EmailField(widget=forms.EmailInput(attrs={"class": 'form-control',"placeholder":'Enter E-mail',"id": 'exampleInputEmail1'}))
password=forms.CharField(widget=forms.PasswordInput(attrs={"class":'form-control',"placeholder":'Enter Password',"id":'exampleInputPassword1'}))
class DropUsaMessage(forms.Form):
name = forms.CharField(widget=forms.TextInput(attrs={"class":'form-control',"placeholder":'Your Name'}))
email = forms.EmailField(widget=forms.EmailInput(attrs={"class": 'form-control',"placeholder":'Your E-mail',"id": 'exampleInputEmail1'}))
phone = forms.IntegerField(widget=forms.NumberInput(attrs={"class":'form-control',"placeholder":'Your Phone Number'}))
message = forms.CharField(widget=forms.Textarea(attrs={"class":'form-control',"placeholder":'Type Your Message',"style":'width:100%; height: 150px'}))
Expected Result:
After filling up the contact us form the field will be there.
Actual result:
there is no field in Contact us(except Send Message button) and no field in SignInForm(no e-mail and no password).

Just follow the code flow and you'll notice that in the case of a POST request and "SignIn" in the post, you return the rendered "index2.html" template without any context (form and forms will be undefined in your template). Idem for the other case:
return render(request, "index2.html", {}) # empty context
Worse, if the form posted is not valid, you'll notice you only define one for the forms and not the other one, so when the code execution reaches the line context = {"form": ..., "forms": ...}, one of them will be undefined and your view will "crash", return a 500 error.
context = {'form': sign_in_detail, # sign_in_detail never defined for second "if"
'forms': message_detail} # message_detail never define for first "if"
In general, when a POST is successful, you should always redirect to another view (or the same view). That's the internet convention, in order to avoid page reload issues that would resubmit the same data. Do this:
return redirect('home') # or whatever your homepage url pattern is called
Also, it would make more sense to post your forms to different views (change the action attribute of each of your <form> tags) so you can process them in separate views which is cleaner code:
<form action="{% url 'create_message' %}" method="post">

Related

Overriding django-allauth views to use with modals

I am trying to handle login/registration functionality in a modal. I got successful login/registration working by importing the LoginForm and RegistrationForm into my modal view and then posting to the appropriate allauth URLs. The desired behavior is to have forms with errors rendered asynchronously in the modal.
I have not been able to get forms with errors (email doesn't exist when trying to login, passwords don't match when registering etc.) to render as an html partial in a modal with the errors. I'm not too sure where to start when trying to add this functionality into my own view/how to piggyback on the allauth views and change their functionality.
Adding the below to my views.py and url.py I've managed to get the allauth default template to load when the form is invalid (e.g. email field does not contain a valid email) but have not been able to get my template to load.
From views.py:
class LoginViewSnippet(LoginView):
success_url = reverse_lazy('home')
template_name = 'user_app/partials/loginmodal.html'
def get_context_data(self, **kwargs):
print('here1')
context = super(LoginView,self).get_context_data(**kwargs)
return context
def form_invalid(self, form):
print('here')
error_msg = 'error'
return HttpResponse(error_msg, status=400)
login = LoginViewSnippet.as_view()
From urls.py:
path('accounts/login',user_app_views.login, name='account_login'),
From user_app/partials/loginmodal.html:
...
<div class="modal-body">
<form id="loginform" method="POST" action="{% url 'account_login' %}" autocomplete="off">
{% csrf_token %}
{% for field in loginform %}
<div class="form-group mb-3">
{{ field.errors }}
{{ field | as_crispy_field }}
</div>
{% endfor %}
</form>
</div>
<div class="mx-auto">
<button form="loginform"type="submit" class="btn btn-success" hx-post="{% url 'account_login' %}" hx-target="#modals-here">Login</button>
<button type="button" class="btn btn-secondary" onclick="closeModal()">Close</button>
</div>
...

Django - Passing an extra argument from template to urls.py to class-based view

Basically, I would like to get some advice about how to transition from a form page back to a previous page when the form is submitted.
There are other posts on similar situations but none seem to show how to implement this using a class-based view, so any advice here would be most appreciated.
So far I have come up with the approach below. Here, I am trying to pass a parameter holding the URL of the previous page, from the template for the previous page to urls.py and then onto the view. However, I don't think I am using the correct syntax in urls.py as the value of the previous_url parameter in the view is simply "previous_url".
Link in the template for the table page
Edit
URL
path('entry/<int:pk>/edit/', EntryUpdateView.as_view(previous_url="previous_url"), name='entry_update'),
View
class EntryUpdateView(LoginRequiredMixin, UpdateView):
model = Entry
template_name = 'entry_update.html'
fields = ('source', 'target', 'glossary', 'notes')
previous_url = ""
def form_valid(self, form):
obj = form.save(commit=False)
obj.updated_by = self.request.user
obj.save()
return HttpResponseRedirect(self.previous_url)
Update form
<form method="POST" novalidate>
{% csrf_token %}
<div class="mb-3">
{{ form.source|as_crispy_field }}
</div>
<div class="mb-3">
{{ form.target|as_crispy_field }}
</div>
<div class="mb-3">
{{ form.glossary|as_crispy_field }}
</div>
<div class="mb-3">
{{ form.notes|as_crispy_field }}
</div>
<div class="text-center mt-3">
<button type="submit" class="btn btn-outline-primary mx-2">Update</button>
</div>
</form>
Error
Page not found (404)
Request Method: GET
Request URL: http://localhost:8000/entry/5131/edit/previous_url
If this helps anyone, I was able to get the value of previous_url within form_valid() using self.request.GET.get('previous_url').
def form_valid(self, form):
obj = form.save(commit=False)
obj.updated_by = self.request.user
obj.save()
previous_url = self.request.GET.get('previous_url')
return HttpResponseRedirect(previous_url)

post django form data via iframe

I have a django form that I want to display on another website via iframe. The form displays ok but whenever I try to submit the form that is displayed in the iframe, I get a "server refused to connect" error message.
Is it possible to make a django form work with iframe or is there a better way to do it?
The views code for the form:
from django.views.decorators.clickjacking import xframe_options_exempt
#xframe_options_exempt
def booking_create_view(request):
form=BookingForm
submitted = False
if request.method=='POST':
form = BookingForm(request.POST)
if form.is_valid():
form.save()
return HttpResponseRedirect('/?submitted=True')
else:
form = BookingForm
if 'submitted' in request.GET:
submitted = True
return render(request, "booking_form.html",{'form':form, 'submitted': submitted})
HTML for the form:
<form class="" action="" method= POST>
<div class="form-group">
{% csrf_token %}
{{ form.as_p }}
<input type="Submit" value="Submit" class = "btn btn-secondary">
</form>
{% endif %}
The iframe:
<h1>The iframe element</h1>
<iframe src="http://127.0.0.1:8000/" title="test">
</iframe>
Note: I have also tried using a live server (pythonanywhere) that had the same result

Django Error Styling with Twitter Bootstrap

I am trying to style errors using twitter bootstrap in my Django project. Right now, I have a simple form that asks for a person's email address, has them press a button, and then submits it to the database. It also has validation checking to see if the email is unique in the database. If it is not unique, it raises an error saying "This email is already registered". However, when the error is raised, for a duplicate email, it brings an ugly bullet point list next to the input field with the text This email is already registered. I'm trying to style it to where it has a little dialog show under the input text with a yellow i icon like it does when the input does not include an # sign, i.e. it isn't an email. The ugly bullet point also appears when a valid domain isn't included in the email, e.g. there isn't a .com appended.
I think the problem lies with the way my form html is set up, or how the view handles the form's errors. Maybe, because the form field is an EmailField, the is_valid indicator doesn't validate and therefore shows the twitter bootstrap alert.
How do I get it to show the alert every time? Below is my code:
form part of the index.html
<form class="form-inline" method="post">
<div class="input-group input-group-newsletter">
<div class="form-group">
{% csrf_token %}
{{ form }}
</div>
<div class="form-group">
<div class="input-group-append">
<button class="btn btn-secondary" type="submit">Button Text</button>
</div>
</div>
</div>
</form>
views.py
from django.shortcuts import render, HttpResponse
from django.views.generic import TemplateView
from appname.forms import AppForm
class AppView(TemplateView):
template_name = 'apps/index.html'
def get(self, request):
form = AppForm()
return render(request, self.template_name, {'form': form})
def post(self, request):
form = AppForm(request.POST)
if form.is_valid():
email = form.cleaned_data['email']
form.save()
form = AppForm()
args = {'form': form, 'email': email, 'signedup': True}
else:
args = {'form': form, 'signedup': False}
return render(request, self.template_name, args)
forms.py
from django import forms
from .models import AppModel
class AppForm(forms.ModelForm):
email = forms.EmailField(required=True,
label='',
widget=forms.EmailInput(attrs={'class': 'form-control',
'placeholder': 'Enter email...',
'name': 'email',
'aria-label': 'Enter email...',
'aria-describedby': 'basic-addon'}))
class Meta:
model = AppModel
fields = ('email',)
def clean_email(self, *args, **kwargs):
email = self.cleaned_data.get("email")
if AppModel.objects.filter(email__iexact=email).exists():
raise forms.ValidationError("This email is already registered.")
return email
You may want to try Django messages framework. This site shows how its done. I have tried it myself and works fine, though I haven't tried putting icons into it.
https://simpleisbetterthancomplex.com/tips/2016/09/06/django-tip-14-messages-framework.html
Update based on the comment below:
Here are the snippets in my project
in settings.py
from django.contrib.messages import constants as messages
...
MESSAGE_TAGS = {
messages.DEBUG: 'alert-info',
messages.INFO: 'alert-info',
messages.SUCCESS: 'alert-success',
messages.WARNING: 'alert-warning',
messages.ERROR: 'alert-danger',
}
messages.html template which can be included in any template where you want to have notifications
{% if messages %}
{% for message in messages %}
<div class="alert {{ message.tags }} alert-dismissible " role="alert">
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
{{ message }}
</div>
{% endfor %}
{% endif %}
log-in.html template
<body>
{% include 'trip_monitor/messages.html' %}
<div class="login-form">
<form method="post">
{% csrf_token %}
<h2 class="text-center">Materials Management</h2>
<p align="center">Please <strong>log in</strong> to continue.</p>
<div class="form-group">
<input name="username" type="text" class="form-control" placeholder="Username" required="required" autofocus>
</div>
<div class="form-group">
<input name="password" type="password" class="form-control" placeholder="Password" required="required" id="password">
</div>
<div class="form-group">
<button type="submit" class="btn btn-primary btn-block">Log in</button>
</div>
</form>
</div>
</body>
views.py
from django.contrib import messages
def login(request):
if request.method == 'POST':
_username = request.POST['username']
_password = request.POST['password']
user = authenticate(request, username=_username, password=_password)
if user is not None:
auth_login(request, user)
return redirect('/trip/')
else:
messages.error(request, 'Username or Password is incorrect!') # this will be shown as pop-up message
return render(request, 'trip_monitor/login.html')
elif request.method == 'GET':
if request.user.is_authenticated:
return redirect('/trip/')
else:
return render(request, 'trip_monitor/login.html')

User can post comment only after login in Django

I'm coding a news website,I want the user can submit the comment of the news only after they have logged in,if not,the website will return to login.html.
Now I have made it that only the user who have logged in can submit a comment,the issue is once I log off and submit a comment the error says:
Cannot assign "<SimpleLazyObject: <django.contrib.auth.models.AnonymousUser object at 0x10fed10b8>>": "NewsComments.user" must be a "UserProfile" instance.
Note:I have rewrote the User models and rename it UserProfile .It works very well.
Here is my news/views.py:
def newsDetailView(request, news_pk):
news = News.objects.get(id=news_pk)
title = news.title
author = news.author_name
add_time = news.add_time
content = news.content
category = news.category
tags = news.tag.annotate(news_count=Count('news'))
all_comments = NewsComments.objects.filter(news=news)
comment_form = CommentForm(request.POST or None)
if request.method == 'POST' and comment_form.is_valid():
comments = comment_form.cleaned_data.get("comment")
comment = NewsComments(user=request.user, comments=comments, news=news)
comment.save()
return render(request, "news_detail.html", {
'title': title,
'author': author,
'add_time': add_time,
'content': content,
'tags': tags,
'category': category,
'all_comments': all_comments,
'comment_form': comment_form
})
Here is my news.detail.html
<form method="POST" action="">{% csrf_token %}
<div class="form-group">
<label for="exampleFormControlTextarea1"><h5>评论 <i class="fa fa-comments"></i></h5></label>
<textarea id="js-pl-textarea" class="form-control" rows="4"
placeholder="我就想说..." name="comment"></textarea>
<div class="text-center mt-3">
<input type="submit" id='js-pl-submit' class="btn btn-danger comment-submit-button" value='Submit'>
</input>
</div>
</div>
</form>
Here is my urls.py:
path('-<int:news_pk>', newsDetailView, name="news_detail"),
You could use djangos login-required-decorator.
#login_required
def newsDetailView(request, news_pk):
...
EDIT to expand the idea from my comments.
You could have two views, one with the login_required decorator. (You could also use class-based-views (CBV) if you prefer)
def view_news_details(request, news_pk):
...
#login_required
def post_comments(request, news_pk):
...
Each view would have their own url:
url(r'^(?P<news_pk>[0-9]+)/$', views.view_news_details, name='view-details'),
url(r'^(?P<news_pk>[0-9]+)/comment/$', views.post_comments, name='comment'),
Then you can have only one template but with conditional rendering. This template will be rendered by the view views.view_news_details, but the form will send its data to the other view (note the forms action attribute).
... display the news details here ...
{% if request.user.is_authenticated %}
<form method="POST" action="{% url 'comment' news_instance.pk %}">
... here goes the content of the form ...
</form>
{% endif %}
Redirect the user to your login view before let him submit any data in your views.py :
# Codes here
if request.method == 'POST': # We separe those two "if statements", because
# We want to redirect the user to login even if the form is not valid, User can bypass your security concern
# For Django < 2.0, use it with () if request.user.is_authenticated():
if request.user.is_authenticated:
return redirect("login_url_name") # Or HttpResponseRedirect("login_url")
if comment_form.is_valid():
comments = comment_form.cleaned_data.get("comment")
# Rest of codes
Important
In your template, give access to the form to only authenticated users
{% if request.user.is_authenticated %}
<form method="POST" action="">{% csrf_token %}
<div class="form-group">
<label for="exampleFormControlTextarea1"><h5>评论 <i class="fa fa-comments"></i></h5></label>
<textarea id="js-pl-textarea" class="form-control" rows="4"
placeholder="我就想说..." name="comment"></textarea>
<div class="text-center mt-3">
<input type="submit" id='js-pl-submit' class="btn btn-danger comment-submit-button" value='Submit' />
</div>
</div>
</form>
{% endif %}
You can check whether the requested user is logged-in or not by user.is_authenticated() method, which returns a boolean value.
Try the following snippet,
def newsDetailView(request, news_pk):
# code
if request.method == 'POST' and comment_form.is_valid():
if not request.user.is_authenticated():
return HttpResponse("Please do login")
comments = comment_form.cleaned_data.get("comment")
comment = NewsComments(user=request.user, comments=comments, news=news)
comment.save()
return render(request, "news_detail.html", {
'title': title,
'author': author,
'add_time': add_time,
'content': content,
'tags': tags,
'category': category,
'all_comments': all_comments,
'comment_form': comment_form
})