I had another post related to this but my code was complex so the communication was difficult. I completely stripped off the issue from the unnecessary details and came up with a very minimal example shown below:
templates/base.html:
<html>
<body>
<p>I am base.html</p>
{% block content %}
{% endblock content %}
</body>
</html>
templates/blog.html:
{% extends 'base.html' %}
{% block content %}
<p>I am blog.html</p>
{{ blog_list }}
{% for blog in blog_list %}
{{ blog.title }}
{% endfor %}
{% endblock %}
And blog/views.py
from django.views import generic
from .models import Blog
class BlogList(generic.ListView):
queryset = Blog.objects.filter()
template_name = 'blog.html'
# context_object_name = 'blog_list'
class BlogDetail(generic.DetailView):
model = Blog
template_name = 'blog.html'
That outputs this:
I was expecting the list of blog titles to be rendered there. I debugged the BlogList class and queryset value was this:
<QuerySet [<Blog: First Page>, <Blog: Intro>]>
So, the query is not empty. I have been completely frustrated by this issue. Anyone know what's going on?
Edit: Here is blog/urls.py:
urlpatterns = [
path('<str:parent>/<slug:slug>/', views.BlogDetail.as_view(), name='blog'),
]
And project urls.py:
urlpatterns = [
path('admin/', admin.site.urls),
path('', include('qa.urls')),
path('blog/', include('blog.urls')),
]
After some clues given by #Abdul Aziz Barkat, I realized the problem is I should be linking to the BlogDetail view instead of BlogList view. So, I changed urlpatterns of blog/urls.py to:
urlpatterns = [
path('<str:parent>/<slug:slug>/', views.BlogDetail.as_view(), name='blog_list'),
]
The problem now is BlogList is not being rendered anymore, but we don't need BlogList anymore - we can create a queryset inside BlogDetail and provide the queryset as a context. To achieve that I added a queryset variable and a get_context_data method to BlogDetail:
class BlogDetail(generic.DetailView):
model = Blog
template_name = 'blog.html'
queryset = Blog.objects.filter()
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['blog_list'] = self.queryset
return context
Related
I've followed the tutorial here to implement a basic search function: https://learndjango.com/tutorials/django-search-tutorial
I'd like to extend that tutorial by making the search function visible on the results page, allowing for repeated search. However, when I do this I can't get the search form to show up on the search results page. The search button shows up, but not the field to provide input.
Relevant code:
home.html:
<div name="searchform">
<form action="{% url 'search_results' %}" method="get">
{{ form }}
<input type="submit" value="Search">
</form>
</div>
{% block content %}
{% endblock %}
search_results.html:
{% extends home.html}
{% block content %}
<h1>Search Results</h1>
<ul>
{% for city in object_list %}
<li>
{{ city.name }}, {{ city.state }}
</li>
{% endfor %}
</ul>
{% endblock %}
Views.py:
from django.db.models import Q
from django.views.generic import TemplateView, ListView, FormView
from .models import City
class HomePageView(FormView):
template_name = 'home.html'
form_class = SearchForm
class SearchResultsView(ListView):
model = City
template_name = 'search_results.html'
def get_queryset(self):
query = self.request.GET.get('q')
object_list = City.objects.filter(
Q(name__icontains=query) | Q(state__icontains=query)
)
return object_list
urls.py:
from django.urls import path
from .views import HomePageView, SearchResultsView
urlpatterns = [
path('search/', SearchResultsView.as_view(), name='search_results'),
path('', HomePageView.as_view(), name='home'),
]
forms.py:
from django import forms
class SearchForm(forms.Form):
q = forms.CharField(label='', max_length=50,
widget=forms.TextInput(attrs={'placeholder': 'Search Here'})
)
Any advice on how I might troubleshoot this sort of issue (or if I'm blatantly doing something un-django-y) would be greatly appreciated.
You're using ListView which is a Generic display view.
You need to use get method, then you can pass the form to make the search again and stay on the same page.
class SearchResultsView(View):
template_name = 'search_results.html'
form_class = SearchForm
def get(self, request):
form = self.form_class()
query = self.request.GET.get('q')
context = {}
context['form'] = form
context['cities'] = City.objects.filter(
Q(name__icontains=query) | Q(state__icontains=query)
)
return render(self.request, self.template_name, context)
You can achieve the same result with ListView but is better if you use other based view class.
You can check the doc. here
class HomePageView(FormView):
template_name = 'home.html'
form_class = SearchForm # This line!
Remember to also apply the form_class attribute to SearchResultsView, otherwise, no forms will be interpreted. The submit button only shows up because it's not a part of the rendered form.
I'm learning Django and trying to setup some dynamic pages. I've mapped url's to /restaurants and /restaurants/mexican however my html content block is not displaying anything from my queryset. Code below:
views.py
def restaurantListView(request):
template_name = 'restaurants/restaurants_list.html'
queryset = Restaurant.objects.all()
context = {
"objectList": queryset
}
return render(request, template_name, context)
class RestaurantListView(ListView):
queryset = Restaurant.objects.all()
template_name = 'restaurants/restaurants_list.html'
class MexicanRestaurantListView(ListView):
queryset = Restaurant.objects.filter(category__iexact='mexican')
template_name = 'restaurants/restaurants_list.html'
class AsianFusionRestaurantListView(ListView):
queryset = Restaurant.objects.filter(category__iexact='asian fusion')
template_name = 'restaurants/restaurants_list.html'
urls.py
from restaurants.views import (
restaurantListView,
RestaurantListView,
MexicanRestaurantListView,
AsianFusionRestaurantListView,
)
urlpatterns = [
path('admin/', admin.site.urls),
path('', TemplateView.as_view(template_name='home.html')),
path('restaurants/', RestaurantListView.as_view()),
path('restaurants/mexican/', MexicanRestaurantListView.as_view()),
path('restaurants/asian/', AsianFusionRestaurantListView.as_view()),
path('about/', TemplateView.as_view(template_name='about.html')),
path('contact/', TemplateView.as_view(template_name='contact.html')),
]
restaurants_list.html
{% extends "base.html" %}
{% block head_title %} Restaurants || {{ block.super }} {% endblock %}
{% block content %}
<h1>Restaurant List</h1>
<ul>
{% for obj in objectList %}
<li>{{ obj.name }} | {{ obj.location }}</li>
{% endfor %}
</ul>
{% endblock content %}
I expected the items of Restaurant.objects.all() to be displayed in my content block on restaurants.html, but instead nothing is displaying. Same occurs for objects.filter() on the /restaurants/mexican route.
The template variable should be object_list, not objectList.
(Note, you certainly don't need one view per restaurant type. Rather, have a view for RestaurantsByType and get the type as a URL parameter.)
'''
Just add one extra "_" in the 'category__iexact'
'''
class MexicanRestauratListView(ListView):
queryset = RestaurantLocation.objects.filter(category__iexact='mexican')
template_name = "restaurants/restaurantlocation_list.html"
class AsianFusionRestauratListView(ListView):
queryset = RestaurantLocation.objects.filter(category__iexact='asian fusion')
template_name = "restaurants/restaurantlocation_list.html"
I'm struggling to show on a webpage some model objects I created from the admin pages on my site.
I combed through the relevant django tutorial page but nothing I've tried works to show the model objects on the page.
Here is my models.py:
from django.db import models
from datetime import datetime
from datetime import timedelta
# Create your models here.
def get_deadline():
return datetime.today() + timedelta(days=3)
class JobPost(models.Model):
created_at = models.DateTimeField(auto_now_add=True, blank=True)
deadline = models.DateField(default=get_deadline)
wordcount = models.IntegerField()
jobtaken = models.BooleanField(default=False)
# client = models.User.username
class Meta:
ordering = (
# ("jobtaken"),
("-created_at"),
)
def publish(self):
self.pub_date = timezone.now()
self.save()
def __str__(self):
return "Job #{}".format(self.pk)`
views.py:
from django.shortcuts import render
from django.views.generic import TemplateView
#from django.contrib.auth.decorators import staff_member_required
from .models import JobPost
from django.utils import timezone
# Create your views here.
# #staff_member_required()
# class JobBoardView(TemplateView):
# template_name = "jobs.html"
# posts = JobPost.objects.filter(published_date__lte=timezone.now()).order_by('pub_date')
#changed published_date to pub_date in .models
def jobs(request):
#posts = JobPost.objects.filter(published_date__lte=timezone.now()).order_by('published_date')
latest_post_list = JobPost.objects.order_by('-pub_date')
context = {
'deadline': deadline,
'created_at': created_at,
'wordcount':wordcount,
'jobtaken':jobtaken,
'JobPost':JobPost,
'latest_post_list':latest_post_list,
}
return render(request, 'jobs.html', context=context)
urls.py:
from django.contrib import admin
from django.urls import path, re_path, include
from django.conf.urls import url
from django.contrib.auth import views as auth_views
from jobboard import views
from login import views
urlpatterns = [
path('admin/', admin.site.urls),
#re_path(r'^login/$', auth_views.login(template_name = 'accounts/login.html'), name='login'),
re_path(r'^signup/$', views.signup, name='signup'),
path('login/', include('login.urls')),
path('', views.index, name='index'),
path('accounts/', include('django.contrib.auth.urls')),
path('users/', include('users.urls')),
path('users/', include('django.contrib.auth.urls')),
path('jobs/', views.jobs, name='jobs')
]
Here is my template(jobs.html):
{% extends "base.html" %}
{% block content %}
{% if latest_post_list %}
<ul>
{% for post in latest_post_list %}
<li>{{ JobPost }}</li>
{% endfor %}
</ul>
{% else %}
<p>No posts are available.</p>
{% endif %}
{% endblock content %}
I tried various templates, including some really simple ones to test, e.g.
{% if latest_post_list %}
<p>hello</p>
{% else %}
<p>No posts are available.</p>
{% endif %}
but everything I try returns the 'else' part of the if statement. Nothing will show the objects on the webpage.
You have a made a few mistakes which I will list below:
Your query method is wrong. It should be:
latest_post_list = JobPost.objects.all().order_by('-pub_date')
or
latest_post_list = JobPost.objects.filter(something=something).order_by('-pub_date')
2. latest_post_list is not an object, it is a query of JobPost objects. If you wish to iterate them in the template, you can just pass it to the template and do the iteration from the query. However,
context = {
'deadline': deadline, # you cannot access deadline (which deadline?)
'created_at': created_at, # same as above
'wordcount':wordcount, # same as above
'jobtaken':jobtaken, # same as above
'JobPost':JobPost, # You are sending the class to the template which is wrong
'latest_post_list':latest_post_list, # the only thing you need!
}
If you wish to loop through the objects of the query in the back-end, you have to do the following:
for post in latest_post_list:
print(post.deadline) # will print the deadline of each JobPost one by one
If you wish to loop through the objects of the query and display them in the template, you have to do this:
{% for post in latest_post_list %}
<p>{{ post.deadline }} - {{ post.created_at }}</p>
{% empty %}
<p>There is no post yet!</p>
{% endfor %}
There is no need to user if in the backend or use {{ JobPost }}. if the query is empty, the {% empty %} will be effective.
RoboBear was correct, it was a name collision in the urls.py file. This fixed the error:
change to from jobboard import views as job_views and change the url line to path('jobs/', job_views.jobs, name='jobs').
Changing this raised exceptions in the views.py file for all the context dictionary entries except for
context = {
'latest_post_list':latest_post_list,
}
as Ramtin suggested.
Then I changed the template to
<ul>
{% for post in latest_post_list %}
<li>{{ post }}</li>
</ul>
{% empty %}
<p>No posts are available.</p>
{% endfor %}
as Ramtin suggested.
I using Django with widgets to get some user input but I can't seem to get it to display even thou the code seems almost identical to examples online.
forms.py
from django import forms
class PickerIDForm(forms.Form):
pickeID = forms.NumberInput()
views.py
def get_id(request):
template = loader.get_template('metrics/lance1.html')
def get(self, request):
form = PickerIDForm()
return render(request, 'self.template', {'form': form})
context ={
}
return HttpResponse(template.render(context,request))
urls.py
from django.urls import path
from . import views
from . import mark_views
app_name = 'metrics'
urlpatterns = [
path('', views.index, name='index'),
path('test/', views.get_id, name='get_id'),
]
test.html
{% extends 'base.html' %}
{% block content %}
<p>User Input</p>
<form method = "post" >
{% csrf_token %}
{{form.as_p}}
<button type="submit"> Submit </button>
</form>
{% endblock %}
I'm never directly calling the get function as defined in views.py that to me seems to be a possible source of the input fields not showing up when I load up test.html
At what point do you link the disparate parts together? Because it seems I'm missing something.
You have defined the widget instead of the field in your form.
To fix that replace pickeID = forms.NumberInput() with pickeID = forms.IntegerField()
And also write your view like this:
def get_id(request):
form = PickerIDForm()
return render(request, 'metrics/lance1.html', {'form': form})
I'm creating a website which has two list views and a details view. I have no problem getting from the first listview to the second, but I'm unsure of how to make the url for the details view
from django.conf.urls import url
from . import views
urlpatterns = [
url(r'^$', views.IndexView.as_view(), name='index'),
url(r'^(?P<pk>[0-9]+)/$', views.ItemsView.as_view(), name='items'),
url(r'^(?P<category_id>[0-9]+)/(?P<products_id>[0-9]+)/$', views.DetailView.as_view(), name='details'),
]
im not sure what to replace "category_id" and "products_id" with on the bottom url
from django.http import Http404
from django.shortcuts import render
from django.views import generic
from .models import Category, Products
class IndexView(generic.ListView):
template_name = 'products/index.html'
context_object_name = 'all_categories'
def get_queryset(self):
return Category.objects.all()
class ItemsView(generic.ListView):
template_name = 'products/items.html'
context_object_name = 'all_items'
def get_queryset(self):
return Products.objects.all()
class DetailView(generic.DetailView):
model = Products
template_name = 'products/details.html'
html:
{% extends 'products/base.html' %}
{%block title%}Home{%endblock%}
{% block body %}
{% if all_items %}
<ul>
{% for product in all_items %}
<li>{{product.name}} </li>
{% endfor %}
</ul>
{% else %}
<h3>You have no categories</h3>
{% endif %}
{% endblock %}
also what would go in the url here where the question marks are
thanks
Use name of your url, define in your url.py.
Check the documentation
<li>{{ product.name }}</li>
Why do you need category in your url ?? You can access to your product like this. And your DetailView need only the primary key of the object.
url(r'^product/(?P<products_id>[0-9]+)/$', views.DetailView.as_view(), name='details'),