I know some people are going to think this is a duplicate, but I have spent all afternoon on Stack Overflow looking for the answer..
I have a page with a list of items, with, for each of them, a button which I would like to trigger a small update in the database. I don't know if this has any bearing, but the html file containing the button is included:
{% include "accueil/impasse.html" with l=l only %}
the file "impasse.html" itself is:
<a {% if l.impasse %} class="impasse" {% endif %} href="{% url "index" %}lecon/{{l.numero}}"> {{l.numero}} : {{l.titre}} </a>
{% if l.impasse %}
{% include "accueil/boutonDesImpasse.html" %}
{% else %}
{% include "accueil/boutonImpasse.html" %}
{% endif %}
and the file "accueil/boutonImpasse" which is the one that triggers the error is:
<form action="{% url "index" %}lecon/{{l.numero}}/impasse" method="post">
{% csrf_token %}
<div class="form-actions">
<button type="submit" class="btn btn-primary">Je fais l'impasse sur cette leçon</button>
</div>
</form>
The corresponding view is:
#login_required()
def impasse(request,numero):
id = numeroAId(numero)
if id==None:
return HttpResponse("Erreur, cette leçon n'existe pas.")
else:
imp = Impasse(user=request.user,lecon=id)
imp.save()
return HttpResponseRedirect('/lecon',context_instance=RequestContext(context))
Now, at this point, I have a CSRF error (in the else branch). I know that many people with the same type of error were advised to use render_to_response with the corresponding template, but I can't, because the view "lecon" associated with the url "lecon" does complicated things before throwing the page, so what I really want is to load the view "lecon". Note that after I get the error, if I try again the url that failed, it loads as wanted. What should I do?
This has nothing to do with render_to_response, or the redirect.
You are specifically excluding the csrf token variable from the context of the excluded templates by using "only" in the include tag. Don't do that.
(And HttpResponseRedirect doesn't take a context_instance parameter, so no idea what you are doing there.)
Related
When I tried to iterate through a list of post titles to create links in a django template the urls never match the requested path (matching the current web page should alter the formatting on the page for that list item). When I try and change things, Django spits out a 404 error which doesn't let me diagnose the problem or it "works" but not as expected.
the relevant url patterns (in blog app within project):
path('', views.index, name='blog_index'),
path('<slug:post>', views.blog_post, name='entries')
the relevant views functions (in blog app within project):
ef index(request):
try:
return render(request, 'blog/index.html', {'posts':blog_posts.keys})
except:
raise Http404()
def blog_post(request, post):
try:
return render(request, 'blog/post.html', {
'post_title':post,
'post_content':blog_posts[],
'posts':blog_posts.keys
})
except:
raise Http404()
the navigation template:
<ul>
{% for post in posts %}
{% url "blog:entries" "{{post}}" as blog_entry %}
<li><a href="/blog/{{post}}" {% if request.path == blog_entry %} class="active"{% endif %}>
<span class="item">{{post}}</span>
</a></li>
{% endfor %}
</ul>
I suspect the {% url "blog:entries" "{{post}}" as blog_entry %} in the template is not resolving correctly as when I replace the href with {% blog_entry %} it causes a 404 error.
I have tried hard coding it in, I have tried different ways of writing the url, I have tried changing the syntax (in case I am doing it incorrectly. I have checked the page source when I have run tests. I have never been able to get the class attribute to be "active" when on the matching page.
EDIT: The links all go to the correct pages, however the IF logic in the template does not work. i.e. None of the changes I have tried make result in reques.path == blog_entry resolving as True.
Also the above code snippets are part of an App within a project and I have included more of the views.py and urls.py file. Currently I have the "blog posts" stored in a dictionary at the top of views.py for testing.
Try this:
<ul>
{% for post in posts %}
{% url "blog:entries" post=post as blog_entry %}
<li><a href="/blog/{{post}}" {% if request.path == blog_entry %} class="active"{% endif %}>
<span class="item">{{post}}</span>
</a></li>
{% endfor %}
</ul>
I'm new to Django and I'm trying to develop an apllication that deals with learning objects metadata. One of the functions of the system is to view the L.O. metadata in browser.
I have an HTML template that lists the result of the query from the database. Each result come along with a "Visualize Metadata" button, that when clicked, should display the metadata of that object in browser. So I want my button to pass the object ID back to my view, so i can make another query by the specific ID and print the results on the screen.
This is my template .html
{% if objects %}
<ul>
{% for object in objects %}
<li>
{{ object.General.title }}
<form action='visualize' method='POST' name='id' value="{{object.General.id}}">
{% csrf_token %}
<button type="submit" >Visualize Metadata </button>
</form>
</li>
{% endfor %}
</ul>
{% else %}
<p>No results found.</p>
{% endif %}
And this is my views.py function
def visualize_lom_metadata(request):
if request.method == 'POST':
objID = request.POST.get('id')
return HttpResponse(objID)
For now i just want to see if that's possible by printing the objID in the screen. But when I try to do that,it just returns "None". Anyone knows how to retrieve data from template.html to my Django views.py?
I believe that all your forms should have different names and ids, and that submit button must be bound to that form.
It would also help if you in your visualize_lom_data would print entire request.POST to see what you get back from template.
I have a detail view that uses a Quiz object to display data stored in that object, like title and author. I want to have a button that links to a new page that displays different data from the same object. I don't know how to pass this data/object.
I can render the view and pass it the context of a specific quiz using an id but I want the id to change to be the id of the object from the initial page.
#assessement view
def assessment(request):
context = {
'quiz':Quiz.objects.get(id=1),
}
return render(request, 'quiz_app/assessment.html', context)
#detailview template for quiz
{% extends "quiz_app/base.html" %}
{% block content %}
<article class="quiz-detail">
<h1>{{ object.title }}</h1>
<h2>{{ object.question_amount }} Questions</h2>
<a class="btn" href="{% url 'quiz-assessment' %}">Start Quiz</a>
</article>
{% endblock content %}
#assessment template
{% extends "quiz_app/base.html" %}
{% block content %}
<h2>Assessment</h2>
<h2>Title is {{ quiz.title }}</h2>
{% endblock content %}
Then you should make another view for url quiz-assessment and pass the quiz pk as you did above in your assessment view.
def quiz_assessment(request,pk):
quiz = Quiz.objects.get (pk=pk)
return render (request,'assessment_template', {'quiz':quiz}
And in your url,pass the quiz id like this:
path ('<int:pk>/quiz/assessment /',views.quiz_assessment,name='quiz_assessment')
And in your template you can give url like this:
< a class="btn" href="{% url 'quiz_assessment' object.pk %}>
As suggested in the comments by #Robin Zigmond, you can do like this.
#assessement view
def assessment(request, qid):
context = {
'quiz':Quiz.objects.get(id=qid),
}
return render(request, 'quiz_app/assessment.html', context)
In the HTML file
#detailview template for quiz
{% extends "quiz_app/base.html" %}
{% block content %}
<article class="quiz-detail">
<h1>{{ object.title }}</h1>
<h2>{{ object.question_amount }} Questions</h2>
<a class="btn" href="{% url 'quiz-assessment' qid=object.id %}">Start Quiz</a>
</article>
{% endblock content %}
and in your urls.py change as:
path('quiz_asswssment/?P<int:qid>/', views.assessment, name="quiz_assessment")
Besides, what SammyJ has suggested, You can use the django sessions library or the django cache framework. You can temporarily store the information you need for the next view and access it whenever you want to.
In what Sammy J had suggested, you will always to have make sure that the queryset is passed in the context, otherwise it will not be rendered.
def assesment(self, request, id):
q = Quiz.objects.get(pk=id)
request.session["someData"] = q.name
request.session["qAmount] = q.amount
In your template file
<p>The title is : {{request.session.title}} and the amount is {{request.session.qamount}}
Note: Django sessions do not allow you to set a queryset as a session record, for that, you can use Django Cache framework.
Example
from django.core.cache import cache
cache.set('quiz', q)
getting cache -> cache.get('quiz')
Sessions framework docs : https://docs.djangoproject.com/en/2.2/topics/http/sessions/
Cache framework docs: https://docs.djangoproject.com/en/2.2/topics/cache/
In Django, in my DB I've created string variables containing boilerplate HTML with dynamic URLs, and I can't quite get them to work in my templates.
I'm using render_as_template (https://github.com/danielrozenberg/django-render-as-template/blob/master/render_as_template/templatetags/render_as_template.py) so the dynamic URLs work. I tried custom template tags, but when I use those with render_as_template, it fails to load.
I then tried a custom context processor. I created two functions in the context processor, one for hyperlinks, and one for tooltips. I got the tooltips processor to work, but I can only reference them in the template via their number in the auto-generated dict from the queryset.
I did the same with the hyperlink processor, then tried modifying it to use string keys instead of integers, but it doesn't load all of the field. I must be missing something.
custom_tags.py
from django import template
register = template.Library()
#register.simple_tag
def rdo_hyper():
value = Boilerplate.objects.filter(name='RDO').values_list('hyperlink',flat=True)
return value[0]
# It's only going to return one field.
# Expected output: <a href="{% url 'guides:rdo' %}" target=”_blank” rel=”noopener noreferrer”>Foobar</a>
# tried a non-DB version, just in case
#register.simple_tag
def rdo_hyper2():
value = "<a href=\"{% url \'guides:rdo\' %}\" target=\”_blank\” rel=\”noopener noreferrer\”>Foobar</a>"
return value
# Expected output: <a href="{% url 'guides:rdo' %}" target=”_blank” rel=”noopener noreferrer”>Foobar</a>
custom_context.py
from myapp.apps.wizard.models import Boilerplate
def boilerplate_hyperlink_processor(request):
boilerplate_hyper = {
"foo": Boilerplate.objects.filter(name='Aftermarket').values_list('hyperlink',flat=True),
"bar": Boilerplate.objects.filter(name='Sights').values_list('hyperlink',flat=True)
}
return {'boilerplate_hyper': boilerplate_hyper}
# Expected output of boilerplate_hyper.foo:
#<a href="{% url 'guides:aftermarket' %}" target=”_blank” rel=”noopener noreferrer”>Aftermarket Support</a>
#
# Expected output of boilerplate_hyper.bar:
# <a href="{% url 'guides:sights' %}" target=”_blank” rel=”noopener noreferrer”>Sights</a>
def boilerplate_tooltip_processor(request):
boilerplate_tooltip = Boilerplate.objects.values_list('tooltip',flat=True)
return {'boilerplate_tooltip': boilerplate_tooltip}
# Expected output of boilerplate_tooltip.0:
#<sup></sup>
template.html
{% load static %}
{% load custom_tags %}
{% rdo_hyper as rdo_hyper %}
{% rdo_hyper2 as rdo_hyper2 %}
{% load render_as_template %}
...
<html>
{% autoescape off %}
1. {% rdo_hyper %}
2. {{ rdo_hyper }}
3. {% rdo_hyper2 %}
4. {{ rdo_hyper2 }}
5. {% render_as_template rdo_hyper %}
6. {{ boilerplate_hyper.foo }}
7. {% render_as_template boilerplate_hyper.foo %}
8. {% render_as_template boilerplate_tooltip.0 %}
{% endautoescape %}
{# The hyperlink value is:
<a href="{% url 'guides:aftermarket' %}" target=”_blank” rel=”noopener noreferrer”>
Aftermarket Support</a> #}
</html>
In template.html, the following occurs:
Renders, but the dynamic URL fails.
Doesn't render the variable at all. Otherwise page loads fine.
Renders, but the dynamic URL fails.
Doesn't render the variable at all. Otherwise page loads fine.
Doesn't render the variable at all. Otherwise page loads fine.
Only renders "Aftermarket Support']>" instead of the full hyperlink field from the DB.
Throws this error:
TemplateSyntaxError:
In template <unknown source>, error at line 1.
Could not parse the remainder: '\'guides:aftermarket\'' from '\'guides:aftermarket\''
1 <QuerySet ['<a href="{% url \'guides:aftermarket\' %}" target=”_blank” rel=”noopener noreferrer”>Aftermarket Support</a>']>
Works fine.
It's great that {% render_as_template boilerplate_tooltip.0 %} works, but I would much rather reference variables in templates through a string key. After all, the ethos of Django's templating language is that its templates can be read and written by non-programmers. Any ideas?
I went back to trying custom tags and this seems to work:
custom_tags.py
#register.simple_tag(takes_context=True)
def rdo_hyper2(context):
value = "<a href=\"{% url \'guides:rdo\' %}\" target=\”_blank\” rel=\”noopener noreferrer\”>Foobar</a>"
rendered = context.template.engine.from_string(value).render(context)
return rendered
template.html
{% load custom_tags %}
...
{% rdo_hyper2 %}
When including {% rdo_hyper2 %} or other custom tags inside a DB field, I also have to use {% load custom_tags %} at the top of that field every time or else it throws:
Invalid block tag on line 12: 'rdo_hyper2'. Did you forget to register or load this tag?
Hopefully that's not resource intensive!
I have been trying to figure out why my Flask form will not properly validate my select field choices even though the choices are coming from the select field options.
My assumption is that the select option when passed back from the server is unicode and is being compared to the choice which is a string, however, I thought coerce=str would fix that. I printed out the form data and request data which is the output below. Why isn't it working?
My code is attached below, removed csrf token key from the output dict. It seems like a very simple thing, but I can't figure it out.
forms.py
class PlatformForm(FlaskForm):
platform_options = [('test', 'Test'), ('test2','Test2')]
platforms = wtforms.SelectField('Platforms', choices=platform_options, coerce=str, validators=[DataRequired()])
views.py
#app.route('/', methods=['POST', 'GET'])
def index():
form = forms.PlatformForm()
if form.is_submitted():
print form.data
print request.form
if form.errors:
print form.errors
return render_template('home.html', form=form)
index.html
{% extends "base.html" %}
{% block content %}
<h4>Select a Platform</h4>
<form method="POST">
{{ form.csrf_token }}
<select class="custom-select" name="platform">
{% for value, text in form.platforms.choices %}<br>
<option value="{{ value }}">{{ text }}</option>
{% endfor %}
</select>
<button id="submit_inputs" type="submit" class="btn btn-default">Submit</button>
</form>
{% endblock %}
output
{'platforms': 'None'}
ImmutableMultiDict([('platform', u'test')])
{'platforms': [u'Not a valid choice']}
EDIT:
I figured out the problem. It's the way I'm creating the Select drop down through HTML and Jinja. Iterating through the choices and creating option tags doesn't seem to instantiate anything in the form data itself when passed back into Python. Changing that whole for loop to just
{{form.platforms}}
created a select drop down field that actually works.
You have a name mismatch. In the form, you named your select field platforms (plural). In the HTML, you use platform (singular).
I recommend that instead of manually rendering the fields in your template, you let WTForms generate the HTML for you. For the form label, you can use {{ form.platforms.label }}, and for the actual field {{ form.platforms() }}. You can pass any attributes you want to field to have as keyword arguments.
I think something might be going wrong because of the way you are rendering the form in your html file. If my hunch is right, try this:
{% extends "base.html" %}
{% block content %}
<h4>Select a Platform</h4>
<form method="POST">
{{ form.hidden_tag() }}
Select: {{ form.plaforms}}
{{ form.submit(class="btn btn-default") }}
</form>
{% endblock %}
and then try if form.validate_on_submit() in your views.py file
taken from this stack overflow answer by pjcunningham:
"validate_on_submit() is a shortcut for is_submitted() and validate().
From the source code, line 89, is_submitted() returns True if the form
submitted is an active request and the method is POST, PUT, PATCH, or
DELETE.
Generally speaking, it is used when a route can accept both GET and
POST methods and you want to validate only on a POST request."