How can I render values from a loop from views to template? - python

I'm creating a private 1 to 1 chat between users and I'd like to show the last sent message, the problem is that when I'm looping every room's last message to my template it only shows me one single data and there are many.
Here is what I have in my views.py:
def my_rooms(request, username):
user = User.objects.get(username=username)
room_user = Room.objects.all()
print('----------------------------------------')
for room in room_user:
rooms = room.messages.all()[:1]
print(rooms)
return render(request, "my_room.html", {
'rooms': rooms,
})
print(rooms), (each article has their own private chat room):
-------------------------------
[]
[<Message: 02-02-2017 09:50AM, user01 to user02 : hey>] #room1
[]
[<Message: 02-02-2017 10:52AM, user01 to user02 : Fine and you ?>] #room2
my_room.html
{% for room in rooms %}
<p>{{ room.client }} : {{ room.message }}</p>
<p>{{ room.timestamp }}</p>
{% endfor %}
What happens in my template ?
I just get :
[<Message: 02-02-2017 10:52AM, user01 to user02 : Fine and you ?>] #room2
's datas published but NOT
[<Message: 02-02-2017 09:50AM, user01 to user02 : hey>] #room1
Why so ? How can I show all concerned rooms datas ?
------ UPDATE ------
The problem I'm trying to solve is that I'd like return values from the for loop to my template. I just don't understand why it's showing only 1 result and not all the for looped results on my template?
models.py might help it a bit clearer :
class Room(models.Model):
gig = models.ForeignKey(Gig, null=True)
creator = models.ForeignKey(User)
...
class Message(models.Model):
client = models.ForeignKey(User, related_name='client', null=True)
seller = models.ForeignKey(User, related_name='seller', null=True)
room = models.ForeignKey(Room, related_name='messages', null=True)
message = models.TextField(max_length=500)
timestamp = models.DateTimeField(default=timezone.now, db_index=True)
...

Thanks to Daniel Roseman's comment, who suggested me to append results to a list I could resolve this issue.
Here is how I did it :
views.py
...
room_user = Room.objects.all()
my_rooms = []
for room in room_user:
rooms = room.messages.all()[:1]
my_rooms.append(rooms)
return render(request, "my_room.html", {
'rooms': my_rooms,
})
my_room.html
{% for room in rooms %}
{% for r in room %}
<p>{{ r.client }} {{ r.message }}</p>
<p>{{ r.timestamp }}</p>
{% endfor %}
{% endfor %}

Looking at the flow of your views.py code, it's only going to pass the rooms object on the last iteration of the for loop into your template. I would suggest passing room_user instead to the dictionary:
return render(request, "my_room.html", {
'room_user': room_user,
}
Then, in your template, you'll want to build a nested for loop:
{% for room in room_user %}
{% for rmessage in room.messages.all()[:1] %}
<p>{{ rmessage.client }} : {{ rmessage.message }}</p>
<p>{{ rmessage.timestamp }}</p>
{% endfor %}
{% endfor %}

Related

How to show sub-sub category of sub-category in Django?

I have the following models:
class TutorialCategory(models.Model):
category_title = models.CharField(max_length=150)
category_summary = models.CharField(max_length=150)
category_slug = models.SlugField(default=1, blank=True)
class TutorialSeries(models.Model):
series_title = models.CharField(max_length=200)
series_maincategory = models.ForeignKey(
TutorialCategory, default=1, on_delete=models.SET_DEFAULT)
series_summary = models.CharField(max_length=200)
class Tutorial(models.Model):
tutorial_title = models.CharField(max_length=150)
tutorial_content = models.TextField()
tutorial_published = models.DateTimeField(
"date Published", default=datetime.now())
tutorial_series = models.ForeignKey(
TutorialSeries, default=1, on_delete=models.SET_DEFAULT)
tutorial_slug = models.SlugField(default=1, blank=True)
As shown above TutorialCategory is main category, TutorialSeries is sub category and Tutorial is sub-sub-category. I created a simple view that shows sub categories of main categories, but don't know how to show the sub-sub categories of sub category.
Please check out views.py and urls.py if you can improve its quality and if there is an easy and better way of doing it. Anyway, this is view:
def single_slug(request, single_slug):
matching_series = TutorialSeries.objects.filter(
series_maincategory__category_slug=single_slug)
series_urls = {}
for i in matching_series.all():
part_one = Tutorial.objects.filter(
tutorial_series__series_title=i.series_title).earliest("tutorial_published")
series_urls[i] = part_one.tutorial_slug
return render(request, 'tutorial/sub-category.html', context={
"tutorial_series": matching_series,
'part_ones': series_urls
})
urls here:
urlpatterns = [
path('', views.home_page, name='home'),
path('tutorial/<int:id>/', views.tutorial_detail, name='tutorial_detail'),
path('<single_slug>/', views.single_slug, name='single_slug'),
]
the template that shows sub-category of main category:
{% for tut, partone in part_ones.items %}
<div class="card">
<div class="card-body">
<h5 class="card-title">{{ tut.series_title }}</h5>
<p>{{ tut.series_summary }}</p>
Read more
</div>
</div>
{% endfor %}
Please help me how to do it and if you know any better way of doing it please let me know and help me. Thank you so much in advance.
edit: #ruddra
I changed views.py to this passing matching_series
def single_slug(request, single_slug):
matching_series = TutorialSeries.objects.filter(
series_maincategory__category_slug=single_slug)
series_urls = {}
for i in matching_series.all():
part_one = Tutorial.objects.filter(
tutorial_series__series_title=i.series_title).earliest("tutorial_published")
series_urls[i] = part_one.tutorial_slug
return render(request, 'tutorial/sub-category.html', context={
"matching_series": matching_series,
'part_ones': series_urls
})
and replaced the previous template with yours. template here:
{% for tutorial in matching_series %}
{% for sub_cat in tutorial.tutorialseries_set.all %}
{{ sub.series_title }}
{% for sub_sub_cat in sub.tutorial_set.all %}
{{ sub_sub_cat.tutorial_title }}
{% endfor %}
{% endfor %}
{% endfor %}
You can try like this:
{% for sub_cat in matching_series %}
{% for sub_sub_cat in sub_cat.tutorial_set.all %}
{{ sub_sub_cat.tutorial_title }}
{% endfor %}
{% endfor %}
Here I am assuming matching_series is being passed through context from view in single_slug. Then I am using backward relation between different models to fetch the data.
Explanation: Assuming there is object named tutorial_category which is instance of TutorialCategory. As there is ForeignKey from TutorialSeries to TutorialCategory, I can use tutorial_category.tutorialseries_set.all() or .filter() etc to fetch the tutorial series from tutorial_category object( As I am rendering it in template, I removed parenthesis after all). Similarly I fetch Tutorial from TutorialSeries.

Reverse M2M query in Django-template

I would like to fetch the names of the speakers in the template. I marked it with xxxx. How can I do this? Thank you so much for helping out. My files:
models.py
class City(models.Model):
name = models.CharField(max_length=100)
class Speaker(models.Model):
name = models.CharField(max_length=100)
url = models.URLField(max_length=100)
city = models.ManyToManyField(City, blank=True)
views.py
def home(request):
cities = City.objects.all().exclude(speaker__isnull=True)
return render(request, "index.html", {"cities":cities})
index.html
{% for i in cities %}
{{ i.name }},xxx{{ i.speaker.name }}xxx<br>
{% endfor %}
You can access the speakers for each city as speaker_set:
{% for cit in cities %}
{{ cit.name }}
{% for spkr in cit.speaker_set.all %}
{{ spkr.name }}
<br>
{% endfor %}
{% endfor %}

Foreign key not rendering in template

I'm building a commenting system, which is working fine but i'm now trying to integrate voting. So I made another model to handle that and I tried to pair it using ForeignKey. Not too familiar with ForeignKey but i've looked at some other answers here to see how to render it in the template. I tried that using the nested for loop in my template below but {{ j.user }} doesn't render anything. Can anyone tell me what I'm doing wrong?
models.py
class Comment(models.Model):
destination = models.CharField(default='1', max_length=12, blank=True)
author = models.CharField(max_length=120, blank=True)
comment_id = models.IntegerField(default=1)
parent_id = models.IntegerField(default=0)
comment_text = models.TextField(max_length=350, blank=True)
timestamp = models.DateTimeField(default=timezone.now, blank=True)
def __str__(self):
return self.comment_text
class CommentScore(models.Model):
user = models.ForeignKey(User)
comment = models.ForeignKey(Comment)
upvotes = models.IntegerField(default=0)
downvotes = models.IntegerField(default=0)
views.py
...
comment_list = Comment.objects.filter(destination=id)
score = CommentScore.objects.all()
context = {
'score': score,
'comment_list': comment_list,
}
return render(request, 'article.html', context)
template
{% for i in comment_list %}
<div class='comment_div'>
<h3>{{ i.author }}</h3>
{% for j in comment_list.score_set.all %}
{{ j.user }} #nothing comes up
{% endfor %}
<p>{{ i.comment_text }}</p>
</div>
{% endfor %}
when using _set, the reverse relationship lookup, you must provide the full model name, you must also specify which instance this list of related models this "set" is for so it should be
{% for j in i.commentscore_set.all %}
{% empty %}
No scores found
{% endfor %}
You may also wish to set a related_name for the foreign key
comment = models.ForeignKey(Comment, related_name='scores')
...
{% for j in i.scores.all %}

Select reverse Foreign Key in ModelChoicefield

I have an order form where the user can choose one item and select a quantity. The price depends on how much is ordered. For example, each item is $10 if you order <100, but $7 if you order 100-200.
In the template, I want to display the pricing information underneath the form for each choice.
These are my Models:
class Product(models.Model):
name = models.TextField()
class Price(models.Model):
"""
This lets us define rules such as:
When ordering <100 items, the price is $10
When ordering 100-200 items, the price is $7
When ordering 200-300 items, the price is $5
etc
"""
price = models.FloatField()
min_quantity = models.PositiveIntegerField()
max_quantity = models.PositiveIntegerField()
product = models.ForeignKey(Product)
class Order(models.Model):
product = models.ForeignKey(Product, null=False, blank=False, default='')
quantity = models.IntegerField()
I can loop over the form fields and the queryset independently:
{% for choice in form.product.field.queryset %}
<h1>{{choice}} {{choice.price_set.all}}</h1>
{% endfor %}
{% for choice in form.product %}
<h1>{{ choice.tag }} {{ choice.choice_label }}</h1>
{% endfor %}
... but I don't know how to combine the loops to display the prices underneath the form fields.
Essentially, I want to select a reverse foreign key from a ModelChoicefield widget. I either need to loop over both the form fields and the queryset at once or access elements in the queryset from the form element. Ideally, this is what I'd like to do in my template:
{% for choice in form.product %}
<h1>{{ choice.tag }} {{ choice.choice_label }}</h1>
{% for price in choice.price_set.all %}
<h1>{{price}} etc...</h1>
{% endfor %}
{% endfor %}
Surely I'm not the first person with this use case. What's the best way to do this?
Edit: As requested, this is my form and my view. Reviewing this, I suppose I should have mentioned I was using the RadioSelect widget.
Form:
class OrderForm(forms.ModelForm):
class Meta:
exclude = ['date_added']
widgets = {
'mailer': forms.RadioSelect
}
model = Order
View:
def processOrder(request):
if request.method == 'POST':
orderForm = OrderForm(request.POST)
if orderForm.is_valid():
orderObject = orderForm.save()
return render(request, TEMPLATE_PREFIX + "confirm.html", {"orderObject": orderObject})
else:
return render(request, TEMPLATE_PREFIX + "register.html", { "form": orderForm })
else:
return render(request, TEMPLATE_PREFIX + "register.html", { "form": OrderForm()})
For (non)perfectionists with deadlines, this code works, albeit inefficiently.
{% for choice in form.product %}
{% for price_candidate in form.mailer.field.queryset %}
{% if price_candidate.id == choice.choice_value|add:0 %}
<h1>{{ choice.tag }} {{ choice.choice_label }}</h1>
{% for price in price_candidate.price_set.all %}
<h1>{{price}} etc...</h1>
{% endfor %}
{% endif %}
{% endfor %}
{% endfor %}
(The add:0 hack converts choice_value into an int. cf http://zurb.com/forrst/posts/Compare_string_and_integer_in_Django_templates-0Az )

Haystack search on a many to many field is not working

I'm trying to run a search on a model that has a many to many field, and I want to filter the search using this field.
here is my current code:
search_indexes.py
class ListingInex(indexes.SearchIndex, indexes.Indexable):
text = indexes.CharField(document=True, use_template=True)
business_name = indexes.CharField(model_attr='business_name')
category = indexes.MultiValueField(indexed=True, stored=True)
city = indexes.CharField(model_attr='city')
neighborhood= indexes.CharField(model_attr='neighborhood')
state = indexes.CharField(model_attr='state')
address = indexes.CharField(model_attr='address')
zip_code = indexes.CharField(model_attr='zip_code')
phone_number = indexes.CharField(model_attr='phone_number')
def get_model(self):
return listing
def index_queryset(self, using=None):
return self.get_model().objects.all()
def prepare_category(self, obj):
return [category.name for category in obj.category_set.all()]
listing_text.txt
{{ object.business_name }}
{{ object.state }}
{{object.city}}
{{object.zip_code}}
{{object.phone_number}}
{{object.neighborhood}}
{% for category in obj.category.all %}
{{ category.name }}
{% endfor %}
I'm trying to do a simple search like:
search_results = SearchQuerySet().filter(category=query_text).filter
it returns an empty list. Thanks in advance.
Reading your code I think the problem is here:
{% for category in obj.category.all %}
{{ category.name }}
{% endfor %}
Category is not an object, is a MultiValueField. Maybe if you try something like:
{% for category in object.category.all %}
{{ category.name }}
{% endfor %}
object.category.all instead of obj.category.all. I'm not sure the solution is this, but I'm pretty sure the error could be in this 3 lines.

Categories