One Django Form in Multiple Views with Inherited Post Request - django

I haven't found any definitive documentation on this, but I have a contact form that I need to use in the sidebar in multiple views. Currently my code is dirty because I am repeating the code snippet below in multiple views to process the form. Is there a way to place the Post Request in a template that can be inherited?
View
def contact(request):
form_class = ContactForm
if request.method == 'POST':
form = form_class(data=request.POST)
messages.add_message(request, messages.SUCCESS, 'Thank you, we have received your message.')
if form.is_valid():
...
Thank you for your help.

Now, I'm assuming you do the same operation as below while every view is run:
def contact(request):
# First you choose the form.
form_class = ContactForm
# Then you want to know if request is POST type
if request.method == 'POST':
# You take the form data from given POST
form = form_class(data=request.POST)
# You add message to messages.
messages.add_message(request, messages.SUCCESS, 'Thank you, we have received your message.')
If you do the same over and over again, you can create your own function at the beginning of your views.py file of any app to make it short and not to repeat yourself again and again.
def take_message(request, form, messages, message):
if request.METHOD == "POST":
# I'm reinitializing <form> variable here.
form = form(data=request.POST)
# That <message> variable below must be a string, then you can dynamically pass your message.
messages.add_message(request, messages.SUCCESS, message)
Then use it in your view:
def contact(request):
take_message(request, ContactForm, messages, "Thanks, we got your message.")
# And the rest here.
However, I recommend for you to use class-based views since they can handle any request types as method. So, I'm changing that take_message method as below:
def take_message(request, form, messages, message):
# I'm reinitializing <form> variable here.
form = form(data=request.POST)
# That <message> variable below must be a string, then you can dynamically pass your message.
messages.add_message(request, messages.SUCCESS, message)
Then, my view is as below:
from django.views.generic import TemplateView
# And any other important imports.
# ...
class ContactView(TemplateView):
template_name = "contact.html" # This is your template.
def get(self, request):
# Do things when the method is GET. Like, viewing current messages in a hypothetical admin template.
def delete(self, request):
# Do things when the method is DELETE. Don't forget to use authentication here, so only superuser can delete messages.
def post(self, request):
# Do things when the method is POST.
# I'm assuming anonymous users can send messages, so there's no need for authentication here.
take_message(request, ContactForm, messages, "Thanks you, we got your message.")
# Other things to do.
# urls.py
url(r"^contact/$", ContactView.as_view(), name="contact-page")

Related

How to deny access of unauthorized users or show them appropriate message when they access urls of django.contrib.auth manually?

I'm making an user authentication app using Django authentication framework. I'm using provided views like LoginView and other views (from django.contrib.auth). Problem is when users (authenticated or anonymous) access urls:
path('password_reset/', views.PasswordResetView.as_view(), name='password_reset'),
path('password_reset/done/', views.PasswordResetDoneView.as_view(), name='password_reset_done'),
path('reset/done/', views.PasswordResetCompleteView.as_view(), name='password_reset_complete'),
they see the these pages. for example the page that confirms that a link to reset their password has been sent to their email. while the user is already signed in and they didn't ask for password reset and there is no email send to their email because they never asked to reset their password. they just manually accessed email has been sent confirmation page 127.0.0.1:8000/account/password_reset/done/.
How to prevent them from accessing these urls or show them appropriate message?
For such cases, you need to generate a unique request no and send it through query params e.g. 127.0.0.1:8000/account/password_reset/done?req_no=XXXXXXXX or just add a lookup field in URL e.g.
path('password_reset/done/<int:req_no> or <str:req_no>', views.PasswordResetDoneView.as_view(), name='password_reset_done'),
now you can get request no. in view, check if such request no exist if so send the templet for success message else redirect to home page or send 404 or anything you like,
to store the request no. you should create a new model and after sending the success templet, delete that req no from the database.
You can use the UserPassesTestMixin which can be imported from
from django.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixin
then you can inherit the method you want to override in views,
for ex.
from django.contrib.auth.views.PasswordResetView
after inheriting the view you can perform the thing you want to do in test function
class ClassName(UserPassesTestMixin, PasswordResetView):
def test_func(self):
# do your thing
Thanks to answers i came up with this solution. (you can use value of was_sent to decide whatever). But as i'm new and have low experience there are major doubts about the practicality of this solution. comments are appreciated. (most of the code is copied from the source. added lines are marked with #)
class CustomPasswordResetDoneView(PasswordResetDoneView):
def get(self, request, *args, **kwargs):
context = self.get_context_data(**kwargs)
self.request.session['was_sent'] = False #
return self.render_to_response(context)
def get_context_data(self, **kwargs):
# Call the base implementation first to get a context
context = super().get_context_data(**kwargs)
context['was_sent'] = self.request.session.get('was_sent', False) #
return context
class CustomPasswordResetView(PasswordResetView):
def get(self, request, *args, **kwargs):
self.request.session.get('was_sent', False) #
self.request.session['was_sent'] = False #
return self.render_to_response(self.get_context_data())
def post(self, request, *args, **kwargs):
self.request.session['was_sent'] = True #
form = self.get_form()
if form.is_valid():
return self.form_valid(form)
else:
return self.form_invalid(form)
def get_context_data(self, **kwargs):
# Call the base implementation first to get a context
context = super().get_context_data(**kwargs)
context['was_sent'] = self.request.session['was_sent'] #
return context

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()

How to add signal method for my view?

I want to count how may files the user has uploaded.
I have added signals.py
from django.dispatch import Signal
upload_completed = Signal(providing_args=['upload'])
And summary.py
from django.dispatch import receiver
from .signals import upload_completed
#receiver(charge_completed)
def increment_total_uploads(sender, total, **kwargs):
total_u += total
to my project.
My views upload
#login_required
def upload(request):
# Handle file upload
user = request.user
if request.method == 'POST':
form = DocumentForm(request.POST, request.FILES)
if form.is_valid():
newdoc = Document(docfile=request.FILES['docfile'])
newdoc.uploaded_by = request.user.profile
upload_completed.send(sender=self.__class__, 'upload')
#send signal to summary
newdoc.save()
# Redirect to the document list after POST
return HttpResponseRedirect(reverse('upload'))
else:
form = DocumentForm() # A empty, unbound form
# Load documents for the upload page
documents = Document.objects.all()
# Render list page with the documents and the form
return render(request,'upload.html',{'documents': documents, 'form': form})
This effort does not work.I got
upload_completed.send(sender=self.__class__, 'upload')
^
SyntaxError: positional argument follows keyword argument
I found signal example testing-django-signals
from .signals import charge_completed
#classmethod
def process_charge(cls, total):
# Process charge…
if success:
charge_completed.send_robust(
sender=cls,
total=total,
)
But it seems to me that classmethod would not work in my case
How to fix my method?
You don't need the 'uploads' argument for the send() method.
But a tip, if you're planning on a persistent count of the number of file uploads (which I assume you most likely are), then I think you should create a new model so that you can save it in your database.Then you can update that model every time a Document model is saved.
And I suggest you have a look at post_save. Have a nice day coding!

Handling form from different view and passing form validation through session in django

I have a requirement here to build a comment-like app in my django project, the app has a view to receive a submitted form process it and return the errors to where ever it came from. I finally managed to get it to work, but I have doubt for the way am using it might be wrong since am passing the entire validated form in the session.
below is the code
comment/templatetags/comment.py
#register.inclusion_tag('comment/form.html', takes_context=True)
def comment_form(context, model, object_id, next):
"""
comment_form()
is responsible for rendering the comment form
"""
# clear sessions from variable incase it was found
content_type = ContentType.objects.get_for_model(model)
try:
request = context['request']
if request.session.get('comment_form', False):
form = CommentForm(request.session['comment_form'])
form.fields['content_type'].initial = 15
form.fields['object_id'].initial = 2
form.fields['next'].initial = next
else:
form = CommentForm(initial={
'content_type' : content_type.id,
'object_id' : object_id,
'next' : next
})
except Exception as e:
logging.error(str(e))
form = None
return {
'form' : form
}
comment/view.py
def save_comment(request):
"""
save_comment:
"""
if request.method == 'POST':
# clear sessions from variable incase it was found
if request.session.get('comment_form', False):
del request.session['comment_form']
form = CommentForm(request.POST)
if form.is_valid():
obj = form.save(commit=False)
if request.user.is_authenticated():
obj.created_by = request.user
obj.save()
messages.info(request, _('Your comment has been posted.'))
return redirect(form.data.get('next'))
else:
request.session['comment_form'] = request.POST
return redirect(form.data.get('next'))
else:
raise Http404
the usage is by loading the template tag and firing
{% comment_form article article.id article.get_absolute_url %}
my doubt is if am doing the correct approach or not by passing the validated form to the session. Would that be a problem? security risk? performance issues?
Please advise
Update
In response to Pol question. The reason why I went with this approach is because comment form is handled in a separate app. In my scenario, I render objects such as article and all I do is invoke the templatetag to render the form. What would be an alternative approach for my case?
You also shared with me the django comment app, which am aware of but the client am working with requires a lot of complex work to be done in the comment app thats why am working on a new one.
I dont see the problem with security, except situation when you using cookies for stroring session. The performance depends on what kind of session backand you are using as well. But I cant find the point why are you complicating things!
And I dont thing that touching session in template tag is a good idea at all.
And maybe Take a look at django Comments Framework
Update:
Ok. I cant see the problems in this approach except complication. For example in my project, i'm using ajax to send data and validate it right in the comments view, therefore I do not require to redirect to original page. Other thing is that I pass the initialized Form in article view, so i'm not using templatetags.
Can provide you with my approche for example purposes:
from forms import CommentForm
from models import Comment
from django.http import HttpResponseForbidden, HttpResponse
from django.shortcuts import get_object_or_404, redirect, render
from django.utils import simplejson
from etv_persons.person.models import Person
from django.contrib import messages
def create_comment(request,slug):
if request.method != 'POST' or not request.POST or request.user.is_anonymous():
return HttpResponseForbidden('Доступ запрещен')
person = get_object_or_404(Person,slug=slug)
form = CommentForm(data=request.POST)
if form.is_valid():
Comment.objects.create(user_id=request.user.id, person=person,text=form.cleaned_data['text'])
if request.is_ajax():
msg={'msg': 'Cement was send',}
else:
messages.info(request, 'COmment was send.')
else:
if request.is_ajax(): msg={'msg': 'Error.',}
else: messages.info(request, 'Error.')
if request.is_ajax():
return HttpResponse(simplejson.dumps(msg),content_type='application/json')
else:
return redirect('person_details',**{"slug":slug,"ptype":person.type})
And in the article view we just do:
response['comment_form'] = CommentForm()
And yes, I do not validate the comments form. There is no reason. Just one text input.

django TemplateView and form

I have some problem to figure out how new django views (template view) and forms can works I also can't find good resources, official doc don't explain me how can get request ( I mean get and post) and forms in new django views class
Thanks
added for better explain
for example I have this form :
from django import forms
class ContactForm(forms.Form):
subject = forms.CharField(max_length=100)
message = forms.CharField()
sender = forms.EmailField()
cc_myself = forms.BooleanField(required=False)
and this is the code for read and print the form (old fashion way):
def contact(request):
if request.method == 'POST': # If the form has been submitted...
form = ContactForm(request.POST) # A form bound to the POST data
if form.is_valid(): # All validation rules pass
# Process the data in form.cleaned_data
# ...
return HttpResponseRedirect('/thanks/') # Redirect after POST
else:
form = ContactForm() # An unbound form
return render_to_response('contact.html', {
'form': form,
})
well my question is how you can do the same thing with template view thanks
Use a FormView instead, i.e.
from django.views.generic import TemplateView, FormView
from forms import ContactUsEmailForm
class ContactView(FormView):
template_name = 'contact_us/contact_us.html'
form_class = ContactUsEmailForm
success_url = '.'
def get_context_data(self, **kwargs):
context = super(ContactView, self).get_context_data(**kwargs)
#context["testing_out"] = "this is a new context var"
return context
def form_valid(self, form):
# This method is called when valid form data has been POSTed.
# It should return an HttpResponse.
#form.send_email()
#print "form is valid"
return super(ContactView, self).form_valid(form)
More on FormView in Django Docs
Technically TemplateView can also be used, just overwrite the post method, since by default template view does not allow you to post to it:
class ContactUsView(TemplateView):
template_name = 'contact_us/contact_us.html'
def post(self, request, *args, **kwargs):
context = self.get_context_data()
if context["form"].is_valid():
print 'yes done'
#save your model
#redirect
return super(TemplateView, self).render_to_response(context)
def get_context_data(self, **kwargs):
context = super(ContactUsView, self).get_context_data(**kwargs)
form = ContactUsEmailForm(self.request.POST or None) # instance= None
context["form"] = form
#context["latest_article"] = latest_article
return context
I think the FormView makes more sense though.
I would recommend just plodding through the official tutorial and I think realization will dawn and enlightenment will come automatically.
Basically:
When you issue a request: '''http://mydomain/myblog/foo/bar'''
Django will:
resolve myblog/foo/bar to a function/method call through the patterns defined in urls.py
call that function with the request as parameter, e.g. myblog.views.foo_bar_index(request).
and just send whatever string that function returns to the browser. Usually that's your generated html code.
The view function usually does the following:
Fill the context dict for the view
Renders the template using that context
returns the resulting string
The template generic view allows you to skip writing that function, and just pass in the context dictionary.
Quoting the django docs:
from django.views.generic import TemplateView
class AboutView(TemplateView):
template_name = "about.html"
All views.generic.*View classes have views.generic.View as their base. In the docs to that you find the information you require.
Basically:
# urls.py
urlpatterns = patterns('',
(r'^view/$', MyView.as_view(size=42)),
)
MyView.as_view will generate a callable that calls views.generic.View.dispatch()
which in turn will call MyView.get(), MyView.post(), MyView.update() etc.
which you can override.
To quote the docs:
class View
dispatch(request, *args, **kwargs)
The view part of the view -- the method that accepts a request
argument plus arguments, and returns a HTTP response. The default
implementation will inspect the HTTP method and attempt to delegate to
a method that matches the HTTP method; a GET will be delegated to
get(), a POST to post(), and so on.
The default implementation also sets request, args and kwargs as
instance variables, so any method on the view can know the full
details of the request that was made to invoke the view.
The big plusses of the class based views (in my opinion):
Inheritance makes them dry.
More declarative form of programming