Django DetailView get_context_data - django

I'm new in Django. There is a html page (project_details) which should show the title and the tasks of the project, but shows only the title of the project, not the tasks. The tasks exists, the problem is the filter!!!
views.py The error is here
from .models import Project,Task
from django.views.generic import ListView, DetailView
class ProjectsList(ListView):
template_name = 'projects_list.html'
queryset= Project.objects.all()
class ProjectDetail(DetailView):
model = Project
template_name = 'projects_details.html'
def get_context_data(self, **kwargs):
context = super(ProjectDetail, self).get_context_data(**kwargs)
## the context is a list of the tasks of the Project##
##THIS IS THE ERROR##
context['tasks'] = Task.object.filter(list=Project) <---->HERE ((work with Task.object.all() ))
return context
models.py
class Project(models.Model):
title = models.CharField(max_length=30)
slug = AutoSlugField(populate_from='title', editable=False, always_update=True)
class Task(models.Model):
title = models.CharField(max_length=250)
list = models.ForeignKey(Project)
slug = AutoSlugField(populate_from='title', editable=False, always_update=True)
urls.py
from django.conf.urls import url
from .models import Project
from .views import ProjectsList, ProjectDetail
urlpatterns = [
url(r'^$', ProjectsList.as_view(), name='project_list'),
url(r'(?P<slug>[\w-]+)/$',ProjectDetail.as_view() , name='project_details'),]
projects_details.html
{% extends './base.html' %}
{% block content %}
<div>
<a href={{ object.get_absolute_url }}>
<h4> {{object.title}} </h4>
</a>
<ul>
{% for task in tasks %} <----> NO OUTPUT <li>
<li> {{task}}</li>
{% endfor %}
</ul>
</div>
{% endblock content %}
Sorry for my bad English.

Project is the model class, so doing (list=Project) doesn't make sense.
If you want to access the object in the detail view's get_context_data method, you can use self.object:
def get_context_data(self, **kwargs):
context = super(ProjectDetail, self).get_context_data(**kwargs)
context['tasks'] = Task.objects.filter(list=self.object)
return context
However, you don't actually have to override the get_context_data method at all. In your template, you can follow the relationship backwards from a project to get its tasks:
{% for task in object.task_set.all %}
<li>{{task}}</li>
{% endfor %}

Related

Objects are not being displayed to the front end as soon as i use context_object_name in django

I was trying to display a list of items on the front end of a project using Django. I successfully did it and could see the objects in a list upon spinning up the server. But as soon as I used context_object_name in the views.py file, and then check the page, the objects are not appearing. Also, there is no error shown in the terminal and the docker logs too.
Here's my Views.py file
from django.shortcuts import render
from django.views.generic import ListView
from .models import Book
# Create your views here.
class BookListView(ListView):
model = Book
context_object_name = 'book_list'
template_name = 'books/book_list.html'
Here's the models.py file
from django.db import models
# Create your models here.
class Book(models.Model):
title = models.CharField(max_length=200)
author = models.CharField(max_length=200)
price = models.DecimalField(max_digits=6, decimal_places=2)
def __str__(self):
return self.title
Here's the book_list.html file
<!--templates/books/book_list.html-->
{% extends '_base.html' %}
{% block title %}Books{% endblock title %}
{% block content %}
{% for book in book_list %}
<div>
<h2>{{ object.title }}</h2>
</div>
{% endfor %}
{% endblock content %}
small mistake in book_list.html. object.title to replace book.title
<h2>{{ book.title }}</h2>

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.

reverse url link within list view template

i created methods within my model class to handle my redirects all work except for the one i created for a link in my list view(go_to_create method ) im using class based views
class Todo(models.Model):
name = models.CharField(max_length=100, default='unamedTodo')
description = models.CharField(max_length=200)
Todo_date = models.DateTimeField('Todo Date')
pub_date = models.DateTimeField('Date Published')
def get_absolute_url(self):
return reverse('ToDo:detail', kwargs={'id': self.id})
def get_back_home(self):
return reverse('ToDo:todos', kwargs={})
def go_to_update(self):
return reverse('ToDo:update', kwargs={'id': self.id})
def go_to_create(self):
return reverse('ToDo:create', kwargs={})
class TodoCreateView(CreateView):
template_name = 'ToDo/todo_create.html'
form_class = TodoForm
queryset = Todo.objects.all()
from django.urls import path
from .views import (
TodoListView,
TodoDetailView,
TodoCreateView,
TodoUpdateView,
TodoDeleteView,
)
app_name = "ToDo"
urlpatterns = [
path('Todos/', TodoListView.as_view(), name='todos'),
path('Todos/<int:id>/', TodoDetailView.as_view(), name='detail'),
path('Todos/create/', TodoCreateView.as_view(), name='create'),
path('Todos/<int:id>/update/', TodoUpdateView.as_view(), name='update'),
path('Todos/<int:id>/delete/', TodoDeleteView.as_view(), name='delete')
]
<h1>ToDo's</h1>
<ul>
{% for object in object_list %}
<li>
<p>
{{ object.id }} -
{{ object.name }}
</p>
</li>
{% endfor %}
<p>Create new Todo here</p>
</ul>
the link calling the go_to_create method does not work i stay on the same page no error is generated
Instead of a method call directly on template,that will do the job.
{% url 'ToDo:create' %}

Bootstrap navbar dropdown populated with database entries in Django 2

I am trying to populate a navbar "dropdown-menu" with individual "dropdown-item"'s populated from data in the sqlite3 DB.
I have something similar working on other pages but I cant get it to work in my navbar.
I am creating a record label, and want the list of artists populated from entries in the DB. I have found one tutorial on doing something similar in php, but doesn't translate, and there doesn't seem to be anything either on youtube or here other than populating form data.
Any help is greatly appreciated, as I have been trying to get it working for about a week now. I know it should be simple, but im missing something.
the app is called "music"
models.py
class Artist(models.Model):
artist_name = models.CharField(max_length=250, default='')
artist_logo = models.FileField()
artist_url = models.URLField(blank=True)
def __str__(self):
return self.artist_name
class Release(models.Model):
artist = models.ForeignKey(Artist, on_delete=models.CASCADE)
release_title = models.CharField(max_length=500)
release_cover = models.FileField()
release_duration = models.IntegerField()
def __str__(self):
return self.release_title
class Track(models.Model):
release = models.ForeignKey(Release, default='', on_delete=models.CASCADE)
artist = models.ForeignKey(Artist, default='', on_delete=models.CASCADE)
track_title = models.CharField(max_length=200)
track_version = models.CharField(max_length=200)
track_genre = models.CharField(max_length=100)
track_duration = models.IntegerField()
track_number = models.SmallIntegerField()
class Meta:
ordering = ["track_number"]
def __str__(self):
return self.track_title
views.py
from django.contrib.auth import authenticate, login
from django.views import generic
from django.views.generic import ListView, View
from .models import Artist, Track, Release
from .forms import UserForm
# class IndexView(ListView):
# template_name = 'music/index.html'
class ReleaseView(generic.ListView):
template_name = 'music/releaselist.html'
context_object_name = 'all_releases'
def get_queryset(self):
return Release.objects.all()
class ArtistView(generic.ListView):
model = Artist
template_name = 'music/artistlist.html'
context_object_name = 'all_artists'
def get_queryset(self):
return Artist.objects.all()
class DetailView(generic.DetailView):
model = Release
template_name = 'music/detail.html'
urls.py (Main)
from django.contrib import admin
from django.urls import path, include, re_path
from django.conf import settings
from django.conf.urls.static import static
urlpatterns = [
path('admin/', admin.site.urls),
# include urls from the music app
path('music/', include('music.urls'))
urls.py ("music" aka the app urls)
from django.contrib import admin
from django.urls import path, include, re_path
from . import views
# defined the app name in case the same fields are used in other apps
app_name = 'music'
urlpatterns = [
# no info past music return index EG /music/
# path('', views.IndexView.as_view(), name='index'),
# albums/releases
re_path(r'^release/$', views.ReleaseView.as_view(), name='release'),
# looking for music page with album id afterwards /music/1
re_path(r'^(?P<pk>[0-9]+)/$', views.DetailView.as_view(), name="detail"),
re_path(r'^(?P<pk>[0-9]+)/$', views.ArtistView.as_view(), name="artist"),
base.html
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="#" id="navbarDropdownMenuLink" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
Artists
</a>
<div class="dropdown-menu" aria-labelledby="navbarDropdownMenuLink">
{% for artist in all_artists %}
<li><a class="dropdown-item" href="#">{{ artist.artist_id }}</a></li>
{% endfor %}
</div>
</li>
Update:
Here is my releases.html which works using similar code, and at the bottom has a test which looks like the for loop is incorrect
{% extends 'music/base.html' %}
{% block title %}KOLD FUZEON: Releases{% endblock %}
{% block body %}
{% if all_releases %}
<ul>
{% for release in all_releases %}
<div class="releaseitem">
<li>{{ release.artist }} - {{ release.release_title }}</li>
<li><a href="{% url 'music:detail' release.id %}"</a><img src="{{ release.release_cover.url }}" style="width: 300px"></li>
</div>
{% endfor %}
</ul>
{% else %}
<h3>We currently dont have any releases yet.</h3>
{% endif %}
{#basic test for the artist list to be printed to screen#}
<ul>
<li>test1</li>
{% for artist in all_artists %}
<li>test2</li>
{% endfor %}
</ul>
{% endblock %}
In your View.py you have ArtistView where template is artistlist.html and your context is all_artist & you get all objects from db.
Code:
class ArtistView(generic.ListView):
model = Artist
template_name = 'music/artistlist.html'
context_object_name = 'all_artists'
def get_queryset(self):
return Artist.objects.all()
Now i believe you have a template named artistlist.html. If not create it in templates in which you will use for Loop
to render artist list so the code should be this for artistlist.html:
{% extends 'music/base.html' %}
{% block body %}
<h1>Artists!</h1>
<ul>
{% for artist in all_artists %}
<li class="artist">
<h1>
<a href='/music/{{ artist.id }}'>{{artist.artist_name }}</a>
</h1>
</li>
{% endfor %}
</ul>
</div>
{% endblock %}
You can Render Artist list in Base.html.
Using: Context Processor
First Create a file named context_processors.py in your App.Then add this code in that file.
from .models import Artist
def basetest(request):
hello = Artist.objects.values_list("artist_name", flat=True)
return {
'testname': hello
}
After that, open Settings.py and find context_processors and add the following settings 'yourapp.context_processors.add_variable_to_context'.
Settings.py
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
#This one is from my code so set it according to you
#appname.context_processors.function_name_context_processor.py',
'blog.context_processors.basetest',
],
},
},
]
After this just place {{ testname }} in your base.html according to your need. it will work. No need for looping just the interpolation will render you a list. Format it according to your need by following this doc
I believe there is some problem in the name of the 'key' that you are passing in the context.
In ReleaseView the context object name is all_releases, while you are trying to iterate on "all_artists".
Even if these changes not work, you can always try running your code on a normal view instead of the a generic view and pass the context in the template.

Django, how to do CRUD with django-mptt?

How we can implement CRUD functionality using generic views and django-mptt ?? I've searched a lot and couldn't find a single tutorial/sample code.
Let's say we have a Course hierarchy or Category hierarchy, or similar thing ... How we can Add/Delete/Update/Read them ??
For instance I have this model:
from django.db import models
from mptt.models import MPTTModel , TreeForeignKey
class Genre(MPTTModel):
name = models.CharField(max_length = 50 , unique = True)
parent = TreeForeignKey('self' , null = True , blank = True , related_name = 'children')
class MPTTMeta:
order_insertion_by = ['name']
and this views.py:
from django.views.generic.list_detail import object_list
from mp.models import Genre
def genres_list(request):
''' Shows all of the genres '''
return object_list(request,
queryset = Genre.tree.all() ,
template_name = 'genres.html' ,
# template_object_name = 'nodes' ## Adding "nodes" variable didn't solve the problem
)
well ... I get this error (error is in line number "5" : {% recursetree nodes %}):
Caught VariableDoesNotExist while rendering: Failed lookup for key [nodes] in u"[{'paginator': None, 'is_paginated': False, 'page_obj': None, 'nodes_list': [<Genre: Genre object>, <Genre: Genre object>, <Genre: Genre object>, <Genre: Genre object>]}, {'csrf_token': <django.utils.functional.__proxy__ object at 0x7f5bb810f090>}, {'perms': <django.utils.functional.__proxy__ object at 0x7f5bb810ff10>, 'messages': <django.contrib.messages.storage.user_messages.LegacyFallbackStorage object at 0x324af50>, 'user': ....................................
<html>
2
3 {% load mptt_tags %}
4 <ul>
5 {% recursetree nodes %}
6 <li>
7 {{node.name}}
Simple CRUD application with MPTT models and class-based generic views (Django 1.4 The function-based implementation has been deprecated).
Let's begin
urls.py
from django.conf.urls.defaults import patterns, include, url
from django.views.generic import DetailView, ListView, CreateView, UpdateView
from genre.models import Genre
urlpatterns = patterns('',
url(r'detail/(?P<pk>\d+)', DetailView.as_view(model=Genre), name="genre_detail",),
url(r'update/(?P<pk>\d+)', UpdateView.as_view(model=Genre), name="genre_update",),
url(r'create', CreateView.as_view(model=Genre), name="genre_create",),
url(r'list', ListView.as_view(model=Genre), name="genre_list",),
)
models.py
from django.core.urlresolvers import reverse
from django.db import models
from mptt.models import MPTTModel
class Genre(MPTTModel):
name = models.CharField(max_length=50 , unique=True)
parent = models.ForeignKey('self' , null=True , blank=True , related_name='children')
def get_absolute_url(self):
return reverse('genre_detail', kwargs={'pk': self.pk, })
class MPTTMeta:
order_insertion_by = ['name']
templates/genre_detail.html
<html>
<body>
<div>Object: {{ object }}</div>
<div>Object's name: {{ object.name }}</div>
<div>Object's parent: {{ object.parent }}</div>
</body>
</html>
templates/genre_form.html
<html>
<body>
<form action="" method="post">
{% csrf_token %}
{{ form.as_ul }}
<button>save</button>
</form>
</body>
</html>
templates/genre_list.html
{% load mptt_tags %}
<html>
<body>
<ul class="root">
{% recursetree object_list %}
<li>
{{ node.name }}
{% if not node.is_leaf_node %}
<ul class="children">
{{ children }}
</ul>
{% endif %}
</li>
{% endrecursetree %}
</ul>
</body>
</html>
and that's it.
I had some spare time today and shared this project on github https://github.com/kaygorodov/simple-crud-mptt.
How can I define my own class-based view?
genre/views.py
from django.view.generic import UpdateView
class MyCustomUpdateView(UpdateView):
model = Genre
def get_form_kwargs(self):
"""
Returns the keyword arguments for instanciating the form.
"""
kwargs = super(MyCustomUpdateView, self).get_form_kwargs()
kwargs.update({'my_first_param_to_init_form': 1,
'my_second_param_to_init_form': 2,
})
return kwargs
genre/urls.py
url(r'update/(?P<pk>\d+)', MyCustomUpdateView.as_view(), name="genre_update",),