Django generic views problems with url - django

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'),

Related

Django is not rendering ListView

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

Django Search Implementation, conditional routing

I am trying to implement basic search functionality with Django. Could use help with accessing query inputs from forms in templates in functional or class based views. Intended functionality:
If exact search query page exists, display that page
If there is a page title that contains what was queried, show results of all
If neither, display a message stating no results found
I'm just learning, so I tried functional views and class based views. I've spent a really long time on documentation/videos/textbooks and don't see how to get the intended behavior out of class-based view. I understand collecting object_list and getting query_set, but how do you then route to those three different conditions. I tried overriding dispatch() and as_views() method to no avail. Tried with a Django form class and without.
For some reason, the functional view keeps executing the first try statement instead of throwing a DoesNotExist exception when the exact match isn't found. So it shows the entry page instead of the search results page. It seems like the request.GET is None type no matter what, as when I try to print nothing shows up.
urls.py
from re import search
from django.urls import path
from . import views
from .views import IndexPageView, SearchView
app_name = "wiki"
urlpatterns = [
# ex: /wiki/
path("", views.index, name="index"),
# path("", IndexPageView.as_view(), name="index"),
# ex: /wiki/EntryPageName
path("wiki/<str:entry>/", views.displayEntry, name="displayEntry"),
# path("wiki/search/", views.searchView, name="searchView")
path("wiki/search/", SearchView.as_view(), name="searchView")
]
model
class Entry(models.Model):
title = models.CharField(max_length=64)
def __str__(self):
return f"{self.title}"
content = models.TextField()
views.py
def searchView(request):
searchedTerm = request.GET.get('q')
try:
exactMatch = Entry.objects.get(title=searchedTerm)
entryTitle = exactMatch.title
entryHTML = markdown2.markdown(exactMatch.content)
return render(request, "encyclopedia/displayEntry.html", {
"entryTitle": entryTitle,
"entryHTML": entryHTML
})
except:
try:
searchResults = Entry.objects.filter(Q(title__icontains=searchedTerm))
return render(request, "encyclopedia/searchResults.html", {
"searchResults": searchResults,
"searchedTerm": searchedTerm
})
except:
return render(request, "encyclopedia/searchResults.html", {
"emptyResults": f"No entries found matching: {searchedTerm}",
"searchedTerm": searchedTerm
})
class SearchView(ListView):
template_name = "encyclopedia/searchResults.html"
model = Entry
context_object_name = "searchList"
def get_queryset(self):
searchedTerm = self.request.GET.get('q')
try:
searchResults = Entry.objects.get(title=searchedTerm)
return searchResults
except:
try:
searchResults = Entry.objects.filter(Q(title__icontains=searchedTerm))
return searchResults
except:
pass
def as_view():
searchedTerm = self.request.GET.get('q')
try:
exactMatch = Entry.objects.get(title=searchedTerm)
entryTitle = exactMatch.title
entryHTML = markdown2.markdown(exactMatch.content)
return render(request, "encyclopedia/displayEntry.html", {
"entryTitle": entryTitle,
"entryHTML": entryHTML,
})
except:
searchResults = Entry.objects.filter(Q(title__icontains=searchedTerm))
return render(request, "encyclopedia/searchResults.html", {
"searchResults": searchResults,
"searchedTerm": searchedTerm
})
else:
return render(request, "encyclopedia/searchResults.html", {
"emptyResults": f"No entries found matching: {searchedTerm}",
"searchedTerm": searchedTerm
})
search form from layout.html
<form action="{% url 'wiki:search' %}" method="GET">
<input class="search" type="text" name="q" placeholder="Search Encyclopedia">
<!-- <input type="submit" value="submit"> -->
</form>
display entry page template
{% extends "encyclopedia/layout.html" %}
{% block title %}
{% if entryTitle %}
{{ entryTitle }}
{% else %}
Page Not Found!
{% endif %}
{% endblock %}
{% block body %}
{% if entryHTML %}
{{ entryHTML|safe }}
{% else %}
<p>This page does not exist yet.</p>
<p>Check your spelling or create a new entry!</p>
<p>?? {{ testPrint }}</p>
{% endif %}
{% endblock %}
search results page
{% extends "encyclopedia/layout.html" %}
{% block title %}
Search Results: {{ searchedTerm }}
{% endblock %}
{% block body %}
{% if searchResults %}
<h3>Search Results</h3>
<ul>
{% for result in searchResults %}
<li>{{ result.title }}</li>
{% endfor %}
</ul>
{% else %}
<h3>{{ emptyResults }}</h3>
{% endif %}
{% endblock %}
Models
class Entry(models.Model):
title = models.CharField(max_length=64)
content = models.TextField()
def __str__(self):
return self.title
Views
from django.views.generic.list import ListView
from django.views.generic.detail import DetailView
from app.models import Entry
class EntryListView(ListView):
model = Entry
paginate_by = 100 # if pagination is desired
def get_queryset(self):
queryset = super().get_queryset()
q = self.request.GET.get("q")
if q:
queryset = queryset.filter(title__icontains=q)
return queryset
class EntryDetailView(DetailView):
model = Entry
Urls
from django.urls import path
from app.views import EntryListView, EntryDetailView
urlpatterns = [
path('', EntryListView.as_view(), name='entry-list'),
path('<int:pk>/', ArticleDetailView.as_view(), name='entry-detail'),
]

Visibility of a Django formview in templates

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.

Implementing views with template in django

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.

Django:Profiles under auth.Group where auth.Group is used as foreign key in the app model

I want to display the dashboards which are under the group of logged in user, and I already have logged in user information and group-name of that user
so far my views.py is
def custom_login(request):
if request.user.is_authenticated():
return HttpResponseRedirect('dashboards')
return login(request, 'login.html', authentication_form=LoginForm)
def custom_logout(request):
return logout(request, next_page='/')
def user(request):
context = {'user': request.user, 'groups': request.user.groups.all()}
return render_to_response('registration/dashboards.html', context, context_instance=RequestContext(request))
dashboard.html is like
{% extends "base.html" %}
{% block content %}
{% if user.is_authenticated %}
<p>Welcome, {{ request.user.get_username }}. <br/>
{% else %}
<p>Welcome, new user. Please log in.</p>
{% endif %}
<ul>
{% for group in groups %}
<li>
and you belongs to <strong>{{ group.name }}<strong> -
{% endfor %}
</li>
</ul>
{% endblock %}
forms.py looks like
from django import forms
from django.contrib.auth.models import User
from django.forms import ModelForm
#from mhawk.models import Dashboard
class LoginForm(forms.Form):
username = forms.CharField(label=(u'User Name'))
password = forms.CharField(label=(u'Password'), widget=forms.PasswordInput(render_value=False))
and my models.py is
from django.db import models
from django.contrib.auth.models import Group, User
from django.utils import timezone
import datetime
class Dashboard(models.Model):
d_name = models.CharField(max_length=200)
d_description = models.CharField(max_length=200)
d_url = models.CharField(max_length=200)
d_status = models.CharField(max_length=200)
owner = models.ForeignKey(Group)
def __str__(self):return self.d_name
and urls.py is
from django.conf.urls import patterns, include, url
from django.contrib import admin
from drinker import views, models
from django.contrib.auth.models import User
urlpatterns = patterns('',
url(r'^admin/', include(admin.site.urls)),
url(r'^login/$', 'django.contrib.auth.views.login',),
url(r'^logout/$', 'django.contrib.auth.views.logout'),
url(r'^dashboards/$', 'mhawk.views.user'),
)
as I am able to display the logged in username, in the same way I want to display his group and the dashboards which are under that group, thank you. Looking forward for help.
Can you not just do something like this:
def user(request):
user_groups = request.user.groups.all()
if request.user.is_anonymous():
user_groups = []
user_dashboards = Dashboard.objects.filter(owner__in=user_groups)
context = {
'user': request.user,
'user_groups': user_groups,
'user_dashboards': user_dashboards,
}
[...]
In your template, you need to do something like this...
{% extends "base.html" %}
{% block content %}
{% if user.is_authenticated %}
<p>Welcome, {{ request.user.get_username }}. <br/>
{% else %}
<p>Welcome, new user. Please log in.</p>
{% endif %}
<p></p>
<p>And you belongs to: </p>
<ul>
{% for group in user_groups %}
<li>
<strong>{{ group.name }}<strong> -
</li>
{% endfor %}
</ul>
{% endblock %}
Learn more about Django templating:
https://docs.djangoproject.com/en/1.7/ref/templates/builtins/#for