Django & Jinja2 templates using {{ url() }} - django

I am trying to figure out how to pass my user_id within my html using jinja's {{ url() }}, using urls that don't need any id like /dashboard/ work fine but I need to pass an id to this- example: /user/3 . I have tried the following with no success:
{{ url('detail') }}
{{ url('detail', user_id=User.id) }}
{{ url('detail', User.id) }}
Here's part of my views and html:
views.py
urlpatterns = [
path('dashboard/', dashboard, name='dashboard'),
path('user/<int:user_id>/', detail, name='detail'),
]
dashboard.html
{% for User in all_users %}
{{ url('detail') }}
{% endfor %}
Any help on this would be appreciated, thanks

I found a solution:
{% for User in all_users %}
{{ url('detail', args=[User.id] )}}
{% endfor %}

This also works:
{% url 'detail' user.id %}
Then, in views.py, your corresponding function should receive user_id as an argument (internally user.id becomes user_id).

Django now has official backend support for Jinja2 - I'm guessing you were using django-jinja which provides an implementation of the url function but it's fairly straightforward to integrate Django and Jinja now without adding any additional dependencies (besides Jinja2).
First, configure your TEMPLATES in your settings file and add a dictionary entry with the jinja2 backend:
TEMPLATES = [
{
'BACKEND': 'django.template.backends.jinja2.Jinja2',
'APP_DIRS': True,
'OPTIONS': {
'extensions': [<custom extensions if any>]
'environment': 'yourapp.jinja2.environment',
...
},
},
...
]
The key part of this is the environment setting. The examples suggest creating a jinja2.py file in your app directory and defining an environment function there that you'll set to a function that returns a Jinja2 Environment, e.g.,
# example yourapp/jinja2.py
from django.conf import settings
from django.urls import reverse
from django.templatetags.static import static
from jinja2 import Environment
def environment(**options):
env = Environment(**options)
env.globals.update({
'static': static,
'url': reverse,
'settings': settings,
...
})
return env
Here we are binding Python functions (and Django things) to be made available in our Jinja2 environment, so now static in a jinja template can be called static(...) and invoke the Django static function defined in django.templatetags.static, and url is bound to the Django reverse function so url('detail', args=[user.id]) should work. If you prefer a signature like {{ url('detail', pk=123) }} without generating an invalid arguments error you can define your own function:
def jinja_url(viewname, *args, **kwargs):
return reverse(viewname, args=args, kwargs=kwargs)
and bind 'url': jinja_url in your TEMPLATES settings instead.
It's also important to note that Django looks for your jinja templates in a jinja2 directory under your app dir, not the templates directory.

Related

python-social-auth template url to jinja template url

How can i convert this django url tag
{% url "social:begin" "github" %}
to the proper jinja url tag? I have already tried
{% set myurl=url("social:begin", "github") %}
and then use the {{url}}
but i get an
ImportError at /login/
No module named github
My settings.py:
UTHENTICATION_BACKENDS = ('social.backends.github.GithubOAuth2',)
SOCIAL_AUTH_GITHUB_KEY = '75ba4983720f9852c22a'
SOCIAL_AUTH_GITHUB_SECRET = '7e43083e9ee92bd95ad195064f3aaa91704cbfe0'
INSTALLED_APPS = [
'social.apps.django_app.default',
]
and my urls.py:
.....
i18n_urls = [
url(r'^$', 'zerver.views.home'),
url('social-auth/', include('social.apps.django_app.urls', namespace='social')),
.....
At first, you have to add reverse() to jinja enviroment as described here.
So, after that you can use in your template:
{{ url('social:begin', args=['github']) }}

django crispy forms with jinja2

I want to use Jinja2 and Django crispy forms together, but when I load crispy_forms_tags in my template and trying to render form with this template tag, I get an error:
Encountered unknown tag 'crispy'. Jinja was looking for the following tags: 'endblock'. The innermost block that needs to be closed is 'block'.
My template code:
{% extends 'base.html' %}
{% load crispy_forms_tags %}
{% block content %}
<div class="panel-heading"><h3>Registration</h3></div>
{% crispy form %}
{% endblock %}
How can I use this apps together?
Better solution:
templatetags.py
from crispy_forms.utils import render_crispy_form
from django_jinja import library
from jinja2 import contextfunction
#contextfunction
#library.global_function
def crispy(context, form):
return render_crispy_form(form, context=context)
In template:
{{ crispy(form) }}
Well, Geoffrey R.'s answer is a good approach, but the wrong method of jinja2 is used. Instead of render_crispy_form, it should be as_crispy_form. So, from crispy_forms.templatetags.crispy_forms_filters import as_crispy_form should be written at the beginning.
The corrected example jinja2.py file is as follows.
from crispy_forms.templatetags.crispy_forms_filters import as_crispy_form # this line is different
from django.contrib import messages
from django.contrib.staticfiles.storage import staticfiles_storage
from django.urls import reverse
from django.utils import translation
from jinja2 import Environment
def environment(**options):
env = Environment(
extensions=["jinja2.ext.i18n", "jinja2.ext.with_"], **options
)
env.globals.update(
{
"get_messages": messages.get_messages,
"static": staticfiles_storage.url,
"crispy": as_crispy_form, # this line is different
"url": reverse,
}
)
env.install_gettext_translations(translation)
return env
Other steps are same with Geoffrey R.'s answer.
It works for me, with the following pip packages:
Django==3.1.2
django-crispy-forms==1.9.2
Jinja2==2.11.2
By the way, I'm not using the django-jinja pip package.
Thanks Geoffrey R. for his great idea.
I found an easy, but not completed way to "hack in".
use 'django-jinja' package to register new filters;
in the filter file, define a crispy filter, which looks like:
from crispy_forms.templatetags.crispy_forms_filters import as_crispy_form
def crispy(form):
return as_crispy_form(form, 'Bootstrap3', form.helper.label_class, form.helper.field_class)
In the form Jinja template, I have to write more code than direct crispy tag in django template:
<form id='id_form' class="form-horizontal" method='post'>
{% csrf_token %}
{{form.media}}
{{ form|crispy() }}
<div>
<input type="submit" name="submit" value="Submit" class="btn btn-primary" id="submit-id-submit">
</div>
</form>
If anyone finds a more effective way to crispy forms, please let me know.
I have been struggling with this issue of yours and the given answer, although they might be relevant a few years ago, did not satisfy me.
So I went myself building a hack to make crispy forms work with Django 3.0.2 (I did not test the others versions, please let me know guys if there is any issue on any other version).
Install the relevant packages to make crispy forms work with Jinja2 :
pip install django-crispy-forms django-jinja
In your project directory (and NOT your application directory), make sure you have a jinja2.py file with:
from crispy_forms.utils import render_crispy_form
from django.contrib import messages
from django.contrib.staticfiles.storage import staticfiles_storage
from django.urls import reverse
from django.utils import translation
from jinja2 import Environment
def environment(**options):
env = Environment(
extensions=["jinja2.ext.i18n", "jinja2.ext.with_"], **options
)
env.globals.update(
{
"get_messages": messages.get_messages,
"static": staticfiles_storage.url,
"crispy": render_crispy_form, # THIS LINE IS RELEVANT
"url": reverse,
}
)
env.install_gettext_translations(translation)
return env
Make sure that your jinja2 template engine loads this option in your django's settings:
TEMPLATES = [
{
"BACKEND": "django.template.backends.jinja2.Jinja2",
"DIRS": [os.path.join(BASE_DIR, "jinja2")],
"APP_DIRS": True,
"OPTIONS": {"environment": "your_django_project.jinja2.environment"},
},
# ...
Note the "crispy": render_crispy_form, which will help you use crispy forms in your jinja2 templates.
Then, in your template, you can use:
{{ crispy(form) }}

NoReverseMatch in render_to_response with django-social-auth

I would like to make just a page that has link to login to twitter/facebook/google with django-social-auth.
but I get a error NoReverseMatch: Reverse for '' with arguments '(u'twitter',)' and keyword arguments '{}' not found.
def index(request):
ctx = {}
return render_to_response('index_before_login.html', {}, RequestContext(request))
index_before_login.html is following
<li>Enter using Twitter</li>
urls.py is following
urlpatterns = patterns('',
url(r'^$', 'lebabcartoon.views.index'),
#url(r'^socialauth_', 'lebabcartoon.views.index'),
url('', include('social_auth.urls')),
my environment is
Django ver1.5
Python Version: 2.7.3
django-social-auth: 0.7.5
anyideas?
Wrap url name in quotes
{% url 'socialauth_begin' 'twitter' %}
To save someone using the new python-social-auth and django > 1.4
Use this :
{% url 'social:begin' 'twitter' %}
I had a similar problem, try adding the following to the url.py file in your project.
url(r'auth/', include('social_auth.urls'))
And also make sure your url parameters are wrapped in quotes like so.
{% url "socialauth_begin" "twitter" %}

Adapt a view if an app is installed with Django

I have a web app with a project that works alone (it's index, login.. pages).
I would need to change the index page if a new app is installed (e.g: add a link, a table in the template with my app models..). Have it dynamic.
The removal of the app must let the project intact and just remove the link.
How can I do that? Is it possible?
You can use the Django's application registry:
In [1]: from django.apps import apps
In [2]: apps.is_installed("django.contrib.admin")
Out[2]: True
An application can actually be enabled by using a dotted Python path to either its package or the application's configuration class (preferred). Simply checking if "app_name" is in settings.INSTALLED_APPS will fail in the latter case.
def my_view(request):
from django.conf import settings
app_installed = 'app_name' in settings.INSTALLED_APPS
return render_to_response(template_name, {'app_installed': app_installed})
template:
{% if app_installed %}
...
{% endif %}
Or use a custom context processor.
In installed_apps.py
from django.conf import settings
def installed_apps(request):
return {
'app_installed' : 'app_name' in settings.INSTALLED_APPS
}
In settings.py:
TEMPLATE_CONTEXT_PROCESSORS = (
...
'installed_apps.installed_apps'
)
simple_tag version:
The tricky part is that you can't pass arguments to your simple_tag when it's used in a conditional. Therefor, you create a new variable specifically for your installed app with as is_myapp_installed.
In templatetags/my_filters.py:
from django import template
register = template.Library()
#register.simple_tag
def is_app_installed(app):
from django.apps import apps
return apps.is_installed(app)
In template:
{% load my_filters %}
...
{% is_app_installed "myapp" as is_myapp_installed %}
{% if is_myapp_installed %}
...
{% endif %}

Django reset_password_confirm TemplateSyntaxError problem

when I use django.contrib.auth.views.password_reset_confirm without arguments at all it works and I can render the template without any problem, when adding uidb36 and token arguments it fails.
Caught NoReverseMatch while rendering: Reverse for 'django.contrib.auth.views.password_reset_confirm' with arguments '()' and keyword arguments '{'uidb36': '111', 'token': '1111111111111'}' not found.
Most likely it is an issue with your urls.py. You need to setup the right pattern to grab the uidb36 and token values passed as URL parameters. If not, it will throw a similar error to what you see above.
Something like:
(r'^reset/(?P<uidb36>[0-9A-Za-z]+)-(?P<token>.+)/$', 'django.contrib.auth.views.password_reset_confirm', {'template_name' : 'registration/password_reset.html', 'post_reset_redirect': '/logout/' })
registration/password_reset.html - is my custom template
logout - is my custom logout action
I had this issue in Django 1.3, and wasted a lot of time because the error can mask a number of underlying issues.
I needed to add this to the top of the reset email template:
{% load url from future %}
Also, the example in the Django docs didn't match the sample url:
{{ protocol}}://{{ domain }}{% url 'auth_password_reset_confirm' uidb36=uid token=token %}
So I had to change the auth_password_reset_confirm above to password_reset_confirm.
If you're using Django 1.6+ and run into something like this it could be that you need to update uidb36 to uidb64 in both your template and your urls.
Example url:
url(r'^password/reset/confirm/(?P<uidb64>[0-9A-Za-z]+)-(?P<token>.+)/$',
auth_views.password_reset_confirm
and reset link in template:
{{ protocol}}://{{ domain }}{% url 'django.contrib.auth.views.password_reset_confirm' uidb64=uid token=token %}
For Django 1.8+ users, just copy this URL to your main urls.py file, so that it recognizes the URL name
url(r'^reset/(?P<uidb64>[0-9A-Za-z_\-]+)/(?P<token>[0-9A-Za-z]{1,13}-[0-9A-Za-z]{1,20})/$',
'django.contrib.auth.views.password_reset_confirm',
name='password_reset_confirm'),
And add this mentioned by: #Lunulata to your password_reset_email.html file:
{{ protocol}}://{{ domain }}{% url 'django.contrib.auth.views.password_reset_confirm' uidb64=uid token=token %}
Try adding following to your urls.py
(r'^reset/(?P<uidb36>[0-9A-Za-z]{1,13})-(?P<token>[0-9A-Za-z]{1,13}-[0-9A-Za-z]{1,20})/$', 'django.contrib.auth.views.password_reset_confirm'),
I found this to work, copied from the default url
url(r'^reset/(?P<uidb64>[0-9A-Za-z_\-]+)/(?P<token>[0-9A-Za-z]{1,13}-[0-9A-Za-z]{1,20})/$',
auth_views.password_reset_confirm, name='password_reset_confirm'),
Just add this line to your urls.py:
url('^', include('django.contrib.auth.urls')),
This enables the django reset_password workflow.
Then override your login.html to include the line:
<div class="password-reset-link">
href="{{ password_reset_url }}">{% trans 'Forgotten your password or username?' %}</a></div>
Now you should be able to use the builtin Django PasswordResetView included with Django as long as your email settings are set up.
if you are using app_name in every urls.py
suppose you have an app and in that app in urls.py you have mentioned app_name="accounts"
in order to retrieve the page you need to mention two things
template_name and success_url inside the PasswordResetView(template_name="accounts/password_reset.html" , success_url= reverse_lazy('accounts:password_reset_sent'))
dont forget to import reverse_lazy from django.urls inside urls.py
so your final code of accounts/urls.py should look like
My app name is landing instead of accounts
from django.urls import path
from . import views
from django.contrib.auth import views as auth_views
from django.urls import reverse_lazy
app_name='landing'
urlpatterns = [
path('',views.home,name="home"),
path('terms/',views.terms,name="terms"),
path('login/',views.loginUser,name="login"),
path('signup/',views.signupUser,name="signup"),
path('about/',views.about,name="about"),
path('logout/',views.logoutUser,name="logout"),
path('password_reset/',
auth_views.PasswordResetView.as_view(template_name='landing/password_reset.html',success_url=reverse_lazy('landing:password_reset_done')),
name="password_reset"),
path('password_reset_sent/',
auth_views.PasswordResetDoneView.as_view(template_name='landing/password_reset_sent.html'),
name="password_reset_done"),
path('reset/<uidb64>/<token>/',
auth_views.PasswordResetConfirmView.as_view(template_name='landing/password_reset_form.html',success_url=reverse_lazy('landing:password_reset_complete')),
name="password_reset_confirm"),
path('password_reset_complete/',
auth_views.PasswordResetCompleteView.as_view(template_name='landing/password_reset_done.html'),
name="password_reset_complete"),
]
you have to use app_name: before the name of url you have mentioned it is very important