Django - detect which form was submitted in a page - django

I have multiple form fields, and multiple submit buttons on a same page. I want to detect by which form field a POST request is triggered.
I am using CBV and my post() method has this:
def post(self, request, **kwargs):
....
form1 = Form1(request.POST, instance=Model1.objects.filter(some_filtering...)
form2 = Form2(request.POST, instance=Model2.objects.filter(some_filtering...)
form3 = Form3(request.POST, instance=Model3.objects.filter(some_filtering...)
form4 = Form4(request.POST, instance=Model4.objects.filter(some_filtering...)
# this is the code I want to know
if POST request is triggered by form1...
# do something....
return super().post(request, **kwargs)
return super().post(request, **kwargs)
How can I detect which form triggered POST request?

I did it by using the input tag in HTML template instead of button tag for submitting the form
<form name="form-1" method="POST" action="{% url 'view_url_name'%}">
{% csrf_token %}
{{ form }}
<!--use input tag for submit button -->
<input class="btn mt-3 btn-primary" name="form-1-submit" value="Submit" type="submit"/>
</form>
Note: use the different name for each form submit input.
Now in your view, you can simply check for the name attribute of the button.
if 'form-1-submit' in request.POST:
form1 = Form1(request.POST, instance=Model1.objects.filter(some_filtering...)
...
# and so on
This implementation will also cover the scenario where you are
submitting the same form from different buttons to use that form data for different
purposes.

you can use an hidden input in your form like this
<input name="formId" value="1" type="hidden"/>
... <!-- in second form --!>
<input name="formId" value="2" type="hidden"/>
then in your view check which form submitted
if request.POST.get("formId") == "1":
form1 = Form1(request.POST, instance=Model1.objects.filter(some_filtering...)
elif request.POST.get("formId") == "2":
form1 = Form2(request.POST, instance=Model1.objects.filter(some_filtering...)
...

Related

Django - Modify a generic detail view to manage a form and post data - Django

I'am new to django.
I want to reate a webpage, and display information about a specific object and have a form to send a message about this object.
At the beginning, i used a detail view to display the info about the object.
Then a created a message form based on my Message Class
I used get_context_data to pass the form in the context for the template.
I would like to know if there is a way to manage the validation of the form in the same view or should i come back to a function view?
I've seen that FormMixin can help.
Could you tell me if it's the right solution.
Thank you for your help
My view:
class LoanDetailView(DetailView):
model = Loan
def get_context_data (self, **kwargs):
context = super(LoanDetailView, self).get_context_data(**kwargs)
msg_form = MessageForm()
context['msg_form'] = msg_form
return context
In my template:
<form method="POST">
{%csrf_token%}
<fieldset class="form-group">
{{ msg_form | crispy }}
</fieldset>
<div class="form-group">
<button class="btn btn-outline-info" type="submit"> Envoyer </button>
</div>
</form>
You could use this pattern
class LoanDetailView(DetailView):
def get_context_data(self, **kwargs):
[...]
def post(self, request, *args, **kwargs):
form = MessageForm(request.POST)
if form.is_valid():
[...] # logic similiar as in function based view

How can I store form input in a session in Django?

Who can help me with the following challenge?
I have a registration template where users can sign up. They are then redirected to an url with a payment button and activated when a successful payment is made. In the HTML template I store the username in a custom field within the payment button which is used to later activate the account. Now since the user is not activated/logged in yet, I can't call the user object yet like {{user.username}}. So I want to try sessions to solve this and capture the username during registration to retrieve this session variable and put it in my custom field on a different page. But how? I tried using request.session but I’m not sure where to fit this the files below and then how to call this variable in the html template.
Any advise or help is greatly appreciated!
Here is my regbackend.py
class MyRegistrationView(RegistrationView):
form_class = UserProfileRegistrationForm
def register(self, form_class):
user_package.username = form_class.cleaned_data['username']
And here my forms.py
class SignUpForm(forms.ModelForm):
class Meta:
model = SignUp
fields = ['username', 'email']
Here my registration.html
<form method="post" action=".">
{% csrf_token %}
{{ form.username|as_crispy_field }}
<input class="btn btn-success" type="submit" value="{% trans 'Submit' %}" /></form>
And finally my HTML Template for after registration with the payment button and custom field.
<form action="some-url" method="post" target="_top">
<input type="hidden" name="custom" value="{{ session.username? }}">
</form>
Im using Django 1.9x and Django-registration-redux
This is how I keep the session to use it on another view.
On your registration form :
def registration(request):
initial={'username': request.session.get('username', None)}
form = RegistrationForm(request.POST or None, initial=initial)
if request.method == 'POST':
if form.is_valid():
request.session['username'] = form.cleaned_data['username']
return HttpResponseRedirect(reverse('your_checkout_view'))
return render(request, 'registration.html', {'form': form})
Once the username field is completed and the form submited it goes to the second view in which you can take the username that is stored in your session.
Just like this :
def your_checkout_view(request):
username = request.session['username']
...
From there you can take the stored username and do what you have to do.

django field is required validators

i have some forms at one page and I try to validate some fields too.
So if I enter wrong input at the test field, I get obviously the message ' invalid input', but also for each other field the messages 'This field is required.'.
How could I fix it? Override the clean function? But how?
class Example1Form(forms.ModelForm):
test = forms.CharField(max_length=30, validators=[RegexValidator(r'^[a-zA-Z0-9_-]+$'), MaxLengthValidator(30)])
And I also distinguish the different forms like:
class View(TemplateView):
def post(self, request, *args, **kwargs):
form_example1 = Example1Form(request.POST)
form_example2 = Example2Form(request.POST)
if form_example1.is_valid():
....
form_example2 = Example2Form()
return render(request, self.template, {'form_example1': form_example1, 'form_example2': form_example2})
if form_example2.is_valid():
....
Thank you very much for help!
First, you need a way to tell which form was submitted. You could name your submit buttons, for example:
<form>
{{ form_example1 }}
<input name="form_example_1" type="submit" value="Submit form 1">
</form>
<form>
{{ form_example2 }}
<input name="form_example_2" type="submit" value="Submit form 2">
</form>
Then, in your view, you should only bind your form to the POST data if that form was submitted. For example:
if "form_example1" in request.POST:
form_example1 = Example1Form(request.POST)
else:
form_example1 = Example1Form()
if "form_example2" in request.POST:
form_example2 = Example2Form(request.POST)
else:
form_example2 = Example2Form()

How to redirect to previous page in Django after POST request

I face a problem which I can't find a solution for. I have a button in navbar which is available on all pages and it is a button responsible for creating some content.
View that links with button:
def createadv(request):
uw = getuw(request.user.username)
if request.method =='POST':
form = AdverForm(request.POST, request.FILES)
if form.is_valid():
form.instance.user = request.user
form.save()
return HttpResponseRedirect('/', {'username': request.user.username, 'uw': uw})
args = {}
args.update(csrf(request))
args['username'] = request.user.username
args['form'] = AdverForm()
args['uw'] = uw
return render_to_response('createadv.html', args)
If you can see now I always redirect to main page '/' after creating content but I want to go back to the page with which I launched the creation of content.
You can add a next field to your form, and set it to request.path. After you processed your form you can redirect to the value of this path.
template.html
<form method="POST">
{% csrf_token %}
{{ form }}
<input type="hidden" name="next" value="{{ request.path }}">
<button type="submit">Let's Go</button>
</form>
views.py
next = request.POST.get('next', '/')
return HttpResponseRedirect(next)
This is roughly what django.contrib.auth does for the login form if I remember well.
If you pass through an intermediate page, you can pass the 'next' value via the querystring:
some_page.html
Go to my form!
template.html
<form method="POST">
{% csrf_token %}
{{ form }}
<input type="hidden" name="next" value="{{ request.GET.next }}">
<button type="submit">Let's Go</button>
</form>
You can use the HTTP_REFERER value:
return HttpResponseRedirect(request.META.get('HTTP_REFERER', '/'))
Note that this will not work if the client disabled sending referrer information (for example, using a private/incognito browser Window). In such a case it will redirect to /.
You can use this
return redirect(request.META.get('HTTP_REFERER'))
Make sure to import this
from django.shortcuts import redirect
My favorite way to do that is giving the request.path as GET parameter to the form.
It will pass it when posting until you redirect.
In Class-Based-Views (FormView, UpdateView, DeleteView or CreateView) you can directly use it as success_url.
Somewhere i read that it's bad practise to mix GET and POST but the simplicity of this makes it to an exception for me.
Example urls.py:
urlpatterns = [
path('', HomeView.as_view(), name='home'),
path('user/update/', UserUpdateView.as_view(), name='user_update'),
]
Link to the form inside of the template:
Update User
Class-Based-View:
class UserUpdateView(UpdateView):
...
def get_success_url(self):
return self.request.GET.get('next', reverse('home'))
In your function based view you can use it as follows:
def createadv(request):
uw = getuw(request.user.username)
if request.method =='POST':
form = AdverForm(request.POST, request.FILES)
if form.is_valid():
form.instance.user = request.user
form.save()
next = request.GET.get('next', reverse('home'))
return HttpResponseRedirect(next)
args = {}
args.update(csrf(request))
args['username'] = request.user.username
args['form'] = AdverForm()
args['uw'] = uw
return render_to_response('createadv.html', args)
you could do this easily with a simple one-liner JS
<button onclick="history.back()">Go Back</button>
This will take you back to the previous page of your history list.
If you don't have a history
https://www.w3schools.com/jsref/met_his_back.asp
Use HTTP_REFERER value:
for use in func return HttpResponseRedirect(request.META.get('HTTP_REFERER', '/'))
for use in template Go Back
In case this helps someone I got this to work in class based UpdateView
template
<form class="form" method="POST">
{% csrf_token %}
<!-- hidden form field -->
<input type="hidden" id="previous_page" name="previous_page"
value="/previous/page/url">
<!-- any other form fields -->
{{ form.name|as_crispy_field }}
{{ form.address|as_crispy_field }}
<!-- form submit button -->
<button class="btn btn-primary" type="submit" id="submit">Submit</button>
</form>
<!-- JS to insert previous page url in hidden input field -->
<script>
prev = document.getElementById("previous_page");
prev.value = document.referrer;
</script>
views.py
class ArticleUpdateView(generic.UpdateView):
model = Article
form_class = ArticleForm
template_name = 'repo/article_form.html'
def form_valid(self, form):
form.instance.author = self.request.user
# if form is valid get url of previous page from hidden input field
# and assign to success url
self.success_url = self.request.POST.get('previous_page')
return super().form_valid(form)
The view now redirects you back to the page where you had clicked the "Update/Edit" button. Any URL query parameters are also preserved.

Trying to use django and dropzone/

I'm trying to use dropzone.js with django.
I'm following the somewhat dated guide here (https://amatellanes.wordpress.com/2013/11/05/dropzonejs-django-how-to-build-a-file-upload-form/)
I strongly suspect My view is at issue.
def test(request):
print "test view has been called"
if request.method == 'POST':
print "test request method is POST"
form = UploadFileForm(request.POST, request.FILES)
print request
print request.FILES
if form.is_valid():
new_file = AttachedFiles(attachedfile=request.FILES['file'])
new_file.save()
id = new_file.pk
print id
print "test form valid"
return HttpResponse(json.dumps({'id': id}), content_type="application/json")
print "test form not valid"
else:
form = UploadFileForm()
data = {'form': form}
return render_to_response('mediamanager/test.html', data, context_instance=RequestContext(request))
I've tested submitting to it with the dropzone code
<!-- IMPORTANT enctype attribute! -->
<form id="my_dropzone" class="dropzone" action="/mediamanager/test/" method="post" enctype="multipart/form-data">
{% csrf_token %}
<button id="submit-all">
Submit all files
</button>
</form>
<script src="{% static 'dropzone/js/dropzone.js' %}"></script>
<script type="text/javascript">
Dropzone.options.myDropzone = {
// Prevents Dropzone from uploading dropped files immediately
autoProcessQueue : true,
init : function() {
var submitButton = document.querySelector("#submit-all")
myDropzone = this;
submitButton.addEventListener("click", function() {
myDropzone.processQueue();
// Tell Dropzone to process all queued files.
});
// You might want to show the submit button only when
// files are dropped here:
this.on("addedfile", function() {
// Show submit button here and/or inform user to click it.
console.log("blah")
});
}
};
</script>
and a basic form
<form action="{% url "test" %}" method="post" enctype="multipart/form-data">
{% csrf_token %}
<input type="file" name="file" />
<input type="submit" value="Submit">
</form>
And the form is never valid.
I'm using a modelform as suggested
class UploadFileForm(forms.ModelForm):
class Meta:
model = AttachedFiles
You can handle Dropzone posts like any other multipart form post.
Here's how I proceed:
#login_required
#usertype_required
def upload_picture(request, uid=None):
"""
Photo upload / dropzone handler
:param request:
:param uid: Optional picture UID when re-uploading a file.
:return:
"""
form = PhotoUploadForm(request.POST, request.FILES or None)
if form.is_valid():
pic = request.FILES['file']
# [...] Process whatever you do with that file there. I resize it, create thumbnails, etc.
# Get an instance of picture model (defined below)
picture = ...
picture.file = pic
picture.save()
return HttpResponse('Image upload succeeded.')
return HttpResponseBadRequest("Image upload form not valid.")
The form is dead simple
class PhotoUploadForm(forms.Form):
# Keep name to 'file' because that's what Dropzone is using
file = forms.ImageField(required=True)
In your model you need the upload_to set:
class Picture(models.Model):
[...]
# Original
file = models.ImageField(upload_to=get_upload_path)
And here's my upload path builder, but you can put anything
def get_upload_path(instance, filename):
""" creates unique-Path & filename for upload """
ext = filename.split('.')[-1]
filename = "%s.%s" % (instance.p_uid, ext)
d = datetime.date.today()
username = instance.author.username
#Create the directory structure
return os.path.join(
'userpics', username, d.strftime('%Y'), d.strftime('%m'), filename
)
Don't forget the csrf_token in the html form itself (I'm using an angularJS directive on top of it so will be different for you)
<form action="{% url 'upload_picture' %}" class="dropzone" drop-zone>
{% csrf_token %}
<div class="fallback">
<h3>Your browser is not supported.</h3>
<strong>
Click here for instructions on how to update it.
</strong>
<p>You can still try to upload your pictures through this form: </p>
<p>
<input name="file" type="file" multiple />
<input type="submit" value="Upload" />
</p>
</div>
</form>
I got dropzone js working by modifying the bootstrap example (I am using bootstrap) here: http://www.dropzonejs.com/bootstrap.html
I do not use any forms. I got a view handling the in coming ajax posts from dropzone. Here is the gist of my view code:
class AjaxUploadView(View):
"""
View for uploading via AJAX.
"""
def post_ajax(self, request, *args, **kwargs):
uploaded_file = request.FILES['file']
# Do stuff with file
# Return appropriate response
Hope it helps.