Django: how to update model attribute through a view? - python

I have albums and songs. When at specific album page I want to add a song by clicking "Add Song" button and return to that album's page:
Album's page
The from opens fine and lets me input song title.
But when I click "Submit" it breaks with this exception:
Exception
Here is a portion of template code where form is called:
{% block body %}
<div class="container-fluid" style="position:relative; padding-left: 100px; padding-top: 50px;">
<img src="{{ album.album_logo.url }}" class="img-responsive" height="200" width="200">
<h1>{{ album.album_title }}</h1>
<h3>{{ album.artist }}</h3>
{% for song in album.song_set.all %}
{{ song.song_title }}
{% if song.is_favorite %}
<img src="http://vignette1.wikia.nocookie.net/defendersquest/images/1/17/Yellow_star_small.png/revision/latest?cb=20120430053933"
height="10" width="10"/>
{% endif %}
<br>
{% endfor %}
<a href="{% url 'music:song-add' album.pk %}">
<span class="glyphicon glyphicon-plus" aria-hidden="true"></span> Add Song
</a>
</div>
{% endblock %}
Here is urls.py:
urlpatterns = [
# /music/
url(r'^$', views.IndexView.as_view(), name='index'), # "$" sign means nothing more was requested from user
# /music/<album_id>/ - here album_id is an ID of specific Album for example
url(r'^(?P<pk>[0-9]+)/$', views.DetailView.as_view(), name='detail'), # this will look for numbers 0-9 in user request
#and map it to variable named 'album_id'. Plus sign emplies to look for one or more numbers
#^-sign implies begingin of reg. expression and $-implies the end of it.
# /music/album/add/
url(r'album/add/$', views.AlbumCreate.as_view(), name='album-add'),
# /music/album/add/
url(r'song/add/(?P<album_pk>[0-9]+)/$', views.SongCreate.as_view(), name='song-add'),
# /music/album/album_id/
url(r'album/(?P<pk>[0-9]+)/$', views.AlbumUpdate.as_view(), name='album-update'),
# /music/album/album_id/delete/
url(r'album/(?P<pk>[0-9]+)/delete/$', views.AlbumDelete.as_view(), name='album-delete')
]
Here is views.py portion:
class SongCreate(CreateView):
model = Song
fields = ['song_title']
def form_valid(self, form):
form.instance.album.pk = self.request.GET.get('album_pk')
return super(SongCreate, self).form_valid(form)
and here is models.py:
class Album(models.Model):
# attributes/columns:
artist = models.CharField(max_length=250) #CharField is the info on type of info that var holds
album_title = models.CharField(max_length=500)
genre = models.CharField(max_length=100)
album_logo = models.FileField()
def get_absolute_url(self):
return reverse('music:detail', kwargs={'pk': self.pk})
def __str__(self): #string representation of Album objects
return self.album_title + ' - ' + self.artist
class Song(models.Model):
#attributes/columns:
album = models.ForeignKey(Album, on_delete=models.CASCADE) #linking songs to Albums!!!
file_type = models.CharField(max_length=10)
song_title = models.CharField(max_length=250)
is_favorite = models.BooleanField(default=False)
def get_absolute_url(self):
return reverse('music:detail', kwargs={'pk': self.album.pk})
def __str__(self):
return self.song_title

You're not setting the related ForeignKey object correctly. You can access the pk of the related field using the hidden _id field of the related object in the model instance:
form.instance.album_id = self.request.GET.get('album_pk')

Related

ValueError at /category/economy/: Field 'id' expected a number but got 'economy'

I try to add new path and this happen "Field 'id' expected a number but got 'economy'."
in traceback the highlighted line is in the views.py file which i mentioned below.
category_posts = Post.objects.filter(category=cats)
I am sharing my files plz help me to get rid of the issue.
urls.py
urlpatterns = [
path('',views.allpost,name="allpost"),
path('search', views.search, name="search"),
path('contact/', views.contact, name="contact"),
path('success/', views.successView, name="success"),
path('category/<str:cats>/', views.CategoryView, name ="category"),
path('<int:blog_id>/',views.detail,name="detail"),
] + static(settings.MEDIA_URL,document_root = settings.MEDIA_ROOT)
here i used str:cats, yet it shows "Field 'id' expected a number but got 'economy'."
views.py
def CategoryView(request, cats): # here cats is same which mentioned in dynamic url.
category_posts = Post.objects.filter(category=cats)
return render(request, 'categories.html', {'cats':cats.title(), 'category_posts':category_posts})
"category_posts = Post.objects.filter(category=cats)" this line of code shows in traceback
models.py
from django.db import models
class Category(models.Model):
created_at = models.DateTimeField(auto_now_add=True, verbose_name="Created at")
title = models.CharField(max_length=255, verbose_name="Title")
parent = models.ForeignKey('self', related_name='children', on_delete=models.CASCADE, blank=
True, null=True)
class Meta:
verbose_name = "Category"
verbose_name_plural = "Categories"
ordering = ['title']
def __str__(self):
return self.title
class Post(models.Model):
title = models.CharField(max_length=100)
public_date = models.DateField(null=True)
public_time = models.TimeField(null=True,default="")
category = models.ForeignKey(Category, on_delete=models.CASCADE, verbose_name="Category", null=True)
image = models.ImageField(upload_to='images/',null=True, blank=True)
body = models.TextField()
class Meta:
verbose_name = "Post"
verbose_name_plural = "Posts"
ordering = ['public_date']
def summary(self):
return self.body[:100]
def pub_date(self):
return self.public_date.strftime('%b %e,%y')
# to give layout for time and date
def __str__(self):
return self.title
categories.html
{% extends 'base.html' %}
{%block content%}
<h1> Category: {{ cats }} </h1>
{% for post in category_posts %}
<div class="container mt-3">
<div class="row mb-2">
<div class="col-md-6">
<div class="card flex-md-row mb-4 box-shadow h-md-250">
<div class="card-body d-flex flex-column align-items-start">
<strong class="d-inline-block mb-2 text-primary">{{ post.category }}</strong>
<h3 class="mb-0">
<a class="text-dark" href="{% url 'detail' post.id %}">{{post.title}}</a>
</h3>
<div class="mb-1 text-muted">{{ post.public_date }}</div>
<p class="card-text mb-auto">{{ post.summary }}</p>
Continue reading
</div>
<img class="card-img-right flex-auto d-none d-md-block" data-src="holder.js/200x250?theme=thumb" alt="Thumbnail [200x250]" style="width: 200px; height: 250px;" src="data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%22200%22%20height%3D%22250%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%20200%20250%22%20preserveAspectRatio%3D%22none%22%3E%3Cdefs%3E%3Cstyle%20type%3D%22text%2Fcss%22%3E%23holder_182c981dfc3%20text%20%7B%20fill%3A%23eceeef%3Bfont-weight%3Abold%3Bfont-family%3AArial%2C%20Helvetica%2C%20Open%20Sans%2C%20sans-serif%2C%20monospace%3Bfont-size%3A13pt%20%7D%20%3C%2Fstyle%3E%3C%2Fdefs%3E%3Cg%20id%3D%22holder_182c981dfc3%22%3E%3Crect%20width%3D%22200%22%20height%3D%22250%22%20fill%3D%22%2355595c%22%3E%3C%2Frect%3E%3Cg%3E%3Ctext%20x%3D%2256.20000076293945%22%20y%3D%22131%22%3EThumbnail%3C%2Ftext%3E%3C%2Fg%3E%3C%2Fg%3E%3C%2Fsvg%3E" data-holder-rendered="true">
</div>
</div>
</div>
</div>
{% endfor %}
{% else %}
<h2>Sorry this page does not exist....</h2>
{% endif %}
{%endblock%}
I am confused it demands. can someone help me to solve it plz.
Its because you are querying it wrong:
So instaed of doing this:
# views.py
def CategoryView(request, cats): # here cats is same which mentioned in dynamic url.
category_posts = Post.objects.filter(category=cats)
return render(request, 'categories.html', {'cats':cats.title(), 'category_posts':category_posts})
Try something with this query. I'm supposing you want to query all the posts of a specific category that will be coming from your URL.
from django.shortcuts import get_object_or_404
def CategoryView(request, cats):
# method 1
category_posts = Post.objects.filter(category__title=cats)
# method 2
category = Category.objects.get(title=cats)
category_posts = category.Category.all() # here .Category is the related_name you used in your Post model
# method 3:
category = get_object_or_404(Category, title=cats) # will raise 404 if no category found with the given title
category_posts = category.Category.all()
return render(request, 'categories.html', {'cats':cats.title(), 'category_posts':category_posts})
PS: When you don't know what your ForeignKey related_name should be. Then go for the plural name of the model. Like in the current case:
# models.py
category = models.ForeignKey(Category, on_delete=models.CASCADE, verbose_name="posts", null=True)
This way we can query like this category_posts = category.posts.all()

Get objects assigned to ForeignKey

to be honest im not pretty sure how to explain this but i want to display all objects assigned to foreignkey when im in one of this objects
MODELS.PY
class Category(models.Model):
def __str__(self):
return self.name
...
name = models.CharField(max_length=267)
CategoryThumbnail = models.CharField(max_length=300)
class Video(models.Model):
def __str__(self):
return self.name
...
Category = models.ForeignKey(Category, on_delete=models.CASCADE, null=True)
nazwa = models.CharField(max_length=267)
embedlink = models.CharField(blank=True, max_length=1000)
thumbnail = models.CharField(max_length=300, blank=True)
VIEWS.PY
def Category(request, slug):
category_user = Category.objects.get(slug=slug)
category_video = Video.objects.filter(Category = category_user)
categories = Category.objects.all()
dict = {'category_user' : category_user, 'category_video' : category_video, 'categories' : categories}
return render(request, 'categories.html', dict)
def video(request, id):
video_user = Video.objects.get(pk=id)
videos = Video.objects.all()
categories = Category.objects.all()
dict = {'video_user' : video_user, 'categories' : categories, 'videos': videos}
return render(request, 'video.html', dict)
TEMPLATE categories.html
{% for video in category_video %} {% if category_video %}
<div class="flex-container">
<div class="between">
<img loading="lazy" id="resize" src="{{ video.thumbnail }}.png" class="miniimg">
<figcaption class="caption"><a style="color: #ffffff; font-size: 17px; font-weight: bold;" href="/video/{{ video.id }}"> {{ video.name }} </a></figcaption>
</div>
</div>
{% endif %} {% endfor %}
To this moment everything works but when i want to display objects from video in category
but when im trying to display Video Objects that are assigned to the Category in Video Template it doesnt work no matter what i do. I'm trying to do it about half a year and no results my video.html changed a lot of times to make it so ill not put code from here. I'll include photo to better understand(rectangles are same videos). To sum up I want to display same elements assigned to foreignkey in category and when i open element.
IMAGE DESCRIPTION
1.Is on the Category HTML
2.On Video HTML
I strongly advise you to back to the document to read about ForeignKey: https://docs.djangoproject.com/en/4.0/topics/db/examples/many_to_one/
anyway, you can follow that:
#views.py
def Category(request, slug):
category_user = Category.objects.get(slug=slug)
category_video = Video.objects.filter(category=category_user)
categories = Category.objects.all()
dict = {'category_user' : category_user, 'category_video' : category_video, 'categories' : categories}
return render(request, 'categories.html', dict)
I just modified the capital of the "c" character in the filter of video model
#html
{% for video in category_video %}
{% for cats in video.category_set.all %}
{{ cats.<fieldname> }}
{% endfor %}
{% endfor %}

get_object_or_404 only for DetailView

Im new to programming, im working on a app to study a given topic, and after I read the topic, im showing some flashcards, but I want to show only the flashcards related to the topic, but I always get more then one Deck of flashcards, probably because I'm not getting the correct deck_id. Here is the code:
models.py:
class Topic(models.Model):
author = models.ForeignKey(
User, related_name="topic", on_delete=models.CASCADE, null=True)
title = models.CharField(max_length=100)
body = RichTextUploadingField(blank=True, null=True)
created_at = models.DateTimeField(auto_now_add=True)
slug = models.SlugField(max_length=120)
class Meta:
ordering = ["title"]
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('topic:topic-detail', kwargs={
"topic_slug": self.slug,})
class Deck(models.Model):
deckTopic = models.ForeignKey(
Topic, null=True, blank=True, on_delete=models.CASCADE)
description = models.CharField(max_length=510, null=False, blank=True)
is_active = models.BooleanField(default=False)
def __str__(self):
return self.description
def get_number_of_cards(self):
'''
Returns the number of cards in the decks related card_set
'''
return self.card_set.count()
get_number_of_cards.short_description = 'Card Count'
class Card(models.Model):
parentDeck = models.ForeignKey(Deck, on_delete=models.CASCADE)
front = models.TextField()
back = models.TextField()
def __str__(self):
return self.front
def has_prev_card(self):
'''
Returns true if card is not thee first card in the deck.
'''
first_card_in_deck = self.parentDeck.card_set.first()
if self == first_card_in_deck:
return False
return True
def get_prev_card(self):
'''
Return previous card in deck
'''
return self.parentDeck.card_set.filter(id__lt=self.id).last()
def has_next_card(self):
'''
Returns true if card is not the last card in the deck.
'''
last_card_in_deck = self.parentDeck.card_set.last()
if self == last_card_in_deck:
return False
return True
def get_next_card(self):
'''
Return next card in deck
'''
return self.parentDeck.card_set.filter(id__gt=self.id).first()
views:
class TopicDetailView(DetailView):
model = Topic
def get_context_data(self, *args, **kwargs):
context = super(TopicDetailView,
self).get_context_data(*args, **kwargs)
# Here is where i want to get the Deck related to the topic, but get more then one:
deck_obj = get_object_or_404(Deck)
card_list = deck_obj.card_set.all()
card_obj = card_list.first()
if self.request.method == 'GET' and 'card' in self.request.GET:
card_obj = get_object_or_404(Card, id=self.request.GET['card'])
context['deck_obj'] = deck_obj
context['card_obj'] = card_obj
return context
topic_detail.html:
{% extends 'base.html' %}
{% block content %}
<div class="topic-title">
<h5 class="mb-0">
{{object.title}}
</h5>
</div>
<div class="topic-body">
<p class="mb-0">
{{object.body}}
</p>
</div>
<div class="topic-deck">
{% if card_obj %}
<div class="notecard">
<div class="notecard-nav text-center">
{% if card_obj.has_prev_card %}
<a href="{% url 'topic:viewDeck' deck_obj.id %}?card=
{{card_obj.get_prev_card.id}}">Prev</a>
{% endif %}
{% if card_obj.has_next_card %}
<a href="{% url 'topic:viewDeck' deck_obj.id %}?card=
{{card_obj.get_next_card.id}}">Next</a>
{% endif %}
</div>
<div class="notecard-front">
<p class="text-center">Front</p>
<p>{{card_obj.front}}</p>
</div>
<div class="notecard-back">
<p class="text-center">Back</p>
<p>{{card_obj.back}}</p>
</div>
</div>
{% else %}
<p>No card found.</p>
{% endif %}
</div>
{% endblock %}
urls.py:
app_name = 'topic'
urlpatterns = [
path('topic/<slug:topic_slug>/<deck_id>/',
TopicDetailView.as_view(), name='viewDeck'),
]
How can I show only the Deck related to de Topic detail?
Your call to get_object_or_404 is incomplete. You've provided the model class, but not the parameters to get the object with:
deck_obj = get_object_or_404(Deck, pk=self.kwargs['deck_id'])
self.kwargs, contains all keyword arguments from the url. In your case it will contain topic_slug and deck_id.
pk is a shortcut for whatever the primary key is on your model. This makes me not think about how I named it and is resistant to future name changes of the primary key field.
I don't have to worry about deck_id not being in kwargs, because if it's not the URL won't match and the view isn't called.
And there it is :)

Why do I get either NoReverseMatch or FieldError

I am working on a project, and want to create a report on a film. (That it doesn´t exist) At the moment I only get error when I try to go to either film// or film//report. Can anybody help me?
When I try film//: NoReverseMatch; Reverse for 'film-report' with no arguments not found. 1 pattern(s) tried: ['film/(?P[0-9]+)/report$']
And film//report gives: FieldError; Unknown field(s) (reported) specified for Report
models.py
class Report(models.Model):
title = models.CharField(default="", max_length=100)
comment = models.TextField(default="")
reporter = models.ForeignKey(User, on_delete=models.CASCADE, related_name="Reporter")
# receiver = models.ForeignKey(
# User, on_delete=models.CASCADE, related_name="Receiver"
# )
reports = models.ForeignKey(Film, on_delete=models.CASCADE)
def __str__(self): # pragma: no cover
return f"{self.reporter.username} reports {self.reports.title}"
urls.py
urlpatterns = [
path("", views.films_view, name="board-home"),
path("film/add", FilmAddView.as_view(), name="film-add"),
path("film/<int:pk>/", FilmDetailView.as_view(), name="film-detail"),
path("film/<int:pk>/report", FilmReport.as_view(), name="film-report")
]
views.py
class FilmReport(LoginRequiredMixin, UpdateView):
model = Report
fields = ["title", "reported"]
# def __str__(self):
# return self.title
def form_valid(self, form):
form.instance.author = self.request.user
return super().form_valid(form)
This should be the page where I can click on "Report", and then be redirected to a report page.
film_detail.html
{% extends "board/base.html" %}
{% block content %}
<article class="media content-section">
<img class="rounded-circle film-img" src="/media/{{object.poster}}">
<!-- Mulighet for å ha en "add review"-knapp på siden der hvor filmene vises. -->
<i class="material-icons right">rate_review</i>add review
<i class="material-icons right">report</i>report
<!-- <a onclick="myFunction()" class="waves-effect waves-light red darken-4 btn"><i class="material-icons right">report</i>report</a>
<script>
function myFunction() {
var txt;
if (confirm("Are you sure you want to report this film?")) {
$("#inline2").fadeIn(300);
$(".overlay-fixed").fadeIn(300);
$(".fancybox-opened").fadeIn(300);
return false;
} else {}
}
</script> -->
<div class="media-body">
<h2 class="film-title">{{ object.title }}</h2>
<p class="film-plot">{{ object.plot }}</p>
</div>
</article>
{% endblock content %}
In your class FilmReport you can just take the fileds from your model Report.
The field "reported" does not exist. You should take "reporter" or "reports".
You can just try the urls you have specified, either film nor film/report are valid urls. You need to have at least one Film object in your database to get access to the url
film/1/report.

How to have my users add song to an album in django

I am trying to be able to add songs through a form but keep getting
NOT NULL constraint failed: post_song.album_id.
As of right now I can only add songs ramdomly and they aren't connected to the original album.
from django.db import models
from django.core.urlresolvers import reverse
from django.conf import settings
from django.contrib.auth.models import User
class Album(models.Model):
creator = models.CharField(max_length=250)
album_name = models.CharField(max_length=250)
album_photo = models.FileField()
author = models.ForeignKey(User, blank=True, null=True, related_name ='album_post')
category = models.ManyToManyField(Category)
def get_absolute_url(self):
return reverse('post:detail', kwargs={'pk': self.pk})
def __str__(self):
return self.creator + ' - ' + self.album_name
class Song(models.Model):
album = models.ForeignKey(Album, on_delete=models.CASCADE, null=True)
song_name= models.CharField(max_length=1000)
def __str__(self):
return self.song_name
def get_absolute_url(self):
return reverse('post:detail', kwargs={'pk': self.pk})
views.py
class SongAdd(CreateView):
model = Song
fields = ['song_name']
def form_valid(self, form):
form.instance.album_id = self.request.GET.get('album_pk')
return super(SongAdd, self).form_valid(form)
details page
{% block body %}
<div class="container-fluid">
<div class="row">
<div class="col-sm-12 col-md-7">
<div class="panel panel-default">
<div class="panel-body">
<form class="form-horizontal" action="" method="post" enctype="multipart/form-data">
{% csrf_token %}
{% include 'post/form-template.html' %}
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<button type="submit" class="btn btn-success">Submit</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
{% endblock %}
In your view, you are missing the album field. The form does not know what album you want to associate the song to.
Try this:
views.py
class SongAdd(CreateView):
model = Song
fields = ['song_name']
def form_valid(self, form):
album = Album.objects.get(pk=album_id_variable)
form.instance.album = album
return super(SongAdd, self).form_valid(form)
Make sure album_id_variable is a valid album id.

Categories