Bootstrap navbar dropdown populated with database entries in Django 2 - django

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.

Related

Why am I getting a No Reverse Match error when submitting using UpdateView?

I'm currently using UpdateView to add edit functionality to my Django project. It's working correctly insofar as I can edit my data, however when I submit the new data, it returns a NoReverseMatch error:
NoReverseMatch at /MyHealth/edit/8
Reverse for 'health_hub_history' not found. 'health_hub_history' is not a valid view function or pattern name.
I've researched it and added a get_absolute_url to my model, but it isn't working. Any help would be appreciated!
models.py:
from django.db import models
from django.contrib.auth.models import User
from django.urls import reverse
class HealthStats(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
date = models.DateField(auto_now=True)
weight = models.DecimalField(max_digits=5, decimal_places=2)
run_distance = models.IntegerField(default=5)
run_time = models.TimeField()
class Meta:
db_table = 'health_stats'
ordering = ['-date']
def get_absolute_url(self):
return reverse('health_hub_history')
def __str__(self):
return f"{self.user} | {self.date}"
urls.py:
from django.urls import path
from django.contrib.staticfiles.storage import staticfiles_storage
from django.views.generic.base import RedirectView
from . import views
app_name = 'HealthHub'
urlpatterns = [
path('', views.home, name='home'),
path('MyHealth/', views.health_hub, name='health_hub'),
path('MyHealth/update', views.UpdateHealth.as_view(), name='health_hub_update'),
path('MyHealth/history', views.health_history, name='health_hub_history'),
path('favicon.ico', RedirectView.as_view(url=staticfiles_storage.url("favicon.ico"))),
path('MyHealth/delete/<item_id>', views.delete_entry, name='health_hub_delete'),
path('MyHealth/edit/<int:pk>', views.EditHealth.as_view(), name='health_hub_edit'),
]
Views.py:
class EditHealth(UpdateView):
model = HealthStats
template_name = 'health_hub_edit.html'
fields = ['weight', 'run_distance', 'run_time']
health_hub_edit.html:
{% extends 'base.html' %}
{% load static %}
{%load crispy_forms_tags %}
{% block content %}
<div class="container-fluid">
<div class="row">
<div class="col-sm-12 text-center">
<h1>Edit my Data</h1>
</div>
</div>
</div>
<div class="container-fluid">
<div class="row justify-content-center">
<div class="col-auto text-center p-3">
<form method="post" style="margin-top: 1.3em;">
{{ form | crispy }}
{% csrf_token %}
<button type="submit" class="btn btn-signup btn-lg">Submit</button>
</form>
</div>
</div>
<div class="row justify-content-center">
</div>
</div>
{% endblock content %}
This error occurs because the django couldn't resolve the url
you didn't specify a primary key to base the query on.
you should modify this function
def get_absolute_url(self):
return reverse('health_hub_history', kwargs={'user': self.user})
also that health_history url is it a class based view you should add .as_view() to the end and observe camel casing
Lastly your url should observe the primary key specified
You can do this:
class EditHealth(UpdateView):
model = HealthStats
template_name = 'health_hub_edit.html'
fields = ['weight', 'run_distance', 'run_time']
def get(self, request):
return HttpResponse("health_hub")
def post(self, request):
# do something
return redirect("health_hub")
urlpatterns = patterns('',
url('', views.home, name='home'),
url('MyHealth/', views.health_hub, name='health_hub'),
url('^MyHealth/update', views.UpdateHealth.as_view(), name='health_hub_update'),
url('MyHealth/history', views.health_history, name='health_hub_history'),
url('favicon.ico', RedirectView.as_view(url=staticfiles_storage.url("favicon.ico"))),
url('MyHealth/delete/<item_id>', views.delete_entry, name='health_hub_delete'),
url('MyHealth/edit/<int:pk>', views.EditHealth.as_view(), name='health_hub_edit'),
)
This will solve your problem
I found the answer to this issue here:
https://stackoverflow.com/a/48068932/19053957
Looks like all I need to do was refer to the app name prior to the name of the URL in get_absolute_url()!

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 2.0 CreateView not working

I am very new in Django. Not sure whether this is a bug or an error.
Here is my model in an app called gcbv (for generic class-based view)
from django.db import models
from core.models import TimeStampModel
from django.urls import reverse
# Create your models here.
class Vehicle(TimeStampModel):
maker = models.CharField(max_length=100)
model_year = models.IntegerField()
vehicle_type = models.CharField(max_length=100)
slug = models.SlugField(max_length=100, unique=True)
vehicle_model = models.CharField(max_length=100)
website = models.URLField(max_length=100, blank=True)
email = models.EmailField(max_length=100, blank=True)
notes = models.TextField(blank=True, default='')
def __str__(self):
x = self.maker + ' ' + self.vehicle_model
return x
And here are the URLs:
from django.contrib import admin
from django.urls import path, include
from django.conf.urls import url
from . import views
from django.urls import reverse
#from django.views.generic.base import TemplateView
app_name = 'gcbv'
urlpatterns = [
path('sub1/', views.SubView.as_view(), name='sub1'),
path('vehicle_list/', views.VehicleListView.as_view(),
name = 'vehicle_list'),
path('vehicle/<str:slug>/',
views.VehicleDetailView.as_view(),
name='vehicle_detail'),
path('vehicle/create', views.VehicleCreateView.as_view(),
name='vehicle_create'),
path('', views.IndexTemplateView.as_view(), name='index'),
]
And here is the relevant view:
class VehicleCreateView(CreateView):
model = Vehicle
fields = ['maker', 'model_year', 'vehicle_type', 'slug',
'vehicle_model', 'website', 'email', 'notes']
labels = {'maker':'Maker', 'model_year':'Year',
'vehicle_type':'Type', 'vehicle_model':'Model',
'website':'Website', 'email':'Email', 'notes':'Notes'}
Here is the template:
{% extends "core/base.html" %}
{% block body_block %}
<h1>Vehicle Create for GCBV</h1>
<form action="POST" action="">
{% csrf_token %}
{{ form.as_p }}
<button name="submit" class="btn btn-primary">Submit</button>
</form>
<h1>End Vehicle Create for GCBV</h1>
{% endblock %}
It looks as if the data aren't saved in the database, but when i'm adding the same data by hand directly in the admin page, everything works fine. I've attached another screenshot showing that VehicleDetailView has found the relevant template and rendered the information.
Any help would be greatly appreciated.
NB: Everything worked fine when I use function views and regex instead of path.
Form
After submit
List
Details
OK, this is what we septuagenarians call a "senior moment". I have been staring at this code for two days and did not see the obvious.
method="POST"!
NOT
action="POST"
Many, many thanks
In the fourth line of your template, method should be equal to "post"
{% extends "core/base.html" %}
{% block body_block %}
<h1>Vehicle Create for GCBV</h1>
<form method="POST" action="">
{% csrf_token %}
{{ form.as_p }}
<button name="submit" class="btn btn-primary">Submit</button>
</form>
<h1>End Vehicle Create for GCBV</h1>
{% endblock %}

Django DetailView get_context_data

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 %}

Django generic views problems with url

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