I am able to render list of all courses and list of topics corresponding to the courses in different templates.
I need help to view list of all courses and when each course is clicked,a new page should show the list of associated topics
models.py
class Topic(models.Model):
topic_name = models.CharField(max_length=200, null=True)
topic_file = models.FileField(upload_to = "topic_file", blank=True, null=True)
def __str__(self):
return self.topic_name
class Course(models.Model):
course_name = models.CharField(max_length=200, null=True)
course_image = models.ImageField(upload_to="images", blank=True, null=True)
related_topic = models.ManyToManyField(Topic)
def __str__(self):
return self.course_name
Views.py
def view_course(request):
course_list = Course.objects.all()
context = {'course_list':course_list}
return render(request, 'upskill/view_course.html',context)
def course_topic(request,pk):
course_topic_list = Course.objects.get(id=pk)
var = course_topic_list.related_topic.all
context = {'var':var}
return render(request, 'upskill/course_topic.html',context)
Here is how you could get the related topics inside of a template.
{% for course in course_list %}
... data
{% for topic in course.related_topic.all %}
...data
{% endfor %}
{% endfor %}
If you don't want to do a query every single iteration of the {{course}} loop, I recommend you this in your views:
course_list = Course.objects.all().prefetch_related('related_topic')
And for a single object:
def course_topic(request,pk):
course = Course.objects.prefetch_related('related_topic').get(id=pk)
context = {'course ':course }
return render(request, 'upskill/course_topic.html',context)
And then in the template:
{{course.data...}}
{% for topic in course.related_topic.all %}
...data
{% endfor %}
To only have topics:
def topic_view(request, pk)
topics = Topic.objects.filter(course__pk=pk) #The reverse name of Course model.
# You can set a related name the "related_topic" field.
# Then access the above filter with that related name.
.... data
context = {
"topics":topics
}
return render(request, 'template.html', context)
I am trying to add multiple images to a django model, it works in admin but i am completely blank on how to show it on my template. here is what i tried, it doesn't give any error but the image doesn't show.
models.py
class Export_Trade_Data(models.Model):
country = models.ForeignKey(
BannerandInformation, on_delete=models.CASCADE, null=True)
# country_name = models.CharField('Country Name', max_length=100, default='')
trade_text = models.TextField('Text for Trade Data', null=True)
# date added
date_added = models.DateTimeField('Date Updated', auto_now=True)
def __str__(self):
return "Export Trade Data"
class ExportImage(models.Model):
country = models.ForeignKey(
BannerandInformation, on_delete=models.CASCADE, null=True)
export_trade_data = models.ForeignKey(Export_Trade_Data, on_delete=models.CASCADE)
export_sample_image = models.ImageField(
'Export Sample Image', upload_to='country', null=True)
def __str__(self):
return "Export Trade Data"
views.py
def country(request, country):
exch_val = ''
banners = BannerandInformation.objects.filter(country=country)
for b in banners:
country_name = b
exch_r = b.exch_rate
exch_cur = exch_r.split("/")[0]
######Export_Trade_Data Model#######
etrds = Export_Trade_Data.objects.filter(country_id=ct_id)
######ExportImage Model#######
expics = ExportImage.objects.filter(country_id=ct_id)
for s in expics:
etd_pic = s.export_sample_image
content = {
"etrds": etrds,
"etd_pic": etd_pic,
}
return render(request, 'country/country-page.html', content)
country-page.html
<tbody>
{% for expics in etrds %}
<tr>
<img src="/country/{{expics.export_sample_image}}" height="604px" width="527px" alt="">
</tr>
{% endfor %}
</tbody>
As long as you have relevant ExportImage queryset in the context, you should be able to render it this way:
{% for ei in export_images %}
<img src="{{ei.export_sample_image.url}}">
{% endfor %}
However, seeing your view code it's unclear what you're trying to do there. I suggest you rewrite it. The part that should get your images would look like this:
def country(request, country_id):
export_images = ExportImage.objects.filter(country_id=country_id)
return render(request,
'country/country-page.html',
{'export_images': export_images})
Hi I'm new in Django and don't know how to get related objects with multiple models.
My code:
#models.py
class Candidate(models.Model):
user = models.OneToOneField(User, primary_key=True)
birth = models.CharField(max_length=50)
...
class CandidatePhotos(models.Model):
user = models.ForeignKey(User)
photo = models.ImageField(upload_to='usergallery/%Y/%m/%d')
class Job(models.Model):
candidate = models.ManyToManyField('Candidate', through='CandidateToJob')
title = models.CharField(max_length=500)
...
class CandidateToJob(models.Model):
job = models.ForeignKey(Job, related_name='applied_to')
candidate = models.ForeignKey(Candidate, related_name='from_user')
STATUS_CHOICES = (
('1', 'Not approved'),
('2', 'Approved'),
('3', 'Hired')
)
status = models.CharField(max_length=2, choices=STATUS_CHOICES)
In the views I have
#views.py
class CandidateDetails(generic.DetailView):
model = Candidate
template_name = 'dashboard/candidate.html'
def get_context_data(self, **kwargs):
context = super(CandidateDetails, self).get_context_data(**kwargs)
context['cand_photos'] = CandidatePhotos.objects.all()
return context
In the template I have
<h2>{{ candidate.user.first_name }} {{ candidate.user.last_name }}</h2>
{% for candidatephotos in cand_photos %}
<img alt="" src="{{ candidatephotos.photo.url }}" >
{% endfor %}
Here is my url dashboard/candidate/pk/.
What happens is in the template all users photos are loaded instead of only the specific user I want.
I have tried to get the user photos using {{ candidate.user.candidatephotos_set.photo.url }} but it doesn't work.
I have also tried to change in views.py the model from "Candidate" to "CandidateToJob" (the through model) but I get a 404 error, I don't know why.
So, what is the best practice to achieve this?
There's no need to query CandidatePhotos in the view at all. Your candidate object already had the relevant relationship, through User, so you can simply follow that:
{% for photo in object.user.candidatephotos_set.all %}
UPDATE: Just found out that the ManyToManyField is causing the admin interface to crash, when a specific album is selected. I commented them out, commented out all references to it, reran makemigrations and migrate, and now the admin interface works again...which leaves me even farther away from making this "favorite" column work :( See this followup: Why is Django ManyToManyField causing admin interface to crash? Why is no through table being created?
Background: My goal is to make the "Favorite?" column in this webpage reflect the favorite albums of the currently-logged-in user, where each is either "no" or "yes", and is a clickable link to toggle the choice. (When not logged in, they will all be grey "n/a"-s.)
Therefore, for each album, there may be exactly zero or one "has favorited" entry per user. If the entry exists, they've favorited it. If it doesn't exist, they didn't.
Here is my Album model, with the favorited_by_users many-to-many column (full models.py at the bottom):
class Album(models.Model):
OFFICIALITY = (
('J', 'Major studio release'),
('I', 'Non-major official release'),
('U', 'Unofficial'),
)
title = models.CharField(max_length=70)
description = models.TextField(max_length=500, default="", null=True, blank=True)
pub_date = models.DateField('release date')
officiality = models.CharField(max_length=1, choices=OFFICIALITY)
is_concert = models.BooleanField(default=False)
main_info_url = models.URLField(blank=False)
thumbnail = models.FileField(upload_to=get_upload_file_name, blank=True, null=True)
#virtual field to skip over the through table.
songs = models.ManyToManyField("Song", through="AlbumSong")
favorited_by_users = models.ManyToManyField(User)
def __str__(self):
return self.title
class Meta:
#Default ordering is by release date, ascending.
ordering = ['pub_date']
I originally had this FavoriteAlbum model, but since it has no extra information beyond the foreign keys, it was recommended that I eliminate it in favor of the above many-to-many column.
class FavoriteSongs(models.Model):
user = models.ForeignKey(User)
song = models.ForeignKey(Song)
class Meta:
unique_together = ('user', 'song',)
def __str__(self):
return "user=" + str(self.user) + ", song=" + str(self.song)
What I need to do is a "left join" between album and user, where all albums are selected, and any favorites by the currently-logged-in user are joined to it (None if they haven't favorited it). I don't know what to do.
I've also been told about the extra() function to do this join. The currently-working query in the view's get_queryset() is
return super(AlbumList, self).get_queryset().order_by("pub_date")
(Full views.py below.) My current guess is this:
return super(AlbumList, self).get_queryset().order_by("pub_date").extra(select={"is_favorite": "favorited_by_users__id = " + str(request.user.id) })
But, while this doesn't crash, the value of each {{ is_favorite }} in the template is nothing (the empty string). This makes sense since there's nothing in the database yet, but what now? I have no idea if this is the correct Django query.
I want to add an item in the database to test this, with a manual SQL statement in postgres (not via a Django command yet), but how and where do I do this?
I've successfully run makemigrations and then migrate with this new m2m column (and without the FavoriteSongs model), but I see nothing in the database that represents the is-favorite value. There's no extra column in billyjoel_album, and no through table akin to billyjoel_favoritealbum. So where/how is this data stored in the database?
(Any other advice regarding this extra "favorite" column would be appreciated as well!)
Thanks.
models.py
from django.db import models
from django.contrib.auth.models import User
from time import time
def get_upload_file_name(instance, filename):
return "uploaded_files/%s_%s" % (str(time()).replace(".", "_"), filename)
class Album(models.Model):
OFFICIALITY = (
('J', 'Major studio release'),
('I', 'Non-major official release'),
('U', 'Unofficial'),
)
title = models.CharField(max_length=70)
description = models.TextField(max_length=500, default="", null=True, blank=True)
pub_date = models.DateField('release date')
officiality = models.CharField(max_length=1, choices=OFFICIALITY)
is_concert = models.BooleanField(default=False)
main_info_url = models.URLField(blank=False)
thumbnail = models.FileField(upload_to=get_upload_file_name, blank=True, null=True)
#virtual field to skip over the through table.
songs = models.ManyToManyField("Song", through="AlbumSong")
favorited_by_users = models.ManyToManyField(User)
def __str__(self):
return self.title
class Meta:
#Default ordering is by release date, ascending.
ordering = ['pub_date']
class Song(models.Model):
name = models.CharField(max_length=100)
description = models.TextField(max_length=500, default="", null=True, blank=True)
length_seconds = models.IntegerField()
lyrics_url = models.URLField(default="", blank=True, null=True)
albums = models.ManyToManyField("Album", through="AlbumSong")
favorited_by_users = models.ManyToManyField(User)
def get_length_desc_from_seconds(self):
if(self.length_seconds == -1):
return "-1"
m, s = divmod(self.length_seconds, 60)
h, m = divmod(m, 60)
if(h):
return "%d:%02d:%02d" % (h, m, s)
else:
return "%d:%02d" % (m, s)
def __str__(self):
return self.name
class AlbumSong(models.Model):
song = models.ForeignKey(Song)
album = models.ForeignKey(Album)
sequence_num = models.IntegerField()
class Meta:
unique_together = ('album', 'sequence_num',)
unique_together = ('album', 'song',)
def __str__(self):
return str(self.album) + ": " + str(self.sequence_num) + ": " + str(self.song)
views.py
from .models import Album, Song, AlbumSong
from datetime import datetime, timedelta
from django.core.context_processors import csrf
from django.shortcuts import render, render_to_response
from django.views.generic import DetailView, ListView
from enum import Enum
def get_str_with_appended(string, between_if_str_non_empty, new_value):
if(len(string) == 0):
return new_value
else:
return string + between_if_str_non_empty + new_value
class PrependQuestionMark(Enum):
YES, NO = range(2)
def get_url_param_string_from_params(prepend_question_mark=PrependQuestionMark.YES, **kwargs_all_params):
param_list = ""
for key in iter(kwargs_all_params):
value = kwargs_all_params[key]
if(value is not None):
param_list = get_str_with_appended(param_list, '&', str(key) + "=" + str(value))
if(len(param_list) == 0):
return param_list;
if(prepend_question_mark == PrependQuestionMark.YES):
return "?" + param_list
else:
return param_list
class AlbumList(ListView):
model = Album
context_object_name = "albums"
#Derived from irc/#dango/tbaxter...START
def dispatch(self, request, *args, **kwargs):
#default to asc
self.sort_order = request.GET.get("sort_order", None)
self.sort_item = request.GET.get("sort_item", None)
self.csrf_token = csrf(request)["csrf_token"]
self.logged_in_user = request.user
#self.csrf_token = request.GET.get("csrf_token", None)
return super(AlbumList, self).dispatch(request, *args, **kwargs)
def get_queryset(self):
#Item zero in both is the default
#should be static global
asc_desc_list = ["asc", "dsc"]
sort_by_types = ["pub_date", "title"]
if(self.sort_order is None and self.sort_item is None):
#Use default ordering
return super(AlbumList, self).get_queryset()
#Custom ordering requested
sort_order = self.sort_order
sort_item = self.sort_item
if(sort_order is None or
sort_order not in asc_desc_list):
sort_order = asc_desc_list[0]
if(sort_item is None or
sort_item not in sort_by_types):
sort_item = sort_by_types[0]
order_minus = "" if sort_order == "asc" else "-"
return super(AlbumList, self).get_queryset().order_by(order_minus + sort_item).extra(select={"is_favorite": "favorited_by_users__id = " + str(self.logged_in_user.id) })
def get_context_data(self, **kwargs):
context = super(AlbumList, self).get_context_data(**kwargs)
context["sort_order"] = self.sort_order
context["sort_item"] = self.sort_item
context["url_params"] = get_url_param_string_from_params(
sort_item=self.sort_item,
sort_order=self.sort_order,
csrf_token=self.csrf_token)
return context
class AlbumDetail(DetailView):
model = Album
context_object_name = "album"
def dispatch(self, request, *args, **kwargs):
#default to asc
self.sort_order = request.GET.get("sort_order", None)
self.sort_item = request.GET.get("sort_item", None)
self.csrf_token = csrf(request)["csrf_token"]
return super(AlbumDetail, self).dispatch(request, *args, **kwargs)
def get_context_data(self, **kwargs):
#Call the base implementation first to get a context
context = super(AlbumDetail, self).get_context_data(**kwargs)
#Add in the required extra info: album_songs, ordered by
#sequence_num
#select_related is to query the database for all songs at once, here
#in the view, to prevent the template from pin-pricking the database
#in each for loop iteration. For large datasets, this is critical.
context['album_songs'] = kwargs["object"].albumsong_set.order_by('sequence_num').select_related("song")
context["url_params"] = get_url_param_string_from_params(
sort_item=self.sort_item,
sort_order=self.sort_order,
csrf_token=self.csrf_token)
return context
album_list.html
{% extends "base.html" %}
{% load bj_filters %}
{% block title %}Billy Joel Album Browser{% endblock %}
{% block sidebar %}
<UL>
<LI>All albums</LI>
<LI>Admin</LI>
</UL>
{% endblock %}
{% block content %}
<TABLE ALIGN="center" WIDTH="100%" BORDER="1" CELLSPACING="0" CELLPADDING="4" BGCOLOR="#EEEEEE"><TR ALIGN="center" VALIGN="middle">
{% if user.is_authenticated %}
<TD>My profile (Logout)</TD>
{% else %}
<TD>Login to view your favorites</TD>
{% endif %}
</TR></TABLE>
<H1>Billy Joel Album Browser</H1>
<!--
<P>url_params={{ url_params }}</P>
-->
{% if albums.count > 0 %}
<P>Officiality: <IMG SRC="/static/images/major.jpg" height="20"/>=Major studio release, <IMG SRC="/static/images/minor.jpg" height="20"/>=Official release, <IMG SRC="/static/images/unofficial.jpg" height="20"/>=Unofficial</P>
<TABLE ALIGN="center" WIDTH="100%" BORDER="1" CELLSPACING="0" CELLPADDING="4" BGCOLOR="#EEEEEE"><TR ALIGN="center" VALIGN="middle">
<TD><B><U><a href="{% url 'album_list' %}?sort_item=title&sort_order=
{% if sort_item == 'pub_date' %}asc{% else %}
{{ sort_order|multival_to_str:'asc,dsc->dsc,asc,dsc' }}
{% endif %}
&csrf_token={{ csrf_token }}">Title</A></U></B><BR><I><FONT SIZE="-1">(click a title to view its song list)</FONT></I></TD>
<TD><B><U><a href="{% url 'album_list' %}?sort_item=pub_date&sort_order=
{% if sort_item == 'title' %}asc{% else %}
{{ sort_order|multival_to_str:'asc,dsc->dsc,asc,dsc' }}
{% endif %}
&csrf_token={{ csrf_token }}">Released</A></U></B></TD>
<TD>Officiality</TD>
<TD>Concert</TD>
<TD>Wiki</TD>
<TD>Favorite?</TD>
{% for album in albums %} <!-- No colon after "albums" -->
</TR><TR>
<TD VALIGN="top">
{% if album.thumbnail %}
<img src="/static/{{ album.thumbnail }}" width="25"/>
{% else %}
<img src="/static/images/white_block.jpg" width="25"/>
{% endif %}
{{ album.title }}
{% if album.description %}
<BR/><FONT SIZE="-1"><I>{{ album.description|truncatewords:10 }}</I></FONT>
{% endif %}
<TD>{{ album.pub_date|date:"m/y" }}</TD>
<TD><IMG SRC="/static/images/{{ album.officiality|multival_to_str:"J,I,U->major,minor,unofficial,broken_image"}}.jpg" height="20"/></TD>
<TD>{{ album.is_concert|yesno:"Yes,No" }}</TD>
<TD>Wiki</TD>
<TD><I>n/a {{ is_favorite }}</I></TD>
{% endfor %}
</TR></TABLE>
{% else %}
<P><I>There are no albums in the database.</I></P>
{% endif %}
{% endblock %}
M2M relationships are brand-new tables created. They get an unique name, and have two foreign keys. A composite key is created so the direct and the related model combinations are unique.
When you do:
class Topping(models.Model):
name = ...
class Pizza(models.Model):
name = ...
toppings = models.ManyToManyField(Topping, related_name="pizzas")
#not including a related_name will generate a "pizza_set" related name.
A new table appears describing the relationship, with an internal name. This table has a pizza_id and a topping_id foreign key, and a composite unique key including both fields. You cannot, and should not, predict the name of such table.
On the other side, if you want to access the relationship and, possible, declare more fields, you can:
class Topping(models.Model):
name = ...
class Pizza(models.Model):
name = ...
toppings = models.ManyToManyField(Topping, related_name="pizzas", through="PizzaAndTopping")
#not including a related_name will generate a "pizza_set" related name.
class PizzaAndTopping(models.Model):
more_data = models.TextField()
pizza = models.ForeignKey(Pizza, null=False)
topping = models.ForeignKey(Topping, null=False)
class Meta:
unique_together = (('pizza','topping'),)
Notice how I added a through parameter. Now you have control of the middle-table BUT you cannot add or delete models from the relationship. This means, with this approach you cannot:
Pizza.objects.get(pk=1).toppings.append(Topping.objects.get(pk=2))
Nor you can remove, nor you can do these operations in the Topping side of the life.
If you want to add or delete toppin-pizza relationships, you must do it directly in the PizzaAndTopping relationship.
If you want to know whether the current user has marked any song as their favorite, you should prefetch the relationship. In Django 1.7, you can prefetch a M2M related field using a custom filter: you can fetch all the albums, and only a query getting the current user, using a Prefetch object. See the official docs about prefetching here.
Antoher solution would involve:
fetching the current user's favorite albums list: ulist = user.album_set.all()
fetching the current page of alboms: _list = Album.objects.all()[0:20]
fetch values of user albums: ulist = ulist.values_list('id', flat=True)
[1, 2, 4, 5, 10, ...] #you'll get a list of ids
when iterating over each album in the page, you test currentAlbum.id in ulist and print a different message (either yes or no).
The many-to-many field is represented in the database in exactly the same way as your original FavouriteSongs model - as a linking table with ForeignKeys to both Song and User. The only benefit of getting rid of FavouriteSongs is that you're now using an automatically-defined through table, rather than a manual one.
I don't understand your example query, since you don't say what model you are actually calling it on, or what self.logged_in_user is. However, you can't use extra like this: you are trying to put Django query syntax there, complete with double-underscore names to traverse relationships, but extra is passed directly to the SQL, and that doesn't know anything about that syntax.
I would not attempt to do this in one query. Instead I would do two queries, one to get all the albums and one to get the user's favourites. get_queryset would just return the full album list, and then you can use get_context_data to get an additional set of objects representing the IDs of the favourites:
favorites = self.logged_in_user.album_set.all().values_list('id', flat=True)
context['favorites'] = set(favorites)
The values_list just gets the IDs of the albums only, since that's all we need, and we then put them into a set to make lookups quicker.
Now, in the template, you can just do:
{% for album in albums %}
...
<td>{% if album.id in favorites %}Yes{% else %}No{% endif %}</td>
{% endfor %}
I have the following model:
class Student(models.Model):
name = models.CharField(max_length=50)
surname = models.CharField(max_length=50)
city = models.CharField(max_length=50)
country = models.CharField(max_length=50)
skypeid = models.CharField(max_length=100)
email = models.EmailField(max_length=75)
youtubechannel = models.URLField(max_length=200)
def __unicode__(self):
return self.surname
class LanguageKnowledge(models.Model):
LOW = 'LOW'
MEDIUM = 'MED'
ADVANCED = 'ADV'
EXPERT = 'EXP'
LANGUAGE_KNOWLEDGE_CHOICES =(
(LOW, 'LOW'),
(MEDIUM, 'Medium'),
(ADVANCED,'Advanced'),
(EXPERT, 'Expert'),
)
student = models.ForeignKey(Student)
language = models.ForeignKey(Language)
grammar = models.CharField(max_length=3, choices=LANGUAGE_KNOWLEDGE_CHOICES)
reading = models.CharField(max_length=3, choices=LANGUAGE_KNOWLEDGE_CHOICES)
speaking = models.CharField(max_length=3, choices=LANGUAGE_KNOWLEDGE_CHOICES)
writing = models.CharField(max_length=3, choices=LANGUAGE_KNOWLEDGE_CHOICES)
def __unicode__(self):
return u'%s : %s' % (self.student, self.language)
Then the following view
def student(request, student_id):
try:
studentdetails = Student.objects.get(pk=student_id)
languageknowledge_list = LanguageKnowledge.objects.filter(student__id__exact=student_id)
except Student.DoesNotExist:
raise Http404
return render(request, 'broker/student.html',
{'studentdetails': studentdetails,
'languageknowledge_list': languageknowledge_list})
However the following template does not list the different languages that are present in languageknowledge_list. As a more general question, how can I list, via template, a query set involving one to many relationships (e.g. a student speaking several languages) ?
{% block content %}
<ul>
{% for languageknowlege in languageknowledge_list %}
<li>{{ languageknowledge.language }}, h2 got it,</li>
{% endfor %}
</ul>
{% endblock %}
You have misspelling in your template for languageknowledge variable.
Change this line:
{% for languageknowlege in languageknowledge_list %}
<li>{{ languageknowledge.language }}, h2 got it,</li>
To:
{% for languageknowledge in languageknowledge_list %}
<li>{{ languageknowledge.language }}, h2 got it,</li>
Note that it's better to using good ide that at least have spell checker, also separate your name of variables by _ when are combinatorial, for example using language_knowledge instead of languageknowledge to help spell checker to better detect typo.