Django How to Add Base Form Template Context Processer - django

In my navbar I have a newsletter button that pops up a modal. In the modal there is a form that I can use to get the email and do some form manipulation. Since this modal is in the base template, the form must be available for all urls. I have added 'blog.context_processors.email_form' in the settings, and a context_processer.py file with the below info:
from .forms import EmailForm
def email_form(request):
return {
'email_form': EmailForm()
}
In my forms.py I have:
from django import forms
from .models import Email
class EmailForm(forms.ModelForm):
email = forms.EmailField(label='',
widget=forms.EmailInput
(attrs={'id': 'emailInput', 'class': 'article-search',
'placeholder': 'Enter yorffreur email here...', 'type': 'text'}))
class Meta:
model = Email
fields = ['email']
def clean_email(self, *args, **kwargs):
email = self.cleaned_data.get('email')
qs = Email.objects.filter(email__iexact=email)
if qs.exists():
raise forms.ValidationError('This email already exists.')
return email
In the base templates I have included the form as {{ email_form }}. But I do not know where to add the form manipulation. Usually I add it in the views but I'm new so I am an unsure how to do this.

You can write a view wherever you want that will receive the form and validate it then redirect to a page that will confirm the success of the subscription.
You just have to use the action property of the <form> tag to specify which view will handle the form :
<form method="post" action="{% url 'handle_newsletter' %}">
Then in your view, something like this :
def handle_newsletter(request):
form = EmailForm(request.POST)
if form.is_valid():
form.save()
return redirect('subscription_confirmation') # you would need to create this view
# An error occurred
print(form.errors)
return redirect('home')
Don't forget to update your urls.py with the appropriate view names.

Related

Confirm Form Resubmission with Context Required Django

I have an account page that lets users edit their accounts. When the save button is pressed, I run the following code:
request.user.first_name = request.POST['first_name']
request.user.last_name = request.POST['last_name']
request.user.save()
context['alert'] = {
'title': "Your information has been updated",
'color': 'primary',
}
As you can see, I pass in context that, in my template, renders as an alert. However, when I refresh the page, it says: Confirm Form Resubmission
How can I get rid of this error? Thanks!
A successful POST request normally results in a redirect, this is the so-called Post/Redirect/Get pattern [wiki].
In order to send messages to the user, you can make use of the messaging framework [Django-doc]. So you can add a message for a user, and then render it in the other view(s):
from django.shortcuts import redirect
from django.contrib import messages
def my_view(request):
request.user.first_name = request.POST['first_name']
request.user.last_name = request.POST['last_name']
request.user.save()
messages.success(request, 'Your information has been updated')
return redirect('name-of-some-view')

Reset Password Form Page not found

I have built a custom password reset in Django, however, after putting the information in 'PasswordResetForm' I get a 404 page not found error.
This is my code for reset_password:
def reset_password(request,username):
if request.method == 'POST':
form = PasswordResetForm(data=request.POST, user=request.user)
if form.is_valid():
form.save()
update_session_auth_hash(request, form.user)
#added this to redirect user to custom url
username = request.user.username
return redirect(reverse('main:home', kwargs={'username': username}))
#return redirect(reverse('main:home'))
else:
return redirect(reverse('main:reset_password'))
else:
form = PasswordResetForm(user=request.user)
args = {'form': form}
return render(request, 'reset_password.html', args)
My urls at myapp/urls.py
urlpatterns=[
path('signup/',views.signup,name='signup'),
path('login',views.user_login,name='user_login'),
path('',views.main_page,name='main_page'),
path('<str:username>', views.home, name='home'),
#replace home/edit with below
path('<str:username>/edit', views.edit_profile, name='edit_profile'),
path('<str:username>/password-reset', views.reset_password, name='reset_password'),
]
and my form for password reset:
class PasswordResetForm(PasswordChangeForm):
class Meta:
model = Profile
fields = ('old_password','new_password1','new_password2')
What seems to be the problem here? I do not know why I am getting this error:
Page not found (404)
Request Method: GET
Request URL: http://127.0.0.1:8000/main/test3-b/login.html?next=/main/test3-b/password-reset
This is my AbstractUser model in models.py (I do not have any other code in my models.py
class Profile(AbstractUser):
bio = models.TextField()
university = models.CharField(max_length=30)
def __str__(self):
return self.username
Your urls.py is not using the same path as the URL you are accessing. The URL example you have given is http://127.0.0.1:8000/main/test3-b/login.html but your password reset url is something like http://127.0.0.1:8000/example-user/password-reset where example-user is the username that you are trying to match inside your path.
Out of interest do you have the URL structure include a username? This is normally not wanted as you would be using request.user to access the current user. You also have the risk a users username could break your patterns as you are using str rather than slug which is safer in URL patterns as otherwise id a user was created with the username of "example/edit" then they would never be able to get to your homepage as it would match the edit_profile entry instead.
I saw that by the form rendered in HTML it does not reset the admin user password, however the others works. In my case, I just extended the user class and inserted the email field.

Is it possible to use/test a value of Django's BooleanField without a model?

I'm trying to make a workflow where the user enters data on one page, then has to check the data and tick a tickbox to accept the T&C's. So the code has to check that the checkbox is checked before going on, but doesn't care until the second step.
It's not a bound field and I think that's the problem - I don't need a model just to handle a workflow, and I don't want to have to store, in a database, a simple ephemeral field in a form!
I'm running Django 2.1.5.
I've tried every possible combination of:
test_form.fields['tickbox'].value - doesn't exist, which is ridiculous
test_form.fields['tickbox'] == False - value doesn't change at all
request.POST['tickbox'] seems to go missing?
views.py
from django.http import HttpResponse
from django.template import loader
from django.forms import Form, CharField, BooleanField
class test_form(Form):
name = CharField()
tickbox = BooleanField(required=False, initial=False)
def testview(request):
if request.method == 'POST':
testform = test_form(request.POST)
if testform.fields['tickbox'] == True:
do_things()
else:
dont_do_things()
else:
testform = test_form()
template = loader.get_template('testform.html')
context = { 'testform : userform,
}
return HttpResponse(template.render(context, request))
I should be able to test the value of the field and get a changing response depending on if the user has ticked the box or not - I seem to get True regardless?
Here is a way how to solve your issue using Class Based Views and Function Based Views:
So, first:
forms.py:
from django import forms
class CheckboxForm(forms.Form):
name = forms.CharField()
tickbox = forms.BooleanField(required=False, initial=False)
def clean_tickbox(self):
'''Here we can check if the checkbox is checked or not'''
tickbox = self.cleaned_data.get('tickbox')
if not tickbox:
# Raise an error if the checkbox is not checked
raise forms.ValidationError("You must select this option")
# And return the value
return tickbox
With a class based view:
views.py:
from django.views import View
from django.contrib import messages
class CheckboxView(View):
template_name = 'checkbox.html'
form_class = forms.CheckboxForm
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)
# check if the form is valid
if form.is_valid():
# Use Django builtin messages framework
messages.success(request, "Checked!")
else:
messages.error(request, "not checked!")
return render(request, self.template_name, {'form': form})
With function based views:
from django.contrib import messages
def checkbox_func(request, *args, **kwargs):
template = 'checkbox.html'
if request.method == 'POST':
form = forms.CheckboxForm(request.POST)
# Check if the form is valid
if form.is_valid():
messages.success(request, "Checked!")
else:
messages.error(request, "Not checked!")
else:
form = forms.CheckboxForm()
return render(request, template, {'form': form})
urls.py:
from django.urls import path
from YOUR_APP import views
urlpatterns = [
# ... Your URLS
# Class Based Views
path('checkbox/', views.CheckboxView.as_view(), name="checkbox"),
# Function Based Views
path('checkbox2/', views.checkbox_func, name="checkbox_v2")
]
And your template: checkbox.html:
{% for message in messages %}
{{message}}
{% endfor %}
<form method="POST">
{% csrf_token %}
{{ form.as_p }}
<button type="submit">Submit</button>
</form>
Demo:
First of all, yes it is perfectly possible to have FormFields without them being declared in a Model.
You seem to be trying to do form validation on your own when django already handles simple cases like this for you. Let's start by looking at the documentation of BooleanField:
Validates that the value is True (e.g. the check box is checked) if
the field has required=True
Since that is exactly what you want to validate we can change the field definition:
tickbox = BooleanField(required=True, initial=False)
Since the documentation told us that django takes care of validating that the checkbox is actually checked there is no need for the custom validation code anymore. Let's look at the view next and refactor it:
def testview(request):
if request.method == 'POST':
testform = test_form(request.POST)
# check if the form is valid (that includes the checkbox being checked)
if testform.is_valid():
do_things()
else:
dont_do_things()
else:
testform = test_form()
template = loader.get_template('testform.html')
context = {'testform': userform} # added a closing single tick
return HttpResponse(template.render(context, request))
So instead of doing custom validation you just call the .is_valid() method of your Form instance to run validation. If you want to access any of the fields instead of using testform.fields[fieldname] you'd do testform.cleaned_data[fieldname] which only accesses fields that have been validated.
For more information on form processing in general I highly recommend reading through django's Working with forms.
I simply use the "cleaned_data" to check if the field is not part of the model but its just a form field.
so in your case this will be in your view
if testform.cleaned_data['tickbox'] == True:
do_things()
else:
dont_do_things()

Wrong form is being displayed in a Django project

I have a django project I created. This django project is split into two apps, the user and common. I have a method in the user app that allows the user to submit a form which will create a new Django user. Once the form is submitted and process I want to redirect the user to the testing page in the common app and display a html template that says testing page. Everything is working, the form is being processed and the redirect is occuring. I know this because the url changed to the expected url which will display the html testing page. For some reason, even though the url is transfering to the correct one, the html template being displayed is actually the signup form html template not the correct template.
Here is the code from the common app:
views.py
# testing method
def test(request):
return render(request, 'common/test.html')
urls.py:
urlpatterns = [
url(r'^test/$', views.test, name='test'),
]
test.html:
{% extends "base.html" %}
{% block standard %}
<p>testing page</p>
{% endblock %}
here is the redirect from the users app:
this is in the signup def after the user has been created
return redirect('test')
here is the entire signup method:
# have a user signup and create his account
def Signup(request):
# check to see if form is submitted
if request.method == "POST":
# grab the form and information
form = SignupForm(request.POST)
# validating form
if form.is_valid():
# grab the form content
cd = form.cleaned_data
username = cd['username']
password = cd['password']
verify = cd['verify']
email = cd['email']
# check if passwords match
if password == verify:
# create safe passwords
secure_password = make_password(password)
# save username in sessions
# request.session['username'] = username
return redirect('test')
else:
# redirec to original forms
message = "Passwords did not match"
# users form
form = SignupForm()
# everything required for the template
parameters = {
'message':message,
'form':form,
}
# display html template
return render(request, 'user/Signup.html', parameters)
else:
# the signing up form
form = SignupForm()
message = "Please fill out the entire form"
# everything that needs to be passed to html template
parameters = {
'form':form,
'message':message,
}
# render the template
return render(request, 'user/Signup.html', parameters)

Form not showing, Only the button shows Django

I am trying to show a newsletter form, and it is not shown in the page
This is my models.py
from django.db import models
# Create your models here.
class newsletter_user(models.Model):
email = models.EmailField()
date_added = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.email
This is my forms.py
from django import forms
from .models import newsletter_user
class newsletterForm(forms.ModelForm):
class Meta:
model = newsletter_user
fields = ['email']
def clean_email(self):
email = self.cleaned_data.get('email')
return email
This is my admin.py
from django.contrib import admin
from .models import newsletter_user
# Register your models here.
class newsletterAdmin(admin.ModelAdmin):
list_display = ('email','date_added',)
admin.site.register(newsletter_user,newsletterAdmin)
This is the views.py
from django.shortcuts import render
from .models import newsletter_user
from .forms import newsletterForm
# Create your views here.
def newsletter_subscribe(request):
form = newsletterForm(request.POST or none)
if form.is_valid():
instance = form.save(commit=false)
if newsletter_user.objects.filter(email=instance.email).exists():
print("already exists")
else:
instance.save()
context = {'form':form,}
template = "/blog/templates/footer.html"
return render(request, template, context)
This is the html
<form method="post" action=''>
<div class = "input-group">
{{form}} {% csrf_token %}
<span class = "input-group-btn">
<button class="btn btn-default" type="submit">Subscribe</button>
</span>
</div>
</form>
This is my urls.py
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^$', views.BlogIndex.as_view(), name='home'),
url(r'^(?P<slug>[-\w]+)/$', views.BlogDetail.as_view(), name='entry_detail'),
url(r'^ckeditor/', include('ckeditor_uploader.urls')),
url(r'^footer/$', subscribe_views.newsletter_subscribe, name='subscribe'),
]
My Project directory
The button is shown
But the form is not shown..
This is my source in web browser RIGHT-CLICK->VIEW SOURCE
The url router will send the request to the first matching view. That is the only one that is called, and that view has to provide the context data that the template consumes. (You can also write your own context processor to insert context that you need everywhere.)
Since another pattern also matches /footer/, your request is clearly handled by some other view.
url(r'^(?P<slug>[-\w]+)/$', views.BlogDetail.as_view(), name='entry_detail'),
If the other view doesn't provide form into the context, there's nothing for Django to render.
Your view function newsletter_detail() is not called from other views, so that context is not used. (Using the undefined none there would have caused a run time error, which shows that the code was never evaluated.)
Catch-all routes such as entry_detail should either be used as the last url route, or be made more specific. Something like r'^/blog/(?P<slug>[-\w]+)/$', for instance, which will not match /footer/.
For a simple "subscribe" form in the footer, I recommend writing it as just html, and set up a route /subscribe/ to handle POST requests. There's not anything to gain by using Django's form framework for such a simple case (Just one field).
The django docs has an example of how you can implement something like this.
You footer.html template fragment should not require any context that is not automatically inserted by a context processor. Django's CsrfViewMiddleware provides the {% csrf_token %}, so that's an example of something you can use in template fragments such as a footer.
If you need some complicated form in your footer, you can write custom middleware to insert a Django Form instance in every context, (but you should probably give it a less generic name than form).
You may need to make some changes in your view somewhat like this,
def newsletter_subscribe(request):
if request.method == 'POST':
form = newsletterForm(request.POST)
if form.is_valid():
instance = form.save(commit=false)
if newsletter_user.objects.filter(email=instance.email).exists():
print("already exists")
else:
instance.save()
else:
form = newsletterForm()
context = {'form':form,}
template = "/blog/templates/footer.html"
return render(request, template, context)
You only need to initialise the form with request.POST , if request method is actually "POST". Else, just initialise a blank form.