The usage of {% url ...%} vs {{ ... }} in Django templates - python

I've been practicing Djnago and for that, I was building a blog. While in the process of building it, I was faced with an error, while using the following code:
<a href="{% url 'blog_post' post.slug %}">
{{ post.title }}
</a>
While studying and doing other courses something like this would work fine. But now, it will raise this exception: NoReverseMatch.
If I use this code though, it will work just fine:
<a href="{{ post.slug }}">
{{ post.title }}
</a>
While working in different projects the first way would work fine, but this time, it doesn't.
My question is why?
Here is the code in my urls and on my views. Maybe the mistake is here, not somewhere else.
If anyone can explain why this is happening, or where I'm going wrong, it will be greatly appreciated
urls:
from django.urls import path
from . import views
app_name = 'blog'
urlpatterns = [
path('', views.blog_index, name='blog_index'),
path('<slug:post_slug>/', views.blog_post, name='blog_post'),
]
views:
from django.shortcuts import render
from .models import Post
# Create your views here.
def blog_index(request):
posts = Post.objects.order_by('-
created').filter(published=True)
data = {
'posts': posts
}
return render(request, 'blog/post_list.html', data)
def blog_post(request, post_slug):
post = Post.objects.get(slug=post_slug)
data = {
'post': post
}
return render(request, 'blog/post_detail.html', data)

try this.your problem is that you have a namespace in your urls.py then in your href you should add that namespace('blog').
<a href="{% url 'blog:blog_post' post.slug %}">
{{ post.title }}
</a>
this
<a href="{{ post.slug }}">
{{ post.title }}
</a>
this code above was working because you are located inside the home page and when you add this(href="{{ post.slug }}" in the a tag) it will just call the current url +"/<'slug:post_slug/' and this is a valid url.but this is a very bad option.use the the first option.
to learn about this refer to this https://docs.djangoproject.com/en/3.2/topics/http/urls/#url-namespaces.

In such cases, you should use:
https://docs.djangoproject.com/en/3.2/ref/models/instances/#get-absolute-url
The less logic in the templates, the better for the application.
It is difficult to write tests on logic that is contained in templates.
You should add a method to your model:
from django.urls import reverse
class Post()
...
def get_absolute_url(self):
return reverse('blog:blog_post', kwargs={'post_slug' : self.slug})
In the reverse function you need to add an application namespace.
In your case, it's a blog.
https://docs.djangoproject.com/en/3.2/topics/http/urls/#introduction
and then in template use:
{{ post.get_absolute_url }}
From documentation:
The logic here is that if you change the URL structure of your objects, even for something small like correcting a spelling error, you don’t want to have to track down every place that the URL might be created. Specify it once, in get_absolute_url() and have all your other code call that one place.

Related

Django context variable in the navigation bar - correct href assignment?

What is the correct assignment of context variables in the navigation bar? My Django example is:
in view.py:
from django.shortcuts import render
from django.http import HttpResponse
context_navigation = {
'Link1' : 'Blog',
'href1' : "{% url 'blog' %}", }
def index(request):
return render(request, 'app_about/index.html', context=context_navigation)
in urls.py:
from django.urls import path
from . import views
urlpatterns = [
path('blog/', views.index, name='blog'),
]
in templates/base.html this does work
<a class="nav-item nav-link text-light" href="{% url 'blog' %}" > {{Link1}}</a>
this does not work (see href1 and Link1)
<a class="nav-item nav-link text-light" href="{{href1}}" > {{Link1}}</a>
In the last case a wrong url is generated, something like http://127.0.0.1:8000/blog/%7B%.
What is the correct assignment of href as a context variable href1 ? Thank you for some hints!
In the second example you insert a variable href1, which is replaced by the content (the string) {% url 'blog %} when rendered. Django trys to render this string (makes it html safe, thats where the %7B.. came from, which is just the html code for {.
Option 1: resolve url in the view
You can either resolve the url using python in the view and pass the actual url as string to the template:
views.py
from django.urls import reverse
context_navigation = {
'Link1' : 'Blog',
'href1' : reverse('blog')
}
base.html
<a class="nav-item nav-link text-light" href="{{href1}}">{{Link1}}</a>
Option 2: resolve url in the template
Or you go with the first example, where {% url 'blog %} will be interpreted and executed by the template rendering engine:
context_navigation = {
'Link1' : 'Blog'
}
base.html
<a class="nav-item nav-link text-light" href="{% url 'blog' %}">{{Link1}}</a>
Option3: view passes url (name) and template resolves it (not tested)
If you need to generate the context dynamically but want to resolve the actual urls in the template you can maybe pass the url name as variable like:
context_navigation = {
'Link1': 'Blog',
'url1': 'blog',
}
base.html
<a class="nav-item nav-link text-light" href="{% url url1 %}">{{Link1}}</a>
But I'm not sure if you can use varaibles in template tags like this (just a guess, never used it this way)
Thank you for the answer #sarbot! Let's give me my experiences with them.
Option 1 looks pretty lean and is in accordance with the django manual, so I would prefer it as the solution. Unfortunately I have to struggle with the fact that runserver does not like this approach and generates a fatal Python error. For the time beeing I do not have an explanation for that.
Option 2 is the option I started with and it works. For a clean programming style I prefere not to stay with it.
Option 3 works for me, so I do not have to say more than thank you :-)

Django not serving text content (First attempt)

Summary:
The purpose of this particular Django web app is to just show some lorem ipsum text on the home page, like a blog post. Django is not serving my blog post content. I know the problem is either with my views.py or urls.py (or both).
Details:
I’ve got the data declared inside my models.py. I’ve got my views.py to instantiate the model. I migrated sqlite and successfully logged into the Admin Dashboard and entered some placeholder data.
I’m trying to get Django to serve the placeholder content that I entered into the Admin Dashboard, but instead it’s blank.
Here is my what my test case looks like: https://i.imgur.com/IuOl3G4.jpg
To describe it, you can see The Blog Post, Date, Image, and Body Text HTML heading elements parsed, but none of the content is showing.
Here is my app’s urls.py:
from django.urls import path, include
from . import views
urlpatterns = [
path('', views.mortems, name='home'),
]
I’ve tried swapping out the quotation marks for the first path() parameter with alls/landings. I’ve tried swapping the name parameter from home to mortems. I also tried using mortem (without the s). None of these changes help.
I've also tried Googling (with variations):
'body text django not showing template'
'django not showing text contents'
Which turned up (among other) SO questions and answers which kind of sound related but are completely different from my issue:
Django Message framework is not showing message in template
Why django template is not showing any output?
Here is my app’s views.py:
from django.shortcuts import redirect, render, get_object_or_404
from mortems.models import Mortem
def mortems(request):
mortem = Mortem.objects.order_by('-pub_date')
context = {'mortem':mortem}
return render(request, 'alls/landings.html', context)
For what it is worth, here are the relevant lines in my model:
class Mortem(models.Model):
title = models.CharField(max_length=161)
pub_date = models.DateTimeField()
image = models.ImageField(upload_to='media/')
body = models.TextField()
now = datetime.datetime.now()
Also, here is my template with the relevant problematic lines (42-50):
<h1> BLOG POST:</h1>
<h4>Date: {{ mortem.pub_date_preference }}</h4>
<br />
Image: <img src="{{ mortem.image.url }}" class="img-responsive center-block" style="max-height:300px;" />
<br />
<!-- Body text should go here : -->
Body Text:
<p>{{ mortem.body|safe }}</p>
The full source code is up on GitHub. Here is the 'mortems' app source code specifically.
For generous SO users with time on their hands, I guess I’m accepting pull requests. haha
I think you need to update the views.py as:
from django.shortcuts import redirect, render, get_object_or_404
from mortems.models import Mortem
def mortems(request):
mortems = Mortem.objects.all().order_by('-pub_date') # returns an iterable queryset
context = {'mortems':mortems} # using plural as it's a list like object
return render(request, 'alls/landings.html', context)
In the template code, you need to iterate over the list to display a single object one at a time. i.e
<h1> BLOG POSTs:</h1>
{% for moertm in mortems}
<h4>Date: {{ mortem.pub_date_preference }}</h4>
<br />
Image: <img src="{{ mortem.image.url }}" class="img-responsive center-block" style="max-height:300px;" />
<br />
<!-- Body text should go here : -->
Body Text:
<p>{{ mortem.body|safe }}</p>
{% endfor %}

How to render data to a {% included a.html %} template in Django

I have a rank.html which is a publicly sharing template for many other templates through {% include rank.html %} method.
This template will display the 48 hours hot news base on the click number.
Here is the view.py:
def rank(self, request):
hot_news_48h = h_mostViewed(48, News, '-pv')
return render(request, "rank.html", {
'hot_news_48h': hot_news_48h,})
h_mostViewed(48, News, '-pv') is a function,that can fetch most viewed(clicked) post within 48 hours.It works.
Here is the rank.html:
<ul>
{% for hot_view in hot_news_48h %}
<li>
<a href="{% url 'news:news_detail' hot_view.pk %}" >
<img src="{{ MEDIA_URL }}{{ hot_view.image }}" >
</a>
<a href="{% url 'news:news_detail' hot_view.pk %}">
<h6>{{ hot_view.title }}</h6>
</a>
</div>
</li>
{% endfor %}
</ul>
Here is the url.py:
path('hot_news', views.rank, name="hot_news")
The problem is,I can only get the html ,but can't receive the data.
But if I give up {% include rank.html %} method and insert the rank.html's code directly inside each template which need this function, I can get the data.
Take new_detail.html template as an example:
Here is the view.py:
def newsDetailView(request, news_pk):
news = get_object_or_404(News, id=news_pk)
all_comments = NewsComments.objects.filter(news=news)
news.comment_nums = all_comments.count()
news.save()
News.objects.filter(id=news_pk).update(pv=F('pv') + 1)
hot_news_48h = h_mostViewed(48, News, '-pv')
relative_news = News.objects.filter(tag__id__in=news.tag.all()).exclude(id=news_pk)[:6]
return render(request, "news_detail.html", {
'news': news,
'all_comments': all_comments,
'hot_news_48h': hot_news_48h,
'relative_news': relative_news
})
Here is the urls.py:
path('-<int:news_pk>', views.newsDetailView, name="news_detail"),
So above,I directly inserted rank.html's code into new_detail.html and it works I can get the data.
My question is what should I do or correct,so that I can get the data in {% include rank.html %} method. Because {% include rank.html %} is simple and flexible.I don't want to repeat the same code in several same template.
Thank you so much for your patience!
How about this:
- Create a folder "templatetags" in your application and add a file "news_tags.py" or name it what you want. Then you can define the tags you need:
from django.template import Library
from your_app.models import your_model
register = Library()
#register.inclusion_tag('your_app/your_template.html')
def hot_news(num, order):
objects = News.objects.order_by(order)[:num]
result['objects'] = objects
return result
In your templates you then do the following:
{% load news_tags %}
{% hot_news 48 '-pv' %}
Then create a template as your already did and reference it in the inclusion tag. Then it should work properly.
If you want it to work for multiple models you can have a look at this: https://docs.djangoproject.com/el/2.1/ref/applications/
The apps framework allows you to fetch models from a string input.
I finally solved the issue by Creating custom context processor.https://www.youtube.com/watch?v=QTgkGBjjVYM

Django html redirects but doesn't load page

I am creating a Django web application and have come across the following problem.
I have created a new html page called teamList.html, which when clicking a href hyperlink on the main page, should redirect to teamList page. The url in the browser, which starts as http://127.0.0.1:8000/ changes to http://127.0.0.1:8000/teamList, but the page underneath doesn't change, but instead reloads the starting page.
The current application handles the login and main page in the html as such (default after login displays a graph):
#index.html
<body>
<img src="{% static "myApp/images/logo.gif" %}" alt="logo" />
{% if user.is_authenticated %}
<p>currently logged in as: {{ user.first_name }} {{ user.last_name }}
<p>logout
<p>Team List
<div id="radarChart">
{% block radarChartBlock %}{% endblock %}
</div>
{% else%}
<div id="login">
{% block login %}{% endblock %}
</div>
{% endif %}
</body>
My urls.py looks like this:
from django.conf.urls import patterns, url
from myApp import views
urlpatterns = patterns('',
#http://localhost:8000/
url(r'^$', views.index, name='index'),
url(r'^/teamList/$', views.renderTeamList, name='teamList')
)
EDIT: My views.py method for my teamList.html looks like this:
from django.shortcuts import render
from django.views.generic.base import TemplateView
from django.contrib.auth.models import User
from django.contrib.auth.decorators import login_required
from myApp.models import FocusArea
from myApp.tables import TeamTable
from django_tables2 import RequestConfig
def renderTeamList(request):
table = TeamTable()
RequestConfig(request).configure(table)
return render(request, 'teamList.html', {'table': table})
In addition to the comments there is a problem with your urls.py. Your regular expression for teamList url starts with / and therefore will not match http://127.0.0.1:8000/teamList as Django replaces a leading slash by default. From the docs:
There’s no need to add a leading slash, because every URL has that. For example, it’s ^articles, not ^/articles.
For more details see Django docs here.

How does {% load url from future %} and namespaces work in Django?

Can someone show me an example (plus a small explanation) of how {% load url from future %} and namespace concept works?
I'm new in python and django and i need to learn how not to make hardcoded urls and also how to use other functions like reverse().
Here is an example of what i'm trying to do:
urls.py
urlpatterns = patterns('',
"""
This one is what i did first but works with hardcoded url inside
top-navigator.html:
url(r'^books/$', 'books.views.book_index'),
The next one is what i'm trying to do:
(but of course is not correct)
"""
url(r'^books/$', include('books.views.book_index', namespace='books')),
)
top-navigator.html
when i'm trying to run the server is shows the error:
Caught ImportError while rendering: No module named book_index
{% load url from future %}
<div class="navbar-inner">
<ul class="nav">
<li class="active">Home</li>
<li>Books</li>
<li>Authors</li>
<li>Publishers</li>
<li>Contact</li>
</ul>
</div>
What can i do in order do to something similar for all the links?
Thanks in advance.
To use namespaces and the include statement, you must import another urls.py file with patterns in it.
You can't just include a view as you've done here.
Change your code to
{% url 'book_index' %}"
url(r'^books/$', 'books.views.book_index', name='books'))
Or to use namespaces for illustration purposes:
more_patterns = patterns('',
url(r'^$', 'book.views.book_index', name='book_index')),
)
urlpatterns = patterns('',
url(r'^books/', include(more_patterns, namespace='books')),
#^^^^^^^ note you would normally use a string that points
# to a python file containing urls.
)
{% url 'books:book_index' %} will now resolve.

Categories