How do I iterate inside a queryset in django template - python

My django view returns a dictionary people with values for all keys in list format.
The code for the view is:
class ProjectDetail(View):
def get(self, request, pk, *args, **kwargs):
project = Project.objects.get(pk=pk)
roles = Role.objects.filter(project=pk)
people = {}
for role in roles:
try:
people[role] += Person.objects.filter(role=role.pk)
except KeyError:
people[role] = [Person.objects.filter(role=role.pk)]
context = {
'project': project,
'people': people
}
return render(request, 'project_management/project_detail.html', context)
My Models
class Project(models.Model):
title = models.CharField(max_length=2000)
introduction = models.TextField(blank=True)
class Role(models.Model):
role_name = models.CharField(max_length=30)
project = models.ForeignKey(Status, on_delete=models.CASCADE)
class Person(models.Model):
name = models.CharField(max_length=30, blank=True, null=True)
role = models.ForeignKey(Role, on_delete=models.CASCADE)
In order to iterate through the dictionary I used:
{% for designation, persons in people.items %}
<h5> {{ designation.role_name }} </h5>
<ul>
{% for person in persons %} <!-- My Problem lies here, this loop is not iterating, it's running only once-->
<li> {{person}} </li>
{% endfor %}
</ul>
{% endfor %}
The result I got is:
I want the items inside queryset listed out separately, instead of being shown inside square brackets. How can I make that happen?

You don't need to do all this work. You can pass only the project. For example with:
from django.shortcuts import get_object_or_404
class ProjectDetail(View):
def get(self, request, pk, *args, **kwargs):
project = get_object_or_404(Project, pk=pk)
return render(request, 'project_management/project_detail.html', {'project': project})
or even simpler with a DetailView [Django-doc]:
class ProjectDetail(DetailView):
queryset = Status.objects.prefetch_related(
'role_set', 'role_set__person_set'
)
template_name = 'project_management/project_detail.html'
context_object_name = 'project'
Then in the template you can render this with:
{% for role in project.role_set.all %}
<h5> {{ role.role_name }} </h5>
<ul>
{% for person in role.person_set.all %}
<li> {{ person.name }} </li>
{% endfor %}
</ul>
{% endfor %}

Related

Accessing a ManyToManyField's contents for a Django template

I'm making a low-spec e-bay clone, I'm trying to implement a watchlist function but the problem is I can't access the ManyToManyField in the Watchlist model so I can use the contents of the field in the template I'm using. To display each user's watchlist to them, so far I only get this result:
but I need to get a result like so:
Code I'm using:
watchlist.html
{% extends "auctions/layout.html" %}
{% load static %}
{% block title %} {{name}}'s Watchlist {% endblock %}
{% block body %}
<h2>{{name}}'s Watchlist</h2>
{% for listing in watchlist %}
<div class='listing'>
<h3>{{ listing.list_title}}</h3>
{% if listing.img_url == "" %}
<a href='#'><img src="{% static 'auctions/img404.png' %}" class='img-fluid'></a>
{% else %}
<a href='#'><img src="{{ listing.img_url }}" class="img-fluid" alt='image of {{ listing.list_title }}'></a>
{% endif %}
<p>
{{ listing.desc }}
</p>
<p>
Current Bid: ${{ listing.start_bid }}
</p>
{% if listing.category == "" %}
<p>Category: No Category Listed</p>
{% else %}
<p>Category: {{ listing.category }}</p>
{% endif %}
<a href='#' class='btn btn-primary' id='go'>Go To Listing</a>
</div>
{% endfor %}
{% endblock %}
views.py
def render_listing(request, title):
if request.method == "POST":
form = BidForm(request.POST)
bid = int(request.POST['new_bid'])
listing = Auction_Listing.objects.get(list_title=title)
comments = Auction_Comments.objects.all().filter(auction_id=listing)
if bid < listing.start_bid:
error = True
else:
error = False
listing.start_bid = bid
listing.save()
return render(request, 'auctions/listing.html', {
"listing": listing,
"form": form,
"comments": comments,
"error": error
})
else:
form = BidForm()
listing = Auction_Listing.objects.get(list_title=title)
comments = Auction_Comments.objects.all().filter(auction_id=listing)
return render(request, 'auctions/listing.html', {
"listing": listing,
"form": form,
"comments": comments,
"error": False
})
#login_required
def watchlist_render(request):
user = request.user.id
username = request.user.username
watched = Watchlist.objects.filter(user_id=user)
return render(request, 'auctions/watchlist.html', {
"watchlist": watched,
"name": username
})
models.py
from django.contrib.auth.models import AbstractUser
from django.db import models
from django.db.models.deletion import CASCADE
CATEGORIES = [
('Appliances', 'Appliances'),
('Tech', 'Tech'),
('Gaming', 'Gaming'),
('Fashion', 'Fashion'),
('Sports and Fitness','Sports and Fitness'),
('Other','Other'),
("Hygiene and Medicine","Hygiene and Medicine"),
("Stationery","Stationery"),
('Decor', 'Decor'),
('Furniture','Furniture'),
('Cars and Mechanical Things','Cars and Mechanical Things'),
("Tools","Tools")
]
# Create models here
class User(AbstractUser):
pass
class Auction_Listing(models.Model):
user_id = models.IntegerField(default=1)
list_title = models.CharField(max_length=64)
desc = models.TextField(max_length=324)
img_url = models.URLField(max_length=200, null=True, blank=True)
start_bid = models.IntegerField()
category = models.CharField(choices=CATEGORIES, max_length=35, null=True, blank=True)
def __str__(self):
return f"ID:{self.id}, {self.list_title}: {self.desc}, {self.start_bid} posted by user:{self.user_id} in Category:{self.category}, url:{self.img_url}"
class Bids(models.Model):
bid = models.IntegerField(default=0)
user_id = models.IntegerField(default=1)
auction_id = models.ManyToManyField(Auction_Listing)
def __str__(self):
return f"ID:{self.id}, Bid {self.bid} posted by user:{self.user_id} on auction {self.auction_id}"
class Auction_Comments(models.Model):
user_id = models.IntegerField(default=1)
comment = models.TextField(max_length=324, default='N/A')
auction_id = models.ManyToManyField(Auction_Listing)
def __str__(self):
return f"ID:{self.id}, Comment: {self.comment} posted by user:{self.user_id} on auction {self.auction_id}"
class Watchlist(models.Model):
user_id = models.ForeignKey(User, on_delete=CASCADE,default=1)
auction_id = models.ManyToManyField(Auction_Listing, related_name='auction_listing')
def __str__(self):
return f"ID:{self.user_id} on auction {self.auction_id}"
Thanks in advance for the help!
Kind Regards
PrimeBeat
EDIT: The results of the links as #nigel222 requested
Ahh CS50W. I love this course. Anyway, I recognize where you've gotten stuck. What you're doing is that you're passing the Watchlist object in the context, instead of passing a list of listings.
This is why your {% for listing in watchlist %} is failing, because watchlist is a single object.
What you can do instead is, in your views.py, get the full list of watched items, and pass in that list in as the context. So:
#login_required
def watchlist_render(request):
user = request.user.id
username = request.user.username
# This will give you the Watchlist instance
watchlist = Watchlist.objects.filter(user_id=user)
# I would recommend renaming auction_id (in your models.py file) to `auctionItems` or 'listings'
# because it returns an object of type Auction_Listing, rather than just the id.
# for eg. watchlist.listings.all() allows you to instinctively understand what you're trying to do.
# Now you can extract the listings from the watchlist
listings = watchlist.auction_id.all()
return render(request, 'auctions/watchlist.html', {
"watchlist": listings,
"name": username
})
Now in your template you can access each listing in the list: {% for listing in listings %}

Django foreign key form field rendering in template

I would like to create a view for multiple object deletion. For this, I thought I could use a modelformset_factory.
This are my models:
class Item(models.Model):
rfid_tag = models.CharField()
asset = models.OneToOneField('Assets', default=None, null=True,
on_delete=models.SET_DEFAULT,)
date = models.DateTimeField(name='timestamp',
auto_now_add=True,)
...
class Assets(models.Model):
id = models.AutoField(db_column='Id', primary_key=True)
assettag = models.CharField(db_column='AssetTag', unique=True, max_length=10)
assettype = models.CharField(db_column='AssetType', max_length=150)
...
class Meta:
managed = False
db_table = 'Assets'
ordering = ['assettag']
def __str__(self):
return f"{self.assettag}"
def __unicode__(self):
return f"{self.assettag}"
Below is the form and formset factory:
class ItemDelete(forms.ModelForm):
asset = forms.CharField(required=True,
help_text= "Item asset tag",
max_length=16,
)
delete = forms.BooleanField(required=False,
label="Delete",
help_text='Check this box to delete the corresponding item',
)
class Meta:
model = Item
fields = ['asset']
ItemDeleteMultiple= forms.modelformset_factory(model=Item,
form=ItemDelete,
extra=0,
)
The view:
class DeleteMultipleView(generic.FormView):
template_name = *some html file*
form_class = ItemDeleteMultiple
success_url = *some url*
def form_valid(self, form):
return super().form_valid(form)
And the template:
{% extends "pages/base.html" %}
{% block title %}
<title>Delete Multiple</title>
{% endblock %}
{% block static %}
{% load static %}
{% endblock %}
{% block content %}
<h1>Delete Multiple Items</h1>
<form class="item_delete_multiple_form" action ="." method="POST"> {% csrf_token %}
<table border="2">
<tr><th colspan="3" scope="row">Select Items to Delete</th></tr>
{% for item_form in form %}
<tr>
{% if item_form.non_field_errors %}
<td>{{ item_form.non_field_errors }}</td>
{% endif %}
{% if item_form.asset.errors %}
<td>{{item_form.asset.errors}}</td>
{% endif %}
<td><label for="{{ item_form.asset.id_for_label }}">AssetTag {{forloop.counter}}:</label></td>
<td>{{item_form.asset}}</td>
{% if item_form.delete.errors %}
<td>{{item_form.delete.errors}}</td>
{% endif %}
<td>{{item_form.delete}}</td>
</tr>
{% endfor %}
</table>
</form>
{% endblock %}
{% block script %}
{% endblock %}
The template is not very easy to the eye, so here is the important part: <td>{{item_form.asset}}</td>.
The issue is the following:
If I don't add the asset = CharField() part in the ItemDelete form, the template will render what the __str__ / __unicode__ method of the Assets model will return (the assettag field) in a choice field.
If the asset field is a CharField in the form, the template will render the id of the Assets. The database entry in the Item table.
I would like to render asset.assettag in a CharField (read only text input). Is it possible to do this?
Or is there a better way to achieve the multiple delete operation, using a list of objects and a checkbox?
I have came to the following solution:
class ItemDelete(forms.ModelForm):
asset = forms.CharField(required=True,
help_text= "Item asset tag",
max_length=16,
disabled=True,
)
delete = forms.BooleanField(required=False,
label="Delete",
help_text='Check this box to delete the corresponding item',
)
def __init__(self, *args, **kwargs):
super(ItemDelete,self).__init__(*args, **kwargs)
self.initial['asset'] = Assets.objects.get(id=self.initial['asset']).assettag
class Meta:
model = Item
fields = ['asset']
Given that the input field is used just for presentation purposes (is disabled and cannot be edited), I think it will do. The downside is that it will hit the database for every object (being a formset, every object will have it's own form).
I am still open to better suggestions.

python django how to only show the information about an individual employee when I click on his name

I'm new to python django, trying to create an employee records project, on the django admin site I added some employees and their information, on the django site, I had the hyperlink for the individual employee, but when I click on the individual name, the next page comes all the employees information instead of the particular one, how can I only make it come out the information of the employee I click? Please help, thank you!
models.py
from django.db import models
import datetime
class Employee(models.Model):
'''An employee's information.'''
full_name = models.CharField(max_length=100)
address = models.CharField(max_length=100)
city = models.CharField(max_length=100)
state = models.CharField(max_length=100)
zip = models.CharField(max_length=100)
hire_date = models.DateField(default=datetime.date.today)
def __str__(self):
'''Return a string representation of the model.'''
return self.full_name
return self.address
return self.city
return self.state
return self.zip
return self.hire_date
views.py
from django.shortcuts import render
from .models import Employee
def index(request):
'''The home page for employee_record.'''
return render(request, 'employee_records/base.html')
def employees(request):
'''Shows all employees'''
employees = Employee.objects.order_by('full_name')
context = {'employees': employees}
return render(request, 'employee_records/employees.html', context)
def employee(request, employee_id):
'''Show a single employee and all his records.'''
employee = Employee.objects.get(id=employee_id)
objects = Employee.objects.all
context = {'employee': employee, 'objects': objects}
return render(request, 'employee_records/employee.html', context)
urls.py
'''Defines URL patterns for employee_records.'''
from django.urls import path
from . import views
app_name = 'employee_records'
urlpatterns = [
# Home page
path('', views.employees, name='employees'),
# Detail page for a single employee
path('employees/<int:employee_id>/', views.employee, name='employee'),
]
base.html
<p>
Employee-Record
</p>
{% block content %}{% endblock content %}
employees.py
{% extends 'employee_records/base.html' %}
{% block content %}
<ul>
{% for employee in employees %}
<li>
{{ employee }}
</li>
{% empty %}
{% endfor %}
</ul>
{% endblock content %}
employee.html
{% extends 'employee_records/employees.html' %}
{% block content %}
<p>{{ employee }}
{% for object in objects %}
<li>{{ object.full_name }}</li>
<li>{{ object.address }}</li>
<li>{{ object.city }}</li>
<li>{{ object.state }}</li>
<li>{{ object.zip }}</li>
<li>{{ object.hire_date }}</li>
{% empty %}
<li>There are no records for this employee yet.</li>
{% endfor %}
</p>
{% endblock content %}
models.py
# you should have only one return statement
class Employee(models.Model):
'''An employee's information.'''
full_name = models.CharField(max_length=100)
address = models.CharField(max_length=100)
city = models.CharField(max_length=100)
state = models.CharField(max_length=100)
zip = models.CharField(max_length=100)
hire_date = models.DateField(default=datetime.date.today)
def __str__(self):
'''Return a string representation of the model.'''
return self.full_name
views.py
# you have to change employee function
def employee(request, employee_id):
'''Show a single employee'''
try:
employee = Employee.objects.get(id=employee_id)
except Employee.DoesNotExist:
employee = None
context = {'employee': employee}
return render(request, 'employee_records/employee.html', context)
employee.html
{% extends 'employee_records/employees.html' %}
{% block content %}
{% if employee %}
<ul>
<li>{{ employee.full_name}}</li>
<li>{{ employee.address}}</li>
<li>{{ employee.city}}</li>
<li>{{ employee.state}}</li>
<li>{{ employee.zip}}</li>
<li>{{ employee.hire_date}}</li>
</ul>
{% endif %}
{% endblock content %}
other way to achieve it is with a built in get_object_or_404 method
individual view
from django.shortcuts import get_object_or_404
def employee(request, employee_id):
employee = get_object_or_404(Employee, employee_id=employee_id)
context = {'employee': employee}
return render(request, 'employee_records/employee.html', context)

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

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

Categories