I am a beginner learning Django through a building an app, called PhoneReview. It will store reviews related to the latest mobile phone. It will also display phone brands, along with the associated phone models and their reviews.
Right now, I am facing an error just after I have added codes to use slug in the URLs. When I go to http://127.0.0.1:8000/index, I see this page:
When I click on "Samsung," I get this error:
Page not found (404)
Request Method: GET
Request URL: http://127.0.0.1:8000/index/samsung/
Raised by: PhoneReview.views.ModelView
No phone model found matching the query
You're seeing this error because you have DEBUG = True in your Django settings file. Change that to False, and Django will display a standard 404 page.
I have successfully performed migration. But still, I am facing the issue.
Here are my codes of models.py located inside PhoneReview folder:
from django.db import models
from django.template.defaultfilters import slugify
# Create your models here.
class Brand(models.Model):
brand_name = models.CharField(max_length=100)
origin = models.CharField(max_length=100)
manufacturing_since = models.CharField(max_length=100, null=True, blank=True)
slug = models.SlugField(unique=True, max_length=150)
def __str__(self):
return self.brand_name
def save(self, *args, **kwargs):
self.slug = slugify(self.brand_name)
super().save(*args, **kwargs)
class PhoneModel(models.Model):
brand = models.ForeignKey(Brand, on_delete=models.CASCADE)
model_name = models.CharField(max_length=100)
launch_date = models.CharField(max_length=100)
platform = models.CharField(max_length=100)
slug = models.SlugField(unique=True, max_length=150)
def __str__(self):
return self.model_name
def save(self, *args, **kwargs):
self.slug = slugify(self.model_name)
super().save(*args, **kwargs)
class Review(models.Model):
phone_model = models.ManyToManyField(PhoneModel, related_name='reviews')
review_article = models.TextField()
date_published = models.DateField(auto_now=True)
slug = models.SlugField(max_length=150, null=True, blank=True)
link = models.TextField(max_length=150, null=True, blank=True)
def __str__(self):
return self.review_article
Here are my codes of urls.py located inside PhoneReview folder:
from . import views
from django.urls import path
app_name = 'PhoneReview'
urlpatterns = [
path('index', views.BrandListView.as_view(), name='brandlist'),
path('index/<slug:slug>/', views.ModelView.as_view(), name='modellist'),
path('details/<slug:slug>/', views.ReviewView.as_view(), name='details'),
]
Here are my codes of views.py located inside PhoneReview folder:
from django.shortcuts import render
from django.views import generic
from .models import Brand, PhoneModel, Review
class BrandListView(generic.ListView):
template_name = 'PhoneReview/index.html'
context_object_name = 'all_brands'
def get_queryset(self):
return Brand.objects.all()
class ModelView(generic.DetailView):
model = PhoneModel
template_name = 'PhoneReview/phonemodel.html'
context_object_name = 'phonemodel'
class ReviewView(generic.DetailView):
model = Review
template_name = 'PhoneReview/details.html'
Here are my codes of apps.py located inside PhoneReview folder:
from django.apps import AppConfig
class PhonereviewConfig(AppConfig):
name = 'PhoneReview'
Here are my codes of index.html located inside templates folder:
{% extends 'PhoneReview/base.html' %}
{% load static %}
{% block title%}
Brand List
{% endblock %}
{% block content %}
<!--Page content-->
<h1>This is Brand List Page</h1>
<h2>Here is the list of the brands</h2>
<ul>
{% for brand in all_brands %}
<!-- <li>{{ brand.brand_name }}</li>-->
<li>{{ brand.brand_name }}</li>
{% endfor %}
</ul>
<img src="{% static "images/brandlist.jpg" %}" alt="Super Mario Odyssey" /> <!-- New line -->
{% endblock %}
Here are my codes of phonemodel.html located inside templates folder:
{% extends 'PhoneReview/base.html' %}
{% load static %}
{% block title%}
Phone Model Page
{% endblock %}
{% block content %}
<!--Page content-->
<h1>This is Phone Model Page</h1>
<h2>Here is the phone model</h2>
<ul>
<li>{{ phonemodel.model_name }}</li>
</ul>
<img src="{% static "images/brandlist.jpg" %}" alt="Super Mario Odyssey" /> <!-- New line -->
{% endblock %}
Here are my codes of details.html located inside templates folder:
{% extends 'PhoneReview/base.html' %}
{% load static %}
<html>
<link rel="stylesheet" type="text/css" href="{% static "css/style.css" %}">
<html lang="en">
{% block title%}Details{% endblock %}
{% block content %}
<h1>This is the Details Page</h1>
<h2>Review:</h2>
<p>{{ review.review_article }}</p>
<h2>News Link:</h2>
<p>{{ review.link }}</p>
{% endblock %}
</html>
I feel that I have made a mistake on either index.html or phonemodel.html. But being a beginner, I can't catch it.
How can I fix the issue?
Update: I added the following codes in phonemodel.html to loop over phone models, as suggested by #c.grey :
<ul>
{% for model_name in all_model_name %}
<li>{{ phonemodel.model_name }}</li>
{% endfor %}
</ul>
Also, I added this line in index.html:
<li>{{ brand.brand_name }}</li>
Moreover, I added these codes in views.py:
class ModelView(generic.ListView):
template_name = 'PhoneReview/phonemodel.html'
context_object_name = 'all_model_name'
def get_queryset(self):
return PhoneModel.objects.all()
But now, I am getting this error:
django.urls.exceptions.NoReverseMatch: Reverse for 'modellist' with arguments '('',)' not found. 1 pattern(s) tried: ['index/(?P<slug>[-a-zA-Z0-9_]+)/$']
Here is the link to my project files:
https://github.com/shawnmichaels583583/phoneradar
Basically you are passing Brand model slug to get PhoneModel model data.
To get details from PhoneModel you need to pass PhoneModel slug to get data
Your error is that your link points to brand.slug (here samsung) but it seems to me that you don't have any phone model whose name is just samsung, do you?
You mix up brands and phone models
Related
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>
I am currently building a website with Django that will host music and album/song details. My models are set up so that each song has a foreign key to it's associated album, or to a database object titled "Singles" if they are not part of an album.
This relationship between song and album has worked great while building the site, until I got to the 'Play Song' page I am currently working on. Each Single has an associated artwork in the 'Song' model, while each song in an album has no 'picture' in the database, as the 'picture' is a part of the Album model and not the Song model in these instances. I am attempting to pass data from both the Song model and the Album model into my DetailView so that if the song being played is from an album rather than a single, it takes the 'picture' from the Album model it has a foreign key to rather than from it's own internal 'picture' object. My code is below, it renders the 'picture' for Singles perfectly, but cannot render the 'picture' object from the album and instead shows my {% else %} condition of a object not found picture. The HTML file has the logic I am using to find the associated picture:
{% elif song in album.song_set.all %}
<img src="{{ album.picture.url }}">
Any help would be appreciated.
models.py
class Album(models.Model):
title = models.CharField(
max_length=200,
validators=[MinLengthValidator(2, "Must be at least two characters.")]
)
release_date = models.DateField()
picture = models.ImageField(upload_to='albums/', blank=True)
content_type = models.CharField(max_length=256, blank=True, help_text='The MIMEType of the file')
description = models.TextField(blank=True)
def __str__(self):
return self.title
def __unicode__(self):
return self.title
class Song(models.Model):
title = models.CharField(
max_length=200,
validators=[MinLengthValidator(2, "Must be at least two characters.")]
)
release_date = models.DateField(blank=True)
length = models.CharField(
max_length=200)
featured_artists = models.CharField(
max_length=200,
blank=True)
picture = models.ImageField(upload_to='singles/', blank=True)
content_type = models.CharField(max_length=256, blank=True, help_text='The MIMEType of the file')
description = models.TextField(blank=True)
alb = models.ForeignKey(Album, on_delete=models.CASCADE)
audio_file = models.FileField(upload_to='music_audio/', blank=True)
def __str__(self):
return self.title
views.py
class PlaySongView(DetailView):
model = Song
template_name = 'music/play_song.html'
def get_context_data(self, **kwargs):
context = super(PlaySongView, self).get_context_data(**kwargs)
context['album'] = Album.objects.exclude(title="Single")
return context
urls.py
urlpatterns = [
path('', views.MusicView.as_view(), name='music'),
path('<int:pk>/', views.AlbumDetailView.as_view(), name='album_detail'),
path('<int:pk>/play', views.PlaySongView.as_view(), name='play')
]
play_song.html
{% load static tailwind_tags %}
<!DOCTYPE html>
<head>
<title>LewnyToons - {{ song.title }}</title>
{% tailwind_css %}
</head>
<body class="antialiased text-slate-400 bg-slate-900">
<div class="w-full sm:w-9/12 lg:w-8/12 px-4 sm:pr-2 lg:pr-4 mx-auto">
<div class="flex flex-col font-bold text-2xl text-center items-center text-white mx-auto pt-2">
<h1 class="mb-4">Listen to {{ song.title }} now!</h1>
{% if song.picture %}
<img src="{{ song.picture.url }}" class="min-h-64 min-w-64 md:h-96 md:w-96"alt="">
{% elif song in album.song_set.all %}
<img src="{{ album.picture.url }}" class="min-h-64 min-w-64 md:h-96 md:w-96"alt="">
{% else %}
<img src="{% static 'something_wrong.jpg' %}" class="min-h-64 min-w-64 md:h-96 md:w-96"alt="">
{% endif %}
</div>
{% if song.audio_file %}
<span class="flex justify-center py-10">
<div>
<audio controls><source src="{{ song.audio_file.url }}" type="audio/mpeg"></audio>
</div>
</span>
{% else %}
<p>The file could not be found.</p>
<a href="{% url 'the_music:music' %}" class="text-white bold_underline">Return to music.</p>
{% endif %}
</div>
</body>
</html>
The issue lies within the album context passed to the template in PlaySongView.get_context_data().
You are passing a QuerySet of albums instead of the album object the song is related to. So when you try to do album.song_set.all, you're running that on a QuerySet instead of an Album object. The condition is not met, hence the fallback to the else block.
Instead of:
context['album'] = Album.objects.exclude(title="Single")
A better way to approach this in get_context_data() would be:
song = self.get_object()
context['album'] = song.alb
self.get_object() will look for a pk in your URL kwargs (in this case, '<int:pk>/play') and lookup the song based on its primary key.
Then in your template, it's as simple as doing:
{{ album }}
There is no need to do a reverse relation in the template here because you have direct access to the album from your song DetailView with self.get_object(). Plus, this way you're not making unnecessary queries by fetching all your albums and passing them as context to the template. All you need is that 1 album.
As a general rule of thumb: if you can accomplish what you need query-wise in the view, it's typically a more optimal solution.
I am creating a blog application in django where i encountered this unusual issue. I am not being able to filter and display blog posts category wise. Please find my code below. Thanks in advance.
MODELS.py
This is the model which i have created for a blog post.
class Post(models.Model):
title = models.CharField(max_length=255)
author = models.ForeignKey(User, on_delete=models.CASCADE)
body = models.TextField()
post_date = models.DateField(auto_now_add=True)
category = models.CharField(max_length=255, default="uncategorized")
def __str__(self):
return self.title + ' | ' + str(self.author)
def get_absolute_url(self):
#return reverse('article-detail', args=(str(self.id)))
return reverse('homepage')
VIEWS.py
This is the view that i have created for a category wise blog posts.
def CategoryView(request,cats):
category_posts = Post.objects.filter(author= 'apaar')
return render(request,'categories.html',{'cat':cat,'category_posts':category_posts})
URLS.py
The urls.py file.
path('category/<str:cat>/', CategoryView, name = 'category'),
CATEGORIES.html
This will be the final display page where the category_posts is displayed as an empty queryset. The for loop is unable to run because category_posts is an empty queryset.
{% extends 'base.html' %}
{% load static %}
{% block content %}
<h1>Categories</h1>
<ul>
{% for posts in category_posts %}
<li>
<div class="category">{{ post.category }}</div>
<a href="{% url 'article-detail' post.pk %}"><h3><b>{{ post.title }} </b></h3>
<p class=card-date> <big>{{ post.author }}</big><small> -{{ post.post_date }} </small> </p>
<p class="excerpt-text">{{ post.body | slice:"100" |safe}}</p>
<div class="article-thumbnail">
<img src="{% static "images/bg.png" %}" alt="article-thumbnail" >
<div class="overlay"></a>
</li>
{% endfor %}
{% endblock %}
By what I understand you're trying to do you want the following:
def CategoryView(request,cat):
category_posts = Post.objects.filter(category=cat)
return render(request,'categories.html',{'category_posts':category_posts})
Use cat as your parameter, since you're using <str:cat> in your urls.py, otherwise you might get an "Unexpected parameter" error.
You have to filter by category, not author. Youre question is correct, but your code isn't.
You don't need to pass cat varibale in context, since any posts would contain it within themselves.
Taking #Willem Van Onsem and #Roham suggestions:
{% for post in category_posts %}
# ...
<div class="category">{{ post.category }}</div>
If you want to use slugs, maybe checkout models.SlugField.
Problem:
I cannot get images to appear on my template plant_detail.html. I think I'm calling on variables incorrectly, but not sure what to change.
Context:
I created a model PlantImage, that allows me to associate multiple images within my model Plant.
I then created a class-based view PlantDetailView to link the two models, PlantImage and Plant together.
However, now when I try to display those images in the template plant_detail.html, nothing will appear. How can I get images to appear on my template?
I tried reading through the Django documentation, reading articles, and watching youtube videos, but I'm not sure what I'm missing or need to edit.
My files:
plants/models.py
class Plant(models.Model):
name = models.CharField(max_length=120)
slug = models.SlugField(null=False, unique=True)
description = models.TextField()
def __str__(self):
return self.name
def get_absolute_url(self):
return reverse("plant_detail", kwargs={"slug": self.slug})
class PlantImage(models.Model):
plant = models.ForeignKey(Plant, default=None, on_delete=models.CASCADE)
images = models.ImageField(upload_to = 'images/')
def __str__(self):
return self.plant.name
plants/views.py
def plant_index(request):
plant_objects = Plant.objects.all()
context = {'plant_objects': plant_objects}
return render(request, 'plants/plant_index.html', context)
class PlantDetailView(DetailView):
model = Plant
template_name = 'plants/plant_detail.html'
slug = 'slug'
def get_context_data(self, **kwargs):
context = super(PlantDetailView, self).get_context_data(**kwargs)
context['plant_images'] = PlantImage.objects.all()
return context
plant_detail = PlantDetailView.as_view()
plants/urls.py
from django.urls import path
from .views import plant_index, plant_detail
app_name = 'plants'
urlpatterns = [
# ex: /plants/
path('', plant_index, name='plant_index'),
# ex: /plants/pothos/
path("<slug:slug>", plant_detail, name="plant_detail"),
]
plants/plant_detail.html
{% block content %}
<body>
<h1> {{ plant.name }} </h1>
<br>
<p> <b>Latin Name: </b>{{ plant.latin_name }} </p>
<br>
<p><b>Description: </b></p>Dj
<p> {{ plant.description }} </p>
<br>
<br>
<p>Images:</p>
<br>
{% for image in plant_images %}
<p><img src="{{ plant.images }}" width="300px"></p>
{% endfor %}
</body>
{% endblock content %}
settings.py
MEDIA_URL = '/media/'
MEDIA_ROOT = str(BASE_DIR.joinpath('mediafiles'))
Screenshots of output:
The code "PlantImage.objects.all()" gets not only specific image related plant but another images.
so if you want to access PlantImage from Plant, you have to write related_name.
https://docs.djangoproject.com/en/4.0/ref/models/fields/#django.db.models.ForeignKey.related_name
class PlantImage(models.Model):
plant = models.ForeignKey(Plant, default=None, on_delete=models.CASCADE, related_name="plant_images")
...
ImageField inherit FileField. so, you have to know something of Filefield functions.
https://docs.djangoproject.com/en/4.0/ref/models/fields/#filefield-and-fieldfile
if you want to show images, add '.url' like this.
{% for image in plant.plant_images.all %}
<p><img src="{{ image.images.url }}" width="300px"></p>
{% endfor %}
For some reason, suggestions from django-autocomplete-light 3.5.0 are displayed beyond the drop-down list, to the right (there's narrow blue rectangle visible that indicates suggestions):
I'm able to reverse the widget's url and obtain correct suggestions. Everything's working fine but rendering - when I click on this narrow rectangle, correct value appears in the edit box. I collected all the static files. I'm working on Linux 20.04 LTS, Chrome 83.0.4103.106, python 3.7.7, django 3.0.3, bootstrap 4.5.0 and font-awesome 4.7.0. Here is the code (except settings) that I wrote
forms.py
class OpinionForm(forms.ModelForm):
# user field is defined as hidden and disabled such that it couldn't be changed
user = forms.ModelChoiceField(
widget=forms.HiddenInput,
queryset=get_user_model().objects.all(),
disabled=True
)
# defining all the fields with autocomplete feature
country = forms.ModelChoiceField(
queryset=Country.objects.all(),
widget=autocomplete.ModelSelect2(url="country_autocomplete")
)
class Meta:
model = Opinion
fields = ("content",)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields["country"].label = "Kraj"
self.fields["content"].label = "Treść"
models.py
class Country(models.Model):
name = models.CharField(max_length=200, null=False)
def __str__(self):
return self.name
class Opinion(models.Model):
user = models.ForeignKey(to=settings.AUTH_USER_MODEL)
content = models.TextField()
created = models.DateTimeField(auto_now=True)
urls.py
urlpatterns = [
path("add_opinion", views.CreateOpinionView.as_view(), name="add_opinion"),
path("country-autocomplete/", CountryAutocomplete.as_view(create_field="name"),
]
views.py
class CountryAutocomplete(autocomplete.Select2QuerySetView):
def get_queryset(self):
if not self.request.user.is_authenticated:
return Country.objects.none()
qs = Country.objects.all()
if self.q:
qs = qs.filter(name__istartswith=self.q)
return qs
class CreateOpinionView(LoginRequiredMixin, CreateView):
form_class = OpinionForm
template_name = "opinions/create.html"
def get_initial(self):
"""
When our view is instantiating the form, it will pass the result of get_initial() as the 'initial'
argument and the POST data as the 'data' argument
:return:
"""
return {
"user": self.request.user.id
}
def form_valid(self, form):
"""
If a form is valid, CreateView will call form_valid.
If the form isn't valid, CreateView will re-render the template.
:param form:
:return:
"""
action = self.request.POST.get("action")
# when we want to save, we will call the original form_valid method
# the original method saves the new opinion
if action == "SAVE":
# save and redirect as usual
return super().form_valid(form)
return HttpResponseBadRequest()
create.html
{% extends "base.html" %}
{% load static %}
{% load crispy_forms_tags %}
{% block title %}Dodaj opinię{% endblock %}
{% block content %}
<div class="col-md-12">
<h1>Dodaj opinię</h1>
<form method="post">
{{ form | crispy }}
{% csrf_token %}
<button class="btn btn-primary" type="submit" name="action" value="SAVE">Zapisz</button>
</form>
</div>
{% endblock %}
{% block footer %}
<script type="text/javascript" src="{% static "admin/js/vendor/jquery/jquery.js" %}"></script>
{{ form.media }}
{% endblock %}
Your code seems to work fine for me, so the error must be somewhere else, probably somewhere in your CSS.
Your code had two errors that prevented it from running though:
on_delete is a required argument for ForeignKey since Django 2.0. Are you sure about the versions you are running?
- user = models.ForeignKey(to=settings.AUTH_USER_MODEL)
+ user = models.ForeignKey(to=settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
I assume this one was a copy-paste error when creating the question
- path("country-autocomplete/", CountryAutocomplete.as_view(create_field="name"),
+ path("country-autocomplete/", CountryAutocomplete.as_view(create_field="name"), name='country_autocomplete'),
Here are additional details of my setup, perhaps you can find what is different from yours:
Versions:
Django==3.0.3
django-autocomplete-light==3.5.0
django-crispy-forms==1.9.1
base.html:
<!DOCTYPE html>
<html>
<head>
<title>{% block title %}{% endblock title %}</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css" integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO" crossorigin="anonymous">
</head>
<body>
{% block content %}
{% endblock %}
{% block footer %}
{% endblock %}
</body>
</html>
settings.py:
# ...
INSTALLED_APPS = [
# ...
'dal',
'dal_select2',
'crispy_forms',
'yourappname'
]
# ...
# (everything else left at defaults)