How to relate two object lists? - python

I have two models related them with a foreign key, pregunta is a question to a especific Comentario (comment).
models.py
class Comentario (models.Model):
titulo = models.CharField(max_length=50)
texto = models.CharField(max_length=200)
autor = models.ForeignKey (Perfil, null=True, blank=True, on_delete=models.CASCADE)
fecha_publicacion = models.DateTimeField(auto_now_add=True)
tag = models.ManyToManyField(Tags, blank=True)
def __str__(self):
return (self.titulo)
class Pregunta (models.Model):
descripcion = models.CharField(max_length=150)
autor = models.ForeignKey (Perfil, null=True, blank=True, on_delete=models.CASCADE)
fecha_pregunta = models.DateTimeField(auto_now_add=True)
comentario_preguntado = models.ForeignKey(Comentario, null=True, blank=True)
def __str__(self):
return (self.descripcion)
I create a view where I want to show only the comments having a question and the questions itself. I create two object list, one with the comments and the other with questions. The problem is that I want to show in the template the first comment and its questions, then the next comment and its questions...
I do not know if this should be done in the template or I need to change my view.
views.py
def responder(request):
comment = Comentario.objects.filter(id__in=Pregunta.objects.all().values_list('comentario_preguntado')).filter(autor=request.user)
question = Pregunta.objects.filter(comentario_preguntado__in=comment)
return render(request, 'home/responder.html', {'comment': comment, 'question': question})

Just access a commentario's preguntas via the related_name of their ForeignKey relation:
def responder(request):
# use __isnull to filter for comments with existing preguntas
# use prefetch_related to minimize db hits
# use plural name to be semantically sound ;)
comments = Comentario.objects\
.filter(pregunta_set__isnull=False, autor=request.user)\
.prefetch_related('pregunta_set')
return render(request, 'home/responder.html', {'comments': comments})
And in the template:
{% for comment in comments %}
# display comment stuff
{% for pregunta in comment.pregunta_set.all %}
# display pregunta stuff
{% endfor %}
{% endfor %}
Docs on related_name, prefetch_related and isnull.

Related

__in filter only returning one value, show query through intermediate table

Noob at coding and need help. I am trying to render the view article by filtering through the model Spots. I have an intermediate table ArticleSpots to link the 2 tables Spots and Articles. In the views article I want to show only the spots that are linked to that specific article. My problem is that Spots.objects.filter(id__in=articleSpots) only shows the first one value and not all of the spots that are linked. What am I doing wrong here?
views.py
def article(request, slug):
articles = get_object_or_404(Articles, slug=slug)
article_id = articles.id
articleSpots = ArticleSpots.objects.filter(article__id=article_id)
spots = Spots.objects.filter(id__in=articleSpots)
context = {"spots": spots, "articles": articles}
template_name = "articletemplate.html"
return render(request, template_name, context)
models.py
class ArticleSpots(models.Model):
article = models.ForeignKey('Articles', models.DO_NOTHING)
spot = models.ForeignKey('Spots', models.DO_NOTHING)
class Meta:
managed = True
db_table = 'article_spots'
verbose_name_plural = 'ArticleSpots'
def __str__(self):
return str(self.article) + ": " + str(self.spot)
class Articles(models.Model):
title = models.CharField(max_length=155)
metatitle = models.CharField(max_length=155)
slug = models.SlugField(unique=True, max_length=155)
summary = models.TextField(blank=True, null=True)
field_created = models.DateTimeField(db_column='_created', blank=True, null=True)
field_updated = models.DateTimeField(db_column='_updated', blank=True, null=True)
cover = models.ImageField(upload_to="cover", blank=True, default='logo-00-06.png')
class Meta:
managed = True
db_table = 'articles'
verbose_name_plural = 'Articles'
def __str__(self):
return str(self.id) + ": " + str(self.title)
class Spots(models.Model):
title = models.CharField(max_length=155)
metatitle = models.CharField(max_length=155)
slug = models.SlugField(unique=True, max_length=155)
author = models.ForeignKey(Authors, models.DO_NOTHING)
field_created = models.DateTimeField(db_column='_created', blank=True, null=True)
field_updated = models.DateTimeField(db_column='_updated', blank=True, null=True)
cover = models.ImageField(upload_to="cover", blank=True, default='logo-00-06.png')
summary = models.TextField(blank=True, null=True)
content1 = models.TextField(blank=True, null=True)
content2 = models.TextField(blank=True, null=True)
class Meta:
managed = True
db_table = 'spots'
verbose_name_plural = 'Spots'
def __str__(self):
return str(self.id) + ": " + str(self.title)
html
<!-- START MAIN -->
<main class="page"></main>
<p>
{{ spots.title }} <br />
{{ spots.content1 }} <br />
{{ articles.title }}
</p>
{% for spots in spots %} {{ spots.title}} {% endfor %}
<!-- END MAIN -->
You are currently retrieving Spots that have the same primary key as the ArticleSpots object, but that does not make much sense: it is possible that this is the case, but even if that happens, the returned Spots does not per se is linked to a relevant ArticleSpots with the given article.
You can retrieve the relevant Spots with:
def article(request, slug):
article = get_object_or_404(Articles, slug=slug)
spots = Spots.objects.filter(articlespots__article=article)
context = {'spots': spots, 'article': article}
return render(request, 'articletemplate.html', context)
I would strongly advise to name you Article object article since it is a single Article, not a collection of Articles. spots on the other hand is a collection of spots.
It makes no sense to render {{ spots.content1 }} and {{ spots.title }}, since spots is a collection of Spots that can contain zero, one or more items.
The template thus should look like:
<p>
{{ article.title }}
</p>
{% for spot in spots %} {{ spot.title}} {% endfor %}
Note: normally a Django model is given a singular name, so Articles instead of Article.

Python - retrieving slug value from foreignkey class

I'm a newbie in Python and I need some help with my code. Not even sure if my title makes sense.
Basically I have my blog and I'm trying to add a sidebar with popular posts. I have created a PostStatistics class to collect the number of visits in each post which can be seen from Django admin.
The PostStatistics class has a ForeignKey to the Post class.
OK, so my problem is in the PostDetail view. I have a QuerySet there called Popular where I retrieve the 5 most popular posts in the last 7 days. There I retrieve the Post_id and Post__Title. I also need to retrieve the Post SLUG but I have no idea how I can do that.
The slug would be used in the following bit of code:
{{ pop_article.post__title }}
The following is what in my models:
class Post(models.Model):
title = models.CharField(max_length=200, unique=True)
slug = models.SlugField(max_length=200, unique=True)
author = models.ForeignKey(
User, on_delete=models.CASCADE, related_name='blog_posts')
updated_on = models.DateTimeField(auto_now=True)
content = models.TextField()
created_on = models.DateTimeField(auto_now_add=True)
status = models.IntegerField(choices=STATUS, default=0)
views = models.PositiveIntegerField(default=0, editable=False)
class Meta:
ordering = ['-created_on']
db_table = "post"
def __str__(self):
return self.title
def get_absolute_url(self):
from django.urls import reverse
return reverse("post_detail", kwargs={"slug": str(self.slug)})
class PostStatistic(models.Model):
class Meta:
db_table = "PostStatistic"
post = models.ForeignKey(Post, on_delete=models.CASCADE)
date = models.DateField('Date', default=timezone.now)
views = models.IntegerField('Views', default=0)
def __str__(self):
return self.post.title
The following is what is in my views:
def PostDetail(request, slug):
template_name = 'post_detail.html'
post = get_object_or_404(Post, slug=slug)
comments = post.comments.filter(active=True)
new_comment = None
context = {}
obj, created = PostStatistic.objects.get_or_create(
defaults={
"post": post,
"date": timezone.now()
},
# At the same time define a fence or statistics object creation
# by two fields: date and a foreign key to the article
date=timezone.now(), post=post
)
obj.views += 1
obj.save(update_fields=['views'])
# Now pick up the list of the last 5 most popular articles of the week
popular = PostStatistic.objects.filter(
# filter records in the last 7 days
date__range=[timezone.now() - timezone.timedelta(7), timezone.now()]
).values(
'post_id', 'post__title'
).annotate(
views=Sum('views')
).order_by(
# sort the records Descending
'-views')[:5] # Take 5 last records
# Comment posted
if request.method == 'POST':
comment_form = CommentForm(data=request.POST)
if comment_form.is_valid():
# Create Comment object but don't save to database yet
new_comment = comment_form.save(commit=False)
# Assign the current post to the comment
new_comment.post = post
# Save the comment to the database
new_comment.save()
else:
comment_form = CommentForm()
return render(request, template_name, {'post': post,
'comments': comments,
'new_comment': new_comment,
'comment_form': comment_form,
'popular_list': popular
},)
The following is in my HTML:
<div class="card-body">
{% if popular_list %}
<p class="card-text">
{% for pop_article in popular_list %}
{{ pop_article.post__title }}
<br>
{% endfor %}
</p>
{% endif %}
</div>
Thanks in advance!
you need to add post__slug in values of this query in view function like this
popular = PostStatistic.objects.filter(
# filter records in the last 7 days
date__range=[timezone.now() - timezone.timedelta(7), timezone.now()]
).values(
'post_id','post__slug' ,'post__title'
).annotate(
views=Sum('views')
).order_by(
# sort the records Descending
'-views')[:5]
then you will be able to do like this in the template
{{ pop_article.post__title }}

How to pass multiple values from models.py to HTML via views.py in Django

I have a following models.py for my Django blog, I made a following views.py to pass the value of the slug for my URL parameter.
However I am struggling to create a model in views to get other data(person & description) from Category class.
I have tried some patterns by myself but can not pass them to HTML. (always Error or not showing)
Can you please give me some idea of how to solve this.
models.py
class Category(models.Model):
person = models.CharField(max_length=20)
description = models.TextField()
slug = models.SlugField()
def __str__(self):
return self.person
views.py
def blog_category(request, category):
posts = Post.objects.filter(categories__slug__contains=category).order_by("-created_on").distinct()
context = {"category": category, "posts": posts}
return render(request, "blog_category.html", context)
HTML(Localhost:8000/slug)
{{ person }}
{{ description }}
this is full code of my models.py
class Category(models.Model):
person = models.CharField(max_length=20)
description = models.TextField()
slug = models.SlugField()
def __str__(self):
return self.person
class Recommender(models.Model):
recommender_name = models.CharField(max_length=20)
slug = models.SlugField()
def __str__(self):
return self.recommender_name
class Post(models.Model):
book_title = models.CharField(max_length=255)
author = models.CharField(max_length=255)
book_link = models.CharField(max_length=255)
recommenders = models.ForeignKey("Recommender", on_delete=models.CASCADE,)
source = models.TextField()
source_link = models.CharField(max_length=255)
created_on = models.DateTimeField(auto_now_add=True)
last_modified = models.DateTimeField(auto_now=True)
categories = models.ManyToManyField("Category", related_name="posts")
slug = models.SlugField()
def __str__(self):
return self.book_title
posts = Post.objects.filter(categories__slug__contains=category).order_by("-created_on").distinct()
Is going to return a queryset. It can have more than one instance of the model class (since you are using filter). In your context you are sending this queryset as posts to your templates.
So in your HTML you can use something like this. You need to use a for loop since there can be more than one item in posts.
{% for post in posts %}
{% for category in post.categories.all %}
{{ category.person }}
{{ category.description }}
{% endfor %}
{% endfor %}
I would look at this example.
Namely, if you render the template like it is shown in the example, you should be able to do
{{ category.person }} {{ category.description }}

Django: the detail page is not showing(template error)

am working on a Django project where showing the details of post and amount
here is my models.py of post
class Loader_post(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE ,related_name="Loader")
pick_up_station = models.CharField(max_length=150)
destination_station = models.CharField(max_length=150)
sender_name = models.CharField(max_length=150)
phone_number = PhoneNumberField(null=False, blank=False, unique=True)
receiver_name = models.CharField(max_length=150)
def __str__(self):
return self.user.username
def get_absolute_url(self):
return reverse("Loader:my_job", kwargs={"pk": self.pk})
this is my second models which I inherit Loader post
class price(models.Model):
my_post = models.ForeignKey(Loader_post, related_name='prices',on_delete=models.CASCADE,
null=True, default='')
user = models.ForeignKey(get_user_model(), on_delete=models.CASCADE, null=True, default='')
driver_price = models.CharField(max_length=150, null=True)
driver_name = models.CharField(max_length=150, null=True)
approved_price = models.BooleanField(default=False)
status = models.BooleanField(default=False)
def get_absolute_url(self):
return reverse("Driver:Driverview")
def __str__(self):
return self.driver_price
this is the view.py of both list and details view
class offer_view(ListView, SelectRelatedMixin):
context_object_name = 'offern'
model = Loader_post
template_name = "offer.html"
def get_queryset(self):
qs = Loader_post.objects.filter(user=self.request.user)
return qs
class offer_view_detail(DetailView):
context_object_name = 'offernew'
model = Loader_post
template_name = "offer_detail.html"
here is my HTML page of list view ...when someone clicks on it it shows the detail of next post
offer.html
{% for my in offern %}
{{my.sender_name}} {% endfor %}
and when someone clicks on its route to the detail page .. but it shows template doesn't exist
this is my detail page ie. offer_details.hml
<p>{{offernew.sender_name}}</p>
<p>{{offernew.receiver_name}}</p>
{% for x in offernew.prices.all %}
<p>
<p>{{x.driver_name}}</p>
</p>
and this is urls.py
path('offerdetail/<int:pk>', views.offer_view_detail.as_view(),name="offerdetail"),
path('offer/', views.offer_view.as_view(), name="offer"),
Following on from comments,
In you ListView,
{{my.sender_name}}
here, the url specified is not defined in your urls.py, that's why it was showing no template doesn't exist, changing to this would solve this.
{{my.sender_name}}
Now, To show prices model in your DetailView, i would do something like this.
class offer_view_detail(DetailView):
context_object_name='offernew'
model = Loader_post
template_name = "offer_detail.html"
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['price_model'] = self.object.prices.all()
return context
and In your Template
<p>{{offernew.sender_name}}</p>
<p>{{offernew.receiver_name}}</p>
{% for x in offernew.price_model %}
<p>{{x.driver_name}}</p>
{% endfor %}
Django Docs for DetailView

Django template sort by 'unrelated' model's field

I'm trying to sort related items in a template by a field in a model three ForeignKey relationships away. I'm assembling the data for the template in the view as proposed in another StackOverflow answer:
Sorting related items in a Django template
As far as I can tell, I copied the code from this as-is except for I had to change variable names. It doesn't throw an error, it just displays no list items in the HTML unordered list.
# checkout.html
{% for item in cart_items %}
<tr>
<td class="left">
{{ item.name }}
<ul>
{% for part in part_list %}
<li>{{ part.name }}
{% endfor %}
</ul></td>
</tr>
{% endfor %}
And the view...
# views.py
def checkout(request):
cart_items = get_cart_items(request)
itemCollection = []
for item in cart_items:
item.part_list = item.product.buildpart.all().order_by('product__family__type')
itemCollection.append(item)
return render(request, 'checkout.html', locals())
And the get_cart_items function:
# cart.py
def get_cart_items(request):
""" return all items from the current user's cart """
return CartItem.objects.filter(cart_id=get_cart_id(request))
As I said, the template and view are pretty much copies of the solution presented in the aforementioned StackOverflow article. One thing I thought was curious was that itemCollection[] from the view is never referenced in the template.
I believe the order_by clause ('product__family__type') is right only because it doesn't generate an error. But in case that is the problem or a part of it here is the chain of models I am attempting to navigate in that order_by clause:
We start from the shopping cart model (CartItem):
class Item(models.Model):
cart_id = models.CharField(max_length=50)
quantity = models.IntegerField(default=1)
product = models.ForeignKey(PartModel, unique=False)
class Meta:
abstract = True
class CartItem(Item):
date_added = models.DateTimeField(auto_now_add=True)
class Meta:
ordering = ['date_added']
verbose_name = "Cart Item"
Through the 'product' field we get to the model holding our inventory and its self-referential BuildPart ManyToMany model:
class PartModel(models.Model):
family = models.ForeignKey(PartFamily)
name = models.CharField("Model Name", max_length=50, unique=True)
buildpart = models.ManyToManyField('self', through='BuildPart',
symmetrical=False, related_name='+')
class Build(models.Model):
build = models.ForeignKey(PartModel, related_name='+')
part = models.ForeignKey(PartModel, related_name='+')
quantity = models.PositiveSmallIntegerField(default=1)
class Meta:
abstract = True
unique_together = ('build', 'part')
def __unicode__(self):
return self.build.name + ' with ' + str(self.quantity) + ' * ' + \
self.part.family.make.name + ' ' + self.part.name
class BuildPart(Build):
pass
class Meta:
verbose_name = "Build Part"
From there we follow the 'family' field to the PartFamily model:
class PartFamily(models.Model):
make = models.ForeignKey(PartMake)
type = models.ForeignKey(PartType)
name = models.CharField("Family Name", max_length=30,
unique=True)
slug = models.SlugField(unique=True)
And lastly, we get to the model with the 'order' field, the one we wish to sort the related items by, PartType:
class PartType(models.Model):
name = models.CharField("Part Type", max_length=30, unique=True)
slug = models.SlugField(unique=True)
order = models.PositiveSmallIntegerField()
description = models.TextField(blank=True, null=True)
To recap, how do I get the shopping cart products' related items, and sort them by the 'order' field in the PartType model?
You have two errors, both in the template.
Firstly, you've put your items with the sorted relationship in a list called itemCollection, but in the template you're iterating over cart_item instead. This is a very good example of why you should be explicit about what variables you pass to the template, rather than relying on locals().
Secondly, you then iterate over part_list without defining it. You mean item.part_list.

Categories

Resources