Get objects assigned to ForeignKey - python

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

Related

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 :)

how to get unique values in django

I have a category model and list of posts related to those category also some post with same category name but when i wanted to make list of category section in template,
it showing duplicate name of category as it related to posts like:
food,
food,
desert,
style,
desert,
but I want like:
food,
desert,
style,
here is my code:
views.py
class ListCategory(ListView):
model = Post
paginate_by = 2
template_name = 'shit.html'
context_object_name = 'queryset'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
cate = Post.objects.all()
context['cate'] = cate
return context
models.py
class Category(models.Model):
title = models.CharField(max_length=20)
thumbnail = models.ImageField()
detail = models.TextField()
featured = models.BooleanField(default=True)
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('post-category', kwargs={
'pk': self.pk
})
class Post(models.Model):
title = models.CharField(max_length=100)
overview = models.TextField()
featured = models.BooleanField(default=True)
timestamp = models.DateTimeField(auto_now_add=True)
user = models.ForeignKey(Author,on_delete=models.CASCADE)
thumbnail = models.ImageField()
category = models.ForeignKey(Category, on_delete=models.CASCADE)
tags = TaggableManager()
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('post-detail', kwargs={
'pk': self.pk
})
templates
{% extends "base.html" %}
{% load static %}
{% block content %}
<div class="sidebar-box ftco-animate">
<ul class="categories">
<h3 class="heading mb-4">Categories</h3>
{% for cat in cate %}
<li>{{cat.category}}<span>(12)</span></li>
{% endfor %}
</ul>
</div>
{% endblock content %}
Thank you so much!
Seems like you want to group your Posts, based on their category; so you can achieve that by iterating over the Category (instead of Post), and use the backward relationship to find out the related Post objects.
views.py
class ListCategory(ListView):
model = Category
paginate_by = 2
template_name = 'shit.html' # :)
context_object_name = 'queryset'
template:
{% extends "base.html" %}
{% load static %}
{% block content %}
<div class="sidebar-box ftco-animate">
<ul class="categories">
<h3 class="heading mb-4">Categories</h3>
{% for category in queryset %}
<li>{{category}}<span>{{ category.posts_set.count }}</span></li>
<ul>
{% for post in category.post_set.all %}
<li>{{ post }}</li>
{% endfor %}
</ul>
{% endfor %}
</ul>
</div>
{% endblock content %}
I also use {{ category.post_set.count }} instead of 12, since I think you are looking for the number of Post objects within each category.
You can use unique=True in desired field, to make every value unique. If you'll try to add new record with same value of unique field, a django.db.IntegrityError will be raised.
More about unique
More about model's fields options

How to modify Django query_set before serving it in a ListView

I have a Django project with a database of Song Objects that users can search through.
My models.py looks like this:
class Songs(models.Model):
title = models.CharField(max_length=100)
artist = models.CharField(max_length=100)
link = models.CharField(max_length=255, unique=True)
album = models.CharField(max_length=100)
duration = models.CharField(max_length=40) # Duration in num_secs
and my views.py looks like this:
class ResultsView(ListView):
template_name = os.path.join(APPNAME, "results.html")
model = Songs
context_object_name = 'results'
paginate_by = 60
ordering = ['title']
def get_context_data(self, **kwargs):
context = super(ResultsView, self).get_context_data(**kwargs)
context['query'] = self.request.GET.get('query')
return context
def get_queryset(self, **kwargs):
query = self.request.GET.get('query')
query_set = Songs.objects.all()
results = query_set.filter(title__icontains=query)
return list(results)
And my results.html template looks like this:
{% if results %}
<div class="container-fluid">
<div class="row">
{% for result in results %}
<div class="col-md-2 result-col">
<a data-toggle="tooltip" title="{{ result.title }}" target="_blank" href="/song/{{ result.id }}">
<div class="result-text">{{ result.title }} </div>
<div class="result-dur">{{ result.duration }}</div>
</a>
</div>
{% endfor %}
</div>
</div>
{% else %}
<h2>No Results</h2>
{% endif %}
Due to the way the data is initially stored in my DB, the duration of each song is stored as the number of seconds, ie a song that is 2 minutes long is stored with a duration of 120. However, I want to display it on the template in the format: "hh:mm:ss".
How can I modify my ResultsView class so that I can parse the duration field of all of the objects in my query_set so that they have a more readable duration?
Note: to be 100% clear, I know how to do the actual conversion, using strftime. What I don't know, is how to access the Song.duration fields in my ResultsView.
You could add a method or property to your model:
#property
def converted_time(self):
return <your converted time>
And in your template {{ result.converted_time }}
You can just use a custom template tag.
Ref
snippet
#register.filter()
def formatSeconds(s):
mins = math.floor(s / 60);
secs = math.floor(s - (mins * 60));
return "%d:%02d" % (mins, secs);

Django: My template is not loading/being called

Edit: as #HåkenLid said, I should've asked this as 2 separate posts, the first question (regarding the template) has been answered!
I'm new to Django and I've been having a nice time with it so far. I'm trying to build a blog for fun but I've run with the some problems.
Here are my models, forms, urls, views and file tree:
http://i.imgur.com/KsLV3d9.png
For the first problem: (To save space I've only added parts of each file)
#blog/views.py
class BlogDetail(generic.DetailView):
model = Entry
template_name = "blog/entry_detail.html"
#blog/templates/blog/entry_detail.html
{% extends "base.html" %}
{% load django_markdown %}
<div class="post">
<h2>{{ object.title }}</h2>
<p class="meta">
{{ object.creation_date }} |
Tagged under {{ object.tags.all|join:", " }}
</p>
{{ object.body|markdown }}
</div>
#blog/urls.py
urlpatterns = patterns(
'',
url(r'^$', views.BlogIndex.as_view(), name="index"),
url(r'^entry/(?P<slug>\S+)$', views.BlogDetail.as_view(), name="entry_detail"),
)
#blog/models.py
class Entry(models.Model):
title = models.CharField(max_length=100)
body = models.TextField()
slug = models.SlugField(max_length=100, unique=True)
creation_date = models.DateTimeField(editable=False)
publication_date = models.DateTimeField(editable=False, null=True)
modification_date = models.DateTimeField(editable=False)
author = models.ForeignKey(User)
categories = models.ManyToManyField(Category)
tags = models.ManyToManyField(Tag)
objects = EntryQuerySet.as_manager()
def __str__(self):
return self.title
def was_published_recently(self):
now = datetime.datetime.now()
return now - datetime.timedelta(days=1) <= self.publication_date <= now
def is_published(self):
return self.publication_date is not False
is_published.boolean = True
is_published.short_description = 'Is it Published?'
def get_absolute_url(self):
return reverse("entry_detail", kwargs={"slug": self.slug})
class Meta():
verbose_name = "Blog Entry"
verbose_name_plural = "Blog Entries"
ordering = ['-creation_date']
def save(self, *args, **kwargs):
""" On save, update timestamps """
if not self.id:
self.creation_date = datetime.datetime.now()
self.modification_date = datetime.datetime.now()
return super(Entry, self).save(*args, **kwargs)
When I enter something like
localhost:8000/entry/this-is-an-entry-title-slug
e.j: http://i.imgur.com/c7yir79.png
I don't get the detail of the entry, just the title defined in base.html and that kind of stuff, I really don't know why it isn't being loaded.
As always, thank you very much for your help, and sorry if this are very basic questions but I haven't been able to find any solutions so far.
When extending templates you need to use {% block %}
If you have a block called 'content' in base.html, you can do this:
#blog/templates/blog/entry_detail.html
{% extends "base.html" %}
{% load django_markdown %}
{% block content %}
<div class="post">
<h2>{{ object.title }}</h2>
<p class="meta">
{{ object.creation_date }} |
Tagged under {{ object.tags.all|join:", " }}
</p>
{{ object.body|markdown }}
</div>
{% endblock content %}
For part 1, I think the issue is with your regex:
r'^entry/(?P<slug>\S+)$
Should be:
r'^entry/(?P<slug>[-\w\d]+)/'
Since your current pattern isn't capturing the slug, it's not being loaded into the context.
I am not much help on part 2, sorry. If you were doing this in your own view you could override the form_valid method of the CBV, but I am not sure how to accomplish the same within the built-in admin panel.

Calling a method from template

I seem to have a problem with calling a method from a django template. Given the model:
class Gallery(models.Model):
class Meta:
verbose_name_plural = "Galerie"
def photo_count(self):
return self.photo_set.count()
def random_image(self):
return self.photo_set.get(id=random.randint(1,self.photo_count()))
title = models.CharField(max_length=50)
imgur_id = models.CharField(max_length=15)
date_created = models.DateTimeField('Data utworzenia', auto_now=True)
is_active = models.BooleanField()
def __unicode__(self):
return self.title
where Gallery is foreignKey for Photo
views.py:
def index(request):
galleries = Gallery.objects.get(is_active=True)
if galleries.count() is 0:
messages.warning(request, "Niestety, żadna galeria nie jest aktywna, zaglądnij niebawem!")
return redirect(reverse(home))
elif galleries.count() is 1:
return render(request, 'gallery/home.html', {'gallery': galleries})
else:
return render(request, 'gallery/index.html', {'galleries': galleries})
I want to do this in template:
{% for gallery in galleries %}
{{ gallery.random_image }} <br />
{% endfor %}
The result I get is:
[ Photo object ]
[]
[]
[]
But when i call
{% for gallery in galleries %}
{{ gallery.photo_count }} <br />
{% endfor %}
The result is correct.
Why does this not work?
In my opinion you are complicated a little bit.
class Gallery(models.Model):
class Meta:
verbose_name_plural = "Galerie"
def random_image(self):
return self.photo_set.order_by('?')[0] if self.photo_set.count() else None
title = models.CharField(max_length=50)
imgur_id = models.CharField(max_length=15)
date_created = models.DateTimeField('Data utworzenia', auto_now=True)
is_active = models.BooleanField()
def __unicode__(self):
return self.title
and in the template,
{% for gallery in galleries %}
{% if gallery.random_image %}
{{ gallery.random_image }} <br />
{% else %}
No images found.
{% endif %}
{% endfor %}
Read more on order_by here
Note that ? could prove slightly heavy. If so, get thelist of ids of the photo_set associated with this gallery object, and get a random id from this list, rather than doing a
random.randint(1,self.photo_count())
Add #property to your fonction.
#property
def photo_count(self):
return self.photo_set.count()

Categories