Flask 405 Method Not Allowed - Despite Allowed - flask

I am using Flask. I understand that one should specify the allowed methods via #app.route(<path>, methods=['GET', 'POST', ...]). However, even though I have "GET" in the methods list, I still get the 405 error:
The strange thing is that the "Request Method", i.e. "GET", is one of the methods in the "Allow" field in "Response Headers", shown above. How is this possible?
Here is my function for handling the route /session/<id>/admin:
#sessmgr.route('/session/<id>/admin', methods=["GET", "POST"])
def session_admin(id):
... # the details doesn't matter because they are not executed.
I am using the following form to submit the GET request
{% extends "base.fhtml" %}
{% block title %}Redirecting...{% endblock %}
{% block styles %}
{{super()}}
<link rel="stylesheet"
href="{{url_for('.static', filename='mystyle.css')}}">
{% endblock %}
{% block content %}
<div class="container h-100">
<div class="w-100 p-5">
<h7 class="text-center">Redirecting...</h7>
</div>
</div>
<form action="/session/1000/admin" method="GET" id="the-form">
<input type="submit" id="submit">
{% for key, value in args.items() %}
<input type="hidden" name="{{ key }}" value="{{ value }}">
{% endfor %}
</form>
<script>
$(document).ready(function() {
$("#the-form input[type=submit]").trigger("click");
});
</script>
{% endblock %}
The form is automatically submitted as soon as the page is loaded. However, I tried removing this automatic submission and manually click on the submit button, and the same thing happens. This doesn't make sense to me. Any ideas? Thanks!
Update
I have tried setting method='get' in the <form> but it didn't work.

Related

ckeditor not saving changes django

I have a form where in one of the fields, I use the ckeditor. However when I submit the form, the changes in the ckeditor field is not being saved. In the model, I have changed the field to aRichTextField. I have installed "ckeditor" in my apps in settings as well.
I have also both tried to load these scripts in my template:
{% load static %}
<script type="text/javascript" src="{% static "ckeditor/ckeditor-init.js" %}"></script>
<script type="text/javascript" src="{% static "ckeditor/ckeditor/ckeditor.js" %}"></script>
On top of that have I also tried to add the {{ form.media }} instead of the scripts but it does still not work.
I am using HTMX to dynamically update the form.
This is my form template right now
<form action='' method="POST" class="form" hx-post='' hx-swap='outerHTML'>
{% csrf_token %}
{{ form.media }}
<div class="form-group">
{% for field in form %}
{{ field }}
</div>
{% endfor %}
<br>
<div class='htmx-indicator'>Loading...</div>
<div class="text-center">
<button class='htmx-inverted-indicator' type='submit' >Save</button>
</div>
{% if message %}
<p>{{ message }}</p>
{% endif %}
</form>
Does anybody know why the form is not being saved?
EDIT
This is my view
#login_required
def book_update_view(request, id=None):
book = get_object_or_404(Book, id=id)
form = BookForm(request.POST or None, instance=book)
context = {
"form": form,
"object": book,
}
if form.is_valid():
form.save()
context['message'] = 'Saved!'
if request.htmx:
return render(request, "book/snippets/forms.html", context)
return render(request, "book/update.html", context)
Looks like there is a conflict between the CKEditor and HTMX. The below relies heavily on this answer. It makes the following changes:
Switches the HTMX labels to the button rather than the form
Applies an event
listener to the CKEditor - it does this via the {{field.label_tag}}
which is now included
Fixes up a misplaced tag
Try making your form something like this (don't forget to replace the name of the CKEditor field - you may need to check your source code to see how this is rendered):
<form method="post">
{% csrf_token %}
{{ form.media }}
<script>
document.body.addEventListener('htmx:configRequest', (event) => {
var element = new CKEDITOR.dom.element( document.getElementById( '{{ form.NAMEOFCKEDITORFIELD.id_for_label }}' ) );
event.detail.parameters['{{ form.NAMEOFCKEDITORFIELD.html_name }}'] = element.getEditor().getData();
})
</script>
<div class="form-group">
{% for field in form %}
{{ field.label_tag }}:<br />{{ field }}
{% endfor %}
</div>
<br>
<div class='htmx-indicator'>Loading...</div>
<div class="text-center">
<button class='htmx-inverted-indicator' type='submit' hx-post="{% url 'book_update_view_name' book.id %}" hx-target="#{{form.id}}" hx-swap="outerHTML">Save</button>
</div>
{% if message %}
<p>{{ message }}</p>
{% endif %}
if you dont want extra get parameter which might give you a problem, you can put onclick on your submit button to have ckeditor transfer the data to your field. something like this :
<script>
function saveCK(){
let ckdata = CKEDITOR.instances.your_field_id.getData();
$('#your_field_id').val(ckdata);
}
</script>
yeah, sorry for the jquery, I'm not sure vanilla javascript equivalent.
and on your submit button, add
onclick="saveCK()"
One more option is to use hx-vals.
For example:
<button type='submit' hx-vals="js:{ {{ form.NAMEOFCKEDITORFIELD.name }}: CKEDITOR.instances['{{ form.NAMEOFCKEDITORFIELD.id_for_label }}'].getData()}" hx-post=POSTURL>Save</button>

Why htmx trigger only work once in django

I am using htmx to trigger a field in Django ModelForm with the following codes.
Everything works as it supposed to the first time around, but after that when you change the option select field nothing happen, no trigger whatsoever. I have to reset and go back to url 'listing' for it to respond again. I want the code to trigger the result everytime I change the option select field before I finally submit. Any help is well appreciated.
class Listing(model.Model):
option=models.ForeignKey(Option,on_delete=models.CASCADE)
package=models.ForeignKey(Package,on_delete=models.CASCADE,blank=True,null=True)
number=models.ForeignKey(Number,on_delete=models.CASCADE,blank=True,null=True)
period=models.ForeignKey(Period,on_delete=models.CASCADE,blank=True,null=True)
title=models.CharField(max_length=20)
class ListingForm(ModelForm):
class Meta:
model=Listing
fields='__all__'
class ListingCreateView(CreateView):
model=Listing
form_class=ListingForm
template_name='listing_form.html'
success_url='/forms/listing/'
def option(request):
option=request.GET.get('option')
form=ListingForm
context={'option':option,'form':form}
return render(request,'partial_option.html',context)
urlpatterns=[
path('',ListingCreateView.as_view(),name='listing-create'),
path('option/',option,name='option'),
]
listing_form.html
{% load widget_tweaks %}
<!DOCTYPE html>
<html>
<head>
<script src="https://unpkg.com/htmx.org#1.6.1"></script>
</head>
<body>
<h1>Listing Form</h1>
<form method="post">
{% csrf_token %}
<div>
{{ form.option.label_tag }}
{% render_field form.option hx-get="/forms/option"
hx-trigger="change" hx-target="#option" hx-swap="outerHTML" %}
</div>
<div id="option"></div>
<input type="submit" value="Send">
</form>
<script>
document.body.addEventListener('htmx:configRequest', (event) =>
{
event.detail.headers['X-CSRFToken']='{{csrf_token}}';
})
</script>
</body>
</html>
partial_option.html:
{% if option %}
{% if option =='1' %}
<p>You have chosen option 1</p>
{% elif option == '2' %}
<p>You have chosen option 2</p>
{{ form.package.label_tag }}
{{ form.package }}
{% elif option == '3' %}
<p>You have chosen option 3</p>
{{ form.number.label_tag }}
{{ form.number }}
{{form.period.label_tag }}
{{ form.period }}
{% endif %}
{% else %}
<p>You have no option</p>
{% endif %}
{{ form.title.label_tag }}
{{ form.title }}
You have set the hx-swap="outerHTML" method, so HTMX will replace the target element with the response. Since your response does not contain a new <div id="option"> element, after the first request/swap cycle HTMX cannot find the target.
To solve this issue, change the swap method to innerHTML or embed the response in a <div id="option"></div> element.

Django login not showing errors

I know there are multiple questions like this around, but none of them contain a clear answer. I am using the default authentication from Django, but have trouble displaying something like 'Your username/password combination is incorrect'. Is it possible to fix this without making a custom view function?
My urls.py looks like this:
url(r'^login/$', auth_views.login, {'template_name': 'login.html'},
name='mysite_login')
Then my login.html has the following code:
{% block content %}
<section class="content">
<div class="container block">
<div class="row">
<div class="col-md-12"></div>
<form action="{% url 'mysite_login' %}" class="form-control" method="post" accept-charset="utf-8">
{% csrf_token %}
{% for field in form %}
<p>
{{ field.label_tag }}<br>
{{ field|addcss:'form-control' }}
{% if field.help_text %}
<small style="color: grey">{{ field.help_text|safe }}</small>
{% endif %}
{% for error in field.errors %}
<p style="color: red">{{ error }}</p>
{% endfor %}
</p>
{% endfor %}
<button type="submit" class="btn btn-dark">Login</button>
<input class="form-control" type="hidden" name="next" value="{{ next }}"><br>
</form>
</div>
</div>
</div>
</section>
{% endblock %}
So this all works, except for displaying the error messages. I've seen answers where you can write a custom view function and form to fix this, but I assume it should be also possible while using the build-in login functionality right? Thanks a lot.
The built-in login form doesn't display errors at an individual field level; it's a security risk to say that just the password is wrong, because it confirms the existence of a particular username. So the errors are raised in the general clean() method and are displayed in the template via {{ form.non_field_errors }}.

Django-Bootstrap3 issues with inline form layout

Using the following code to render a form:
<div class="row">
<div class="col-lg-6 col-lg-offset-3">
<form action="{% url 'chartboard:chart_url' %}" method="post" class="form">
{% csrf_token %}
{% bootstrap_form form layout='inline' %}
{% buttons %}
<button type="submit" class="btn btn-primary">
{% bootstrap_icon "star" %} Submit
</button>
{% endbuttons %}
</form>
</div>
</div>
Here is the output:
And here it is when removing layout='inline'
Datepicking is done by adding a custom class upon form creation with form widgets and using a jquery datepicker (if that is of any relevance at all)
Why are field labels being hidden in the first case?
Why isn't the entire form displayed in a single line when opting for inline display?
Working with python 3.4 and the following venv:
Django==1.8.11
django-bootstrap3==7.0.1
flake8==2.5.4
mccabe==0.4.0
numexpr==2.5
numpy==1.10.4
pep8==1.7.0
pyflakes==1.0.0
requests==2.9.1
tables==3.2.2
You need to change the class of your <form> element to class="form-inline" and it should work. It doesn't seem to appear in the docs but check the example inline form on GitHub.

Page stall when passing data

I am trying to pass some data between pages, but it's not working. Any tips? I click submit and it takes me to a blank page. If I refresh then it shows my base template styles, but no data is passed.
index.html
{% extends "polls/base.html" %}
{% block title %}Vote{% endblock %}
{% block content %}
<h1>Welcome</h1>
<form action="/polls/" method="post">{% csrf_token %}
<p><label for="pin">Enter group pin:</label>
<input id="pin" type="text" name="pin" maxlength="4" />
<input type="submit" value="View Polls" /></p>
</form>
Moderator login
</p>
{% endblock %}
polls/index.html
{% extends "polls/base.html" %}
{% block title %}Recent Polls{% endblock %}
{% block content %}
{{ pin }}
{% endblock %}
polls/urls.py
url(r'^$',
ListView.as_view(
model=Poll,
template_name='polls/index.html')),
You need to write a view to handle the form submision. The view that works, is listing all your Polls. I recommend you to read the tutorial:
Basic views: https://docs.djangoproject.com/en/1.4/intro/tutorial03/
Form submission: https://docs.djangoproject.com/en/1.4/intro/tutorial04/
Basically, your form will send the data to another URL that will handle the processing of your data.
You must specify it in the action attribute:
<form action="/polls/create-poll" method="post">{% csrf_token %}
<input type='text' name='poll-name' />
<input type='submit' />
</form>
and in your views:
def create_poll(request):
poll_name = request.POST.get('poll-name')
poll = Poll.objects.create(name=poll_name)
return HttpResponse("Poll created")
I don't want to sound rude. But you should start with some HTTP and HTML tutorial. A good web programmer is the one that knows the basic stuff in detail. HTTP is a great protocol, try to learn it all the way through.